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 17 package com.google.inject.persist.jpa; 18 19 import com.google.common.annotations.VisibleForTesting; 20 import com.google.common.base.Preconditions; 21 import com.google.inject.Inject; 22 import com.google.inject.Provider; 23 import com.google.inject.Singleton; 24 import com.google.inject.persist.PersistService; 25 import com.google.inject.persist.UnitOfWork; 26 import java.lang.annotation.Documented; 27 import java.lang.annotation.ElementType; 28 import java.lang.annotation.Retention; 29 import java.lang.annotation.RetentionPolicy; 30 import java.lang.annotation.Target; 31 import java.util.Map; 32 import javax.persistence.EntityManager; 33 import javax.persistence.EntityManagerFactory; 34 import javax.persistence.Persistence; 35 36 /** @author Dhanji R. Prasanna (dhanji@gmail.com) */ 37 @Singleton 38 class JpaPersistService implements Provider<EntityManager>, UnitOfWork, PersistService { 39 private final ThreadLocal<EntityManager> entityManager = new ThreadLocal<>(); 40 41 private final String persistenceUnitName; 42 private final Map<?, ?> persistenceProperties; 43 44 @Inject JpaPersistService( @pa String persistenceUnitName, @Nullable @Jpa Map<?, ?> persistenceProperties)45 public JpaPersistService( 46 @Jpa String persistenceUnitName, @Nullable @Jpa Map<?, ?> persistenceProperties) { 47 this.persistenceUnitName = persistenceUnitName; 48 this.persistenceProperties = persistenceProperties; 49 } 50 51 @Override get()52 public EntityManager get() { 53 if (!isWorking()) { 54 begin(); 55 } 56 57 EntityManager em = entityManager.get(); 58 Preconditions.checkState( 59 null != em, 60 "Requested EntityManager outside work unit. " 61 + "Try calling UnitOfWork.begin() first, or use a PersistFilter if you " 62 + "are inside a servlet environment."); 63 64 return em; 65 } 66 isWorking()67 public boolean isWorking() { 68 return entityManager.get() != null; 69 } 70 71 @Override begin()72 public void begin() { 73 Preconditions.checkState( 74 null == entityManager.get(), 75 "Work already begun on this thread. Looks like you have called UnitOfWork.begin() twice" 76 + " without a balancing call to end() in between."); 77 78 entityManager.set(emFactory.createEntityManager()); 79 } 80 81 @Override end()82 public void end() { 83 EntityManager em = entityManager.get(); 84 85 // Let's not penalize users for calling end() multiple times. 86 if (null == em) { 87 return; 88 } 89 90 try { 91 em.close(); 92 } finally { 93 entityManager.remove(); 94 } 95 } 96 97 private volatile EntityManagerFactory emFactory; 98 99 @VisibleForTesting start(EntityManagerFactory emFactory)100 synchronized void start(EntityManagerFactory emFactory) { 101 this.emFactory = emFactory; 102 } 103 104 @Override start()105 public synchronized void start() { 106 Preconditions.checkState(null == emFactory, "Persistence service was already initialized."); 107 108 if (null != persistenceProperties) { 109 this.emFactory = 110 Persistence.createEntityManagerFactory(persistenceUnitName, persistenceProperties); 111 } else { 112 this.emFactory = Persistence.createEntityManagerFactory(persistenceUnitName); 113 } 114 } 115 116 @Override stop()117 public synchronized void stop() { 118 Preconditions.checkState(emFactory.isOpen(), "Persistence service was already shut down."); 119 emFactory.close(); 120 } 121 122 @Singleton 123 public static class EntityManagerFactoryProvider implements Provider<EntityManagerFactory> { 124 private final JpaPersistService emProvider; 125 126 @Inject EntityManagerFactoryProvider(JpaPersistService emProvider)127 public EntityManagerFactoryProvider(JpaPersistService emProvider) { 128 this.emProvider = emProvider; 129 } 130 131 @Override get()132 public EntityManagerFactory get() { 133 assert null != emProvider.emFactory; 134 return emProvider.emFactory; 135 } 136 } 137 138 @Documented 139 @Retention(RetentionPolicy.RUNTIME) 140 @Target(ElementType.PARAMETER) 141 private @interface Nullable {} 142 } 143