1/**
2 * Copyright (C) 2010 Google, Inc.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.google.inject.persist.jpa;
18
19import com.google.common.annotations.VisibleForTesting;
20import com.google.common.base.Preconditions;
21import com.google.inject.Inject;
22import com.google.inject.Provider;
23import com.google.inject.Singleton;
24import com.google.inject.persist.PersistService;
25import com.google.inject.persist.UnitOfWork;
26
27import java.lang.annotation.Documented;
28import java.lang.annotation.ElementType;
29import java.lang.annotation.Retention;
30import java.lang.annotation.RetentionPolicy;
31import java.lang.annotation.Target;
32import java.util.Map;
33
34import javax.persistence.EntityManager;
35import javax.persistence.EntityManagerFactory;
36import javax.persistence.Persistence;
37
38/**
39 * @author Dhanji R. Prasanna (dhanji@gmail.com)
40 */
41@Singleton
42class JpaPersistService implements Provider<EntityManager>, UnitOfWork, PersistService {
43  private final ThreadLocal<EntityManager> entityManager = new ThreadLocal<EntityManager>();
44
45  private final String persistenceUnitName;
46  private final Map<?,?> persistenceProperties;
47
48  @Inject
49  public JpaPersistService(@Jpa String persistenceUnitName,
50      @Nullable @Jpa Map<?,?> persistenceProperties) {
51    this.persistenceUnitName = persistenceUnitName;
52    this.persistenceProperties = persistenceProperties;
53  }
54
55  public EntityManager get() {
56    if (!isWorking()) {
57      begin();
58    }
59
60    EntityManager em = entityManager.get();
61    Preconditions.checkState(null != em, "Requested EntityManager outside work unit. "
62        + "Try calling UnitOfWork.begin() first, or use a PersistFilter if you "
63        + "are inside a servlet environment.");
64
65    return em;
66  }
67
68  public boolean isWorking() {
69    return entityManager.get() != null;
70  }
71
72  public void begin() {
73    Preconditions.checkState(null == entityManager.get(),
74        "Work already begun on this thread. Looks like you have called UnitOfWork.begin() twice"
75         + " without a balancing call to end() in between.");
76
77    entityManager.set(emFactory.createEntityManager());
78  }
79
80  public void end() {
81    EntityManager em = entityManager.get();
82
83    // Let's not penalize users for calling end() multiple times.
84    if (null == em) {
85      return;
86    }
87
88    try {
89      em.close();
90    }
91    finally {
92      entityManager.remove();
93    }
94  }
95
96  private volatile EntityManagerFactory emFactory;
97
98  @VisibleForTesting
99  synchronized void start(EntityManagerFactory emFactory) {
100    this.emFactory = emFactory;
101  }
102
103  public synchronized void start() {
104    Preconditions.checkState(null == emFactory, "Persistence service was already initialized.");
105
106    if (null != persistenceProperties) {
107      this.emFactory = Persistence
108          .createEntityManagerFactory(persistenceUnitName, persistenceProperties);
109    } else {
110      this.emFactory = Persistence.createEntityManagerFactory(persistenceUnitName);
111    }
112  }
113
114  public synchronized void stop() {
115    Preconditions.checkState(emFactory.isOpen(), "Persistence service was already shut down.");
116    emFactory.close();
117  }
118
119  @Singleton
120  public static class EntityManagerFactoryProvider implements Provider<EntityManagerFactory> {
121    private final JpaPersistService emProvider;
122
123    @Inject
124    public EntityManagerFactoryProvider(JpaPersistService emProvider) {
125      this.emProvider = emProvider;
126    }
127
128    public EntityManagerFactory get() {
129      assert null != emProvider.emFactory;
130      return emProvider.emFactory;
131    }
132  }
133
134  @Documented
135  @Retention(RetentionPolicy.RUNTIME)
136  @Target(ElementType.PARAMETER)
137  private @interface Nullable { }
138
139}
140