1   /*
2    * Copyright 2002-2014 the original author or authors.
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 org.springframework.orm.jpa;
18  
19  import java.io.IOException;
20  import java.io.ObjectInputStream;
21  import java.io.Serializable;
22  import java.lang.reflect.InvocationHandler;
23  import java.lang.reflect.InvocationTargetException;
24  import java.lang.reflect.Method;
25  import java.lang.reflect.Proxy;
26  import java.util.HashSet;
27  import java.util.Map;
28  import java.util.Set;
29  import javax.persistence.EntityManager;
30  import javax.persistence.EntityManagerFactory;
31  import javax.persistence.Query;
32  import javax.persistence.TransactionRequiredException;
33  
34  import org.apache.commons.logging.Log;
35  import org.apache.commons.logging.LogFactory;
36  
37  import org.springframework.util.ClassUtils;
38  import org.springframework.util.CollectionUtils;
39  
40  /**
41   * Delegate for creating a shareable JPA {@link javax.persistence.EntityManager}
42   * reference for a given {@link javax.persistence.EntityManagerFactory}.
43   *
44   * <p>A shared EntityManager will behave just like an EntityManager fetched from
45   * an application server's JNDI environment, as defined by the JPA specification.
46   * It will delegate all calls to the current transactional EntityManager, if any;
47   * otherwise it will fall back to a newly created EntityManager per operation.
48   *
49   * <p>For a behavioral definition of such a shared transactional EntityManager,
50   * see {@link javax.persistence.PersistenceContextType#TRANSACTION} and its
51   * discussion in the JPA spec document. This is also the default being used
52   * for the annotation-based {@link javax.persistence.PersistenceContext#type()}.
53   *
54   * @author Juergen Hoeller
55   * @author Rod Johnson
56   * @author Oliver Gierke
57   * @since 2.0
58   * @see javax.persistence.PersistenceContext
59   * @see javax.persistence.PersistenceContextType#TRANSACTION
60   * @see org.springframework.orm.jpa.JpaTransactionManager
61   * @see ExtendedEntityManagerCreator
62   */
63  public abstract class SharedEntityManagerCreator {
64  
65  	private static final Class<?>[] NO_ENTITY_MANAGER_INTERFACES = new Class<?>[0];
66  
67  	private static final Set<String> transactionRequiringMethods = new HashSet<String>(6);
68  
69  	private static final Set<String> queryTerminationMethods = new HashSet<String>(3);
70  
71  	static {
72  		transactionRequiringMethods.add("joinTransaction");
73  		transactionRequiringMethods.add("flush");
74  		transactionRequiringMethods.add("persist");
75  		transactionRequiringMethods.add("merge");
76  		transactionRequiringMethods.add("remove");
77  		transactionRequiringMethods.add("refresh");
78  
79  		queryTerminationMethods.add("getResultList");
80  		queryTerminationMethods.add("getSingleResult");
81  		queryTerminationMethods.add("executeUpdate");
82  	}
83  
84  
85  	/**
86  	 * Create a transactional EntityManager proxy for the given EntityManagerFactory.
87  	 * @param emf the EntityManagerFactory to delegate to.
88  	 * @return a shareable transaction EntityManager proxy
89  	 */
90  	public static EntityManager createSharedEntityManager(EntityManagerFactory emf) {
91  		return createSharedEntityManager(emf, null, true);
92  	}
93  
94  	/**
95  	 * Create a transactional EntityManager proxy for the given EntityManagerFactory.
96  	 * @param emf the EntityManagerFactory to delegate to.
97  	 * @param properties the properties to be passed into the
98  	 * {@code createEntityManager} call (may be {@code null})
99  	 * @return a shareable transaction EntityManager proxy
100 	 */
101 	public static EntityManager createSharedEntityManager(EntityManagerFactory emf, Map<?, ?> properties) {
102 		return createSharedEntityManager(emf, properties, true);
103 	}
104 
105 	/**
106 	 * Create a transactional EntityManager proxy for the given EntityManagerFactory.
107 	 * @param emf the EntityManagerFactory to delegate to.
108 	 * @param properties the properties to be passed into the
109 	 * {@code createEntityManager} call (may be {@code null})
110 	 * @param synchronizedWithTransaction whether to automatically join ongoing
111 	 * transactions (according to the JPA 2.1 SynchronizationType rules)
112 	 * @return a shareable transaction EntityManager proxy
113 	 * @since 4.0
114 	 */
115 	public static EntityManager createSharedEntityManager(
116 			EntityManagerFactory emf, Map<?, ?> properties, boolean synchronizedWithTransaction) {
117 		Class<?> entityManagerInterface = (emf instanceof EntityManagerFactoryInfo ?
118 				((EntityManagerFactoryInfo) emf).getEntityManagerInterface() : EntityManager.class);
119 		return createSharedEntityManager(emf, properties, synchronizedWithTransaction,
120 				(entityManagerInterface == null ? NO_ENTITY_MANAGER_INTERFACES :
121 					new Class<?>[] { entityManagerInterface }));
122 	}
123 
124 	/**
125 	 * Create a transactional EntityManager proxy for the given EntityManagerFactory.
126 	 * @param emf EntityManagerFactory to obtain EntityManagers from as needed
127 	 * @param properties the properties to be passed into the
128 	 * {@code createEntityManager} call (may be {@code null})
129 	 * @param entityManagerInterfaces the interfaces to be implemented by the
130 	 * EntityManager. Allows the addition or specification of proprietary interfaces.
131 	 * @return a shareable transactional EntityManager proxy
132 	 */
133 	public static EntityManager createSharedEntityManager(
134 			EntityManagerFactory emf, Map<?, ?> properties, Class<?>... entityManagerInterfaces) {
135 
136 		return createSharedEntityManager(emf, properties, true, entityManagerInterfaces);
137 	}
138 
139 	/**
140 	 * Create a transactional EntityManager proxy for the given EntityManagerFactory.
141 	 * @param emf EntityManagerFactory to obtain EntityManagers from as needed
142 	 * @param properties the properties to be passed into the
143 	 * {@code createEntityManager} call (may be {@code null})
144 	 * @param synchronizedWithTransaction whether to automatically join ongoing
145 	 * transactions (according to the JPA 2.1 SynchronizationType rules)
146 	 * @param entityManagerInterfaces the interfaces to be implemented by the
147 	 * EntityManager. Allows the addition or specification of proprietary interfaces.
148 	 * @return a shareable transactional EntityManager proxy
149 	 * @since 4.0
150 	 */
151 	public static EntityManager createSharedEntityManager(EntityManagerFactory emf, Map<?, ?> properties,
152 			boolean synchronizedWithTransaction, Class<?>... entityManagerInterfaces) {
153 
154 		ClassLoader cl = null;
155 		if (emf instanceof EntityManagerFactoryInfo) {
156 			cl = ((EntityManagerFactoryInfo) emf).getBeanClassLoader();
157 		}
158 		Class<?>[] ifcs = new Class<?>[entityManagerInterfaces.length + 1];
159 		System.arraycopy(entityManagerInterfaces, 0, ifcs, 0, entityManagerInterfaces.length);
160 		ifcs[entityManagerInterfaces.length] = EntityManagerProxy.class;
161 		return (EntityManager) Proxy.newProxyInstance(
162 				(cl != null ? cl : SharedEntityManagerCreator.class.getClassLoader()),
163 				ifcs, new SharedEntityManagerInvocationHandler(emf, properties, synchronizedWithTransaction));
164 	}
165 
166 
167 	/**
168 	 * Invocation handler that delegates all calls to the current
169 	 * transactional EntityManager, if any; else, it will fall back
170 	 * to a newly created EntityManager per operation.
171 	 */
172 	@SuppressWarnings("serial")
173 	private static class SharedEntityManagerInvocationHandler implements InvocationHandler, Serializable {
174 
175 		private final Log logger = LogFactory.getLog(getClass());
176 
177 		private final EntityManagerFactory targetFactory;
178 
179 		private final Map<?, ?> properties;
180 
181 		private final boolean synchronizedWithTransaction;
182 
183 		private transient volatile ClassLoader proxyClassLoader;
184 
185 		public SharedEntityManagerInvocationHandler(
186 				EntityManagerFactory target, Map<?, ?> properties, boolean synchronizedWithTransaction) {
187 			this.targetFactory = target;
188 			this.properties = properties;
189 			this.synchronizedWithTransaction = synchronizedWithTransaction;
190 			initProxyClassLoader();
191 		}
192 
193 		private void initProxyClassLoader() {
194 			if (this.targetFactory instanceof EntityManagerFactoryInfo) {
195 				this.proxyClassLoader = ((EntityManagerFactoryInfo) this.targetFactory).getBeanClassLoader();
196 			}
197 			else {
198 				this.proxyClassLoader = this.targetFactory.getClass().getClassLoader();
199 			}
200 		}
201 
202 		@Override
203 		public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
204 			// Invocation on EntityManager interface coming in...
205 
206 			if (method.getName().equals("equals")) {
207 				// Only consider equal when proxies are identical.
208 				return (proxy == args[0]);
209 			}
210 			else if (method.getName().equals("hashCode")) {
211 				// Use hashCode of EntityManager proxy.
212 				return hashCode();
213 			}
214 			else if (method.getName().equals("toString")) {
215 				// Deliver toString without touching a target EntityManager.
216 				return "Shared EntityManager proxy for target factory [" + this.targetFactory + "]";
217 			}
218 			else if (method.getName().equals("getEntityManagerFactory")) {
219 				// JPA 2.0: return EntityManagerFactory without creating an EntityManager.
220 				return this.targetFactory;
221 			}
222 			else if (method.getName().equals("getCriteriaBuilder") || method.getName().equals("getMetamodel")) {
223 				// JPA 2.0: return EntityManagerFactory's CriteriaBuilder/Metamodel (avoid creation of EntityManager)
224 				try {
225 					return EntityManagerFactory.class.getMethod(method.getName()).invoke(this.targetFactory);
226 				}
227 				catch (InvocationTargetException ex) {
228 					throw ex.getTargetException();
229 				}
230 			}
231 			else if (method.getName().equals("unwrap")) {
232 				// JPA 2.0: handle unwrap method - could be a proxy match.
233 				Class<?> targetClass = (Class<?>) args[0];
234 				if (targetClass == null || targetClass.isInstance(proxy)) {
235 					return proxy;
236 				}
237 			}
238 			else if (method.getName().equals("isOpen")) {
239 				// Handle isOpen method: always return true.
240 				return true;
241 			}
242 			else if (method.getName().equals("close")) {
243 				// Handle close method: suppress, not valid.
244 				return null;
245 			}
246 			else if (method.getName().equals("getTransaction")) {
247 				throw new IllegalStateException(
248 						"Not allowed to create transaction on shared EntityManager - " +
249 						"use Spring transactions or EJB CMT instead");
250 			}
251 
252 			// Determine current EntityManager: either the transactional one
253 			// managed by the factory or a temporary one for the given invocation.
254 			EntityManager target = EntityManagerFactoryUtils.doGetTransactionalEntityManager(
255 					this.targetFactory, this.properties, this.synchronizedWithTransaction);
256 
257 			if (method.getName().equals("getTargetEntityManager")) {
258 				// Handle EntityManagerProxy interface.
259 				if (target == null) {
260 					throw new IllegalStateException("No transactional EntityManager available");
261 				}
262 				return target;
263 			}
264 			else if (method.getName().equals("unwrap")) {
265 				// We need a transactional target now.
266 				if (target == null) {
267 					throw new IllegalStateException("No transactional EntityManager available");
268 				}
269 				// Still perform unwrap call on target EntityManager.
270 			}
271 			else if (transactionRequiringMethods.contains(method.getName())) {
272 				// We need a transactional target now, according to the JPA spec.
273 				// Otherwise, the operation would get accepted but remain unflushed...
274 				if (target == null) {
275 					throw new TransactionRequiredException("No transactional EntityManager available");
276 				}
277 			}
278 
279 			// Regular EntityManager operations.
280 			boolean isNewEm = false;
281 			if (target == null) {
282 				logger.debug("Creating new EntityManager for shared EntityManager invocation");
283 				target = (!CollectionUtils.isEmpty(this.properties) ?
284 						this.targetFactory.createEntityManager(this.properties) :
285 						this.targetFactory.createEntityManager());
286 				isNewEm = true;
287 			}
288 
289 			// Invoke method on current EntityManager.
290 			try {
291 				Object result = method.invoke(target, args);
292 				if (result instanceof Query) {
293 					Query query = (Query) result;
294 					if (isNewEm) {
295 						Class<?>[] ifcs = ClassUtils.getAllInterfacesForClass(query.getClass(), this.proxyClassLoader);
296 						result = Proxy.newProxyInstance(this.proxyClassLoader, ifcs,
297 								new DeferredQueryInvocationHandler(query, target));
298 						isNewEm = false;
299 					}
300 					else {
301 						EntityManagerFactoryUtils.applyTransactionTimeout(query, this.targetFactory);
302 					}
303 				}
304 				return result;
305 			}
306 			catch (InvocationTargetException ex) {
307 				throw ex.getTargetException();
308 			}
309 			finally {
310 				if (isNewEm) {
311 					EntityManagerFactoryUtils.closeEntityManager(target);
312 				}
313 			}
314 		}
315 
316 		private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
317 			// Rely on default serialization, just initialize state after deserialization.
318 			ois.defaultReadObject();
319 			// Initialize transient fields.
320 			initProxyClassLoader();
321 		}
322 	}
323 
324 
325 	/**
326 	 * Invocation handler that handles deferred Query objects created by
327 	 * non-transactional createQuery invocations on a shared EntityManager.
328 	 */
329 	private static class DeferredQueryInvocationHandler implements InvocationHandler {
330 
331 		private final Query target;
332 
333 		private EntityManager em;
334 
335 		public DeferredQueryInvocationHandler(Query target, EntityManager em) {
336 			this.target = target;
337 			this.em = em;
338 		}
339 
340 		@Override
341 		public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
342 			// Invocation on Query interface coming in...
343 
344 			if (method.getName().equals("equals")) {
345 				// Only consider equal when proxies are identical.
346 				return (proxy == args[0]);
347 			}
348 			else if (method.getName().equals("hashCode")) {
349 				// Use hashCode of EntityManager proxy.
350 				return hashCode();
351 			}
352 			else if (method.getName().equals("unwrap")) {
353 				// Handle JPA 2.0 unwrap method - could be a proxy match.
354 				Class<?> targetClass = (Class<?>) args[0];
355 				if (targetClass == null || targetClass.isInstance(proxy)) {
356 					return proxy;
357 				}
358 			}
359 
360 			// Invoke method on actual Query object.
361 			try {
362 				Object retVal = method.invoke(this.target, args);
363 				return (retVal == this.target ? proxy : retVal);
364 			}
365 			catch (InvocationTargetException ex) {
366 				throw ex.getTargetException();
367 			}
368 			finally {
369 				if (queryTerminationMethods.contains(method.getName())) {
370 					// Actual execution of the query: close the EntityManager right
371 					// afterwards, since that was the only reason we kept it open.
372 					EntityManagerFactoryUtils.closeEntityManager(this.em);
373 					this.em = null;
374 				}
375 			}
376 		}
377 
378 		@Override
379 		protected void finalize() throws Throwable {
380 			// Trigger explicit EntityManager.close() call on garbage collection,
381 			// in particular for open/close statistics to be in sync. This is
382 			// only relevant if the Query object has not been executed, e.g.
383 			// when just used for the early validation of query definitions.
384 			EntityManagerFactoryUtils.closeEntityManager(this.em);
385 			super.finalize();
386 		}
387 	}
388 
389 }