001: /*
002: * Copyright 2002-2007 the original author or authors.
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016:
017: package org.springframework.orm.jdo;
018:
019: import java.lang.reflect.InvocationHandler;
020: import java.lang.reflect.InvocationTargetException;
021: import java.lang.reflect.Method;
022: import java.lang.reflect.Proxy;
023: import java.util.Collection;
024: import java.util.Map;
025:
026: import javax.jdo.JDOException;
027: import javax.jdo.PersistenceManager;
028: import javax.jdo.PersistenceManagerFactory;
029: import javax.jdo.Query;
030:
031: import org.springframework.dao.DataAccessException;
032: import org.springframework.dao.InvalidDataAccessApiUsageException;
033: import org.springframework.transaction.support.TransactionSynchronizationManager;
034: import org.springframework.util.Assert;
035: import org.springframework.util.ClassUtils;
036: import org.springframework.util.ReflectionUtils;
037:
038: /**
039: * Helper class that simplifies JDO data access code, and converts
040: * JDOExceptions into Spring DataAccessExceptions, following the
041: * <code>org.springframework.dao</code> exception hierarchy.
042: *
043: * <p>The central method is <code>execute</code>, supporting JDO access code
044: * implementing the {@link JdoCallback} interface. It provides JDO PersistenceManager
045: * handling such that neither the JdoCallback implementation nor the calling
046: * code needs to explicitly care about retrieving/closing PersistenceManagers,
047: * or handling JDO lifecycle exceptions.
048: *
049: * <p>Typically used to implement data access or business logic services that
050: * use JDO within their implementation but are JDO-agnostic in their interface.
051: * The latter or code calling the latter only have to deal with business
052: * objects, query objects, and <code>org.springframework.dao</code> exceptions.
053: *
054: * <p>Can be used within a service implementation via direct instantiation
055: * with a PersistenceManagerFactory reference, or get prepared in an
056: * application context and given to services as bean reference.
057: * Note: The PersistenceManagerFactory should always be configured as bean in
058: * the application context, in the first case given to the service directly,
059: * in the second case to the prepared template.
060: *
061: * <p>This class can be considered as direct alternative to working with the
062: * raw JDO PersistenceManager API (through
063: * <code>PersistenceManagerFactoryUtils.getPersistenceManager()</code>).
064: * The major advantage is its automatic conversion to DataAccessExceptions, the
065: * major disadvantage that no checked application exceptions can get thrown from
066: * within data access code. Corresponding checks and the actual throwing of such
067: * exceptions can often be deferred to after callback execution, though.
068: *
069: * <p>Note that even if {@link JdoTransactionManager} is used for transaction
070: * demarcation in higher-level services, all those services above the data
071: * access layer don't need to be JDO-aware. Setting such a special
072: * PlatformTransactionManager is a configuration issue, without introducing
073: * code dependencies: For example, switching to JTA is just a matter of
074: * Spring configuration (use JtaTransactionManager instead) and JDO provider
075: * configuration, neither affecting application code.
076: *
077: * <p>{@link LocalPersistenceManagerFactoryBean} is the preferred way of obtaining
078: * a reference to a specific PersistenceManagerFactory, at least in a non-EJB
079: * environment. The Spring application context will manage its lifecycle,
080: * initializing and shutting down the factory as part of the application.
081: *
082: * <p>Note that lazy loading will just work with an open JDO PersistenceManager,
083: * either within a Spring-driven transaction (with JdoTransactionManager or
084: * JtaTransactionManager) or within OpenPersistenceManagerInViewFilter/Interceptor.
085: * Furthermore, some operations just make sense within transactions,
086: * for example: <code>evict</code>, <code>evictAll</code>, <code>flush</code>.
087: *
088: * <p><b>NOTE:</b> This class is compatible with both JDO 1.0 and JDO 2.0,
089: * as far as possible. It uses reflection to adapt to the actual API present
090: * on the class path (concretely: for the <code>newObjectIdInstance</code>,
091: * <code>makePersistent</code> and <code>makePersistentAll</code> methods).
092: * Make sure that the JDO API jar on your class path matches the one that
093: * your JDO provider has been compiled against!
094: *
095: * @author Juergen Hoeller
096: * @since 03.06.2003
097: * @see #setPersistenceManagerFactory
098: * @see JdoCallback
099: * @see javax.jdo.PersistenceManager
100: * @see JdoInterceptor
101: * @see LocalPersistenceManagerFactoryBean
102: * @see org.springframework.jndi.JndiObjectFactoryBean
103: * @see JdoTransactionManager
104: * @see org.springframework.transaction.jta.JtaTransactionManager
105: * @see org.springframework.orm.jdo.support.OpenPersistenceManagerInViewFilter
106: * @see org.springframework.orm.jdo.support.OpenPersistenceManagerInViewInterceptor
107: */
108: public class JdoTemplate extends JdoAccessor implements JdoOperations {
109:
110: private static Method newObjectIdInstanceMethod;
111:
112: private static Method makePersistentMethod;
113:
114: private static Method makePersistentAllMethod;
115:
116: static {
117: // Determine whether the JDO 1.0 newObjectIdInstance(Class, String) method
118: // is available, for use in JdoTemplate's getObjectById implementation.
119: try {
120: newObjectIdInstanceMethod = PersistenceManager.class
121: .getMethod("newObjectIdInstance", new Class[] {
122: Class.class, String.class });
123: } catch (NoSuchMethodException ex) {
124: newObjectIdInstanceMethod = null;
125: }
126: // Fetch makePersistent(Object) method through reflection
127: // for use in JdoTemplate's makePersistent implementation.
128: // Return value is void in JDO 1.0 but Object in JDO 2.0.
129: try {
130: makePersistentMethod = PersistenceManager.class.getMethod(
131: "makePersistent", new Class[] { Object.class });
132: } catch (NoSuchMethodException ex) {
133: throw new IllegalStateException(
134: "JDO makePersistent(Object) method not available");
135: }
136: // Fetch makePersistent(Object) method through reflection
137: // for use in JdoTemplate's makePersistent implementation.
138: // Return value is void in JDO 1.0 but Collection in JDO 2.0.
139: try {
140: makePersistentAllMethod = PersistenceManager.class
141: .getMethod("makePersistentAll",
142: new Class[] { Collection.class });
143: } catch (NoSuchMethodException ex) {
144: throw new IllegalStateException(
145: "JDO makePersistentAll(Collection) method not available");
146: }
147: }
148:
149: private boolean allowCreate = true;
150:
151: private boolean exposeNativePersistenceManager = false;
152:
153: /**
154: * Create a new JdoTemplate instance.
155: */
156: public JdoTemplate() {
157: }
158:
159: /**
160: * Create a new JdoTemplate instance.
161: * @param pmf PersistenceManagerFactory to create PersistenceManagers
162: */
163: public JdoTemplate(PersistenceManagerFactory pmf) {
164: setPersistenceManagerFactory(pmf);
165: afterPropertiesSet();
166: }
167:
168: /**
169: * Create a new JdoTemplate instance.
170: * @param pmf PersistenceManagerFactory to create PersistenceManagers
171: * @param allowCreate if a non-transactional PersistenceManager should be created
172: * when no transactional PersistenceManager can be found for the current thread
173: */
174: public JdoTemplate(PersistenceManagerFactory pmf,
175: boolean allowCreate) {
176: setPersistenceManagerFactory(pmf);
177: setAllowCreate(allowCreate);
178: afterPropertiesSet();
179: }
180:
181: /**
182: * Set if a new PersistenceManager should be created when no transactional
183: * PersistenceManager can be found for the current thread.
184: * <p>JdoTemplate is aware of a corresponding PersistenceManager bound to the
185: * current thread, for example when using JdoTransactionManager.
186: * If allowCreate is true, a new non-transactional PersistenceManager will be
187: * created if none found, which needs to be closed at the end of the operation.
188: * If false, an IllegalStateException will get thrown in this case.
189: * @see PersistenceManagerFactoryUtils#getPersistenceManager(javax.jdo.PersistenceManagerFactory, boolean)
190: */
191: public void setAllowCreate(boolean allowCreate) {
192: this .allowCreate = allowCreate;
193: }
194:
195: /**
196: * Return if a new PersistenceManager should be created if no thread-bound found.
197: */
198: public boolean isAllowCreate() {
199: return allowCreate;
200: }
201:
202: /**
203: * Set whether to expose the native JDO PersistenceManager to JdoCallback
204: * code. Default is "false": a PersistenceManager proxy will be returned,
205: * suppressing <code>close</code> calls and automatically applying transaction
206: * timeouts (if any).
207: * <p>As there is often a need to cast to a provider-specific PersistenceManager
208: * class in DAOs that use the JDO 1.0 API, for JDO 2.0 previews and other
209: * provider-specific functionality, the exposed proxy implements all interfaces
210: * implemented by the original PersistenceManager. If this is not sufficient,
211: * turn this flag to "true".
212: * @see JdoCallback
213: * @see javax.jdo.PersistenceManager
214: * @see #prepareQuery
215: */
216: public void setExposeNativePersistenceManager(
217: boolean exposeNativePersistenceManager) {
218: this .exposeNativePersistenceManager = exposeNativePersistenceManager;
219: }
220:
221: /**
222: * Return whether to expose the native JDO PersistenceManager to JdoCallback
223: * code, or rather a PersistenceManager proxy.
224: */
225: public boolean isExposeNativePersistenceManager() {
226: return exposeNativePersistenceManager;
227: }
228:
229: public Object execute(JdoCallback action)
230: throws DataAccessException {
231: return execute(action, isExposeNativePersistenceManager());
232: }
233:
234: public Collection executeFind(JdoCallback action)
235: throws DataAccessException {
236: Object result = execute(action,
237: isExposeNativePersistenceManager());
238: if (result != null && !(result instanceof Collection)) {
239: throw new InvalidDataAccessApiUsageException(
240: "Result object returned from JdoCallback isn't a Collection: ["
241: + result + "]");
242: }
243: return (Collection) result;
244: }
245:
246: /**
247: * Execute the action specified by the given action object within a
248: * PersistenceManager.
249: * @param action callback object that specifies the JDO action
250: * @param exposeNativePersistenceManager whether to expose the native
251: * JDO persistence manager to callback code
252: * @return a result object returned by the action, or <code>null</code>
253: * @throws org.springframework.dao.DataAccessException in case of JDO errors
254: */
255: public Object execute(JdoCallback action,
256: boolean exposeNativePersistenceManager)
257: throws DataAccessException {
258: Assert.notNull(action, "Callback object must not be null");
259:
260: PersistenceManager pm = PersistenceManagerFactoryUtils
261: .getPersistenceManager(getPersistenceManagerFactory(),
262: isAllowCreate());
263: boolean existingTransaction = TransactionSynchronizationManager
264: .hasResource(getPersistenceManagerFactory());
265: try {
266: PersistenceManager pmToExpose = (exposeNativePersistenceManager ? pm
267: : createPersistenceManagerProxy(pm));
268: Object result = action.doInJdo(pmToExpose);
269: flushIfNecessary(pm, existingTransaction);
270: return postProcessResult(result, pm, existingTransaction);
271: } catch (JDOException ex) {
272: throw convertJdoAccessException(ex);
273: } catch (RuntimeException ex) {
274: // callback code threw application exception
275: throw ex;
276: } finally {
277: PersistenceManagerFactoryUtils.releasePersistenceManager(
278: pm, getPersistenceManagerFactory());
279: }
280: }
281:
282: /**
283: * Create a close-suppressing proxy for the given JDO PersistenceManager.
284: * Called by the <code>execute</code> method.
285: * <p>The proxy also prepares returned JDO Query objects.
286: * @param pm the JDO PersistenceManager to create a proxy for
287: * @return the PersistenceManager proxy, implementing all interfaces
288: * implemented by the passed-in PersistenceManager object (that is,
289: * also implementing all provider-specific extension interfaces)
290: * @see javax.jdo.PersistenceManager#close()
291: * @see #execute(JdoCallback, boolean)
292: * @see #prepareQuery
293: */
294: protected PersistenceManager createPersistenceManagerProxy(
295: PersistenceManager pm) {
296: Class[] ifcs = ClassUtils.getAllInterfaces(pm);
297: return (PersistenceManager) Proxy.newProxyInstance(getClass()
298: .getClassLoader(), ifcs,
299: new CloseSuppressingInvocationHandler(pm));
300: }
301:
302: /**
303: * Post-process the given result object, which might be a Collection.
304: * Called by the <code>execute</code> method.
305: * <p>Default implementation always returns the passed-in Object as-is.
306: * Subclasses might override this to automatically detach result
307: * collections or even single result objects.
308: * @param pm the current JDO PersistenceManager
309: * @param result the result object (might be a Collection)
310: * @param existingTransaction if executing within an existing transaction
311: * (within an existing JDO PersistenceManager that won't be closed immediately)
312: * @return the post-processed result object (can be simply be the passed-in object)
313: * @see #execute(JdoCallback, boolean)
314: */
315: protected Object postProcessResult(Object result,
316: PersistenceManager pm, boolean existingTransaction) {
317: return result;
318: }
319:
320: //-------------------------------------------------------------------------
321: // Convenience methods for load, save, delete
322: //-------------------------------------------------------------------------
323:
324: public Object getObjectById(final Object objectId)
325: throws DataAccessException {
326: return execute(new JdoCallback() {
327: public Object doInJdo(PersistenceManager pm)
328: throws JDOException {
329: return pm.getObjectById(objectId, true);
330: }
331: }, true);
332: }
333:
334: public Object getObjectById(final Class entityClass,
335: final Object idValue) throws DataAccessException {
336: return execute(new JdoCallback() {
337: public Object doInJdo(PersistenceManager pm)
338: throws JDOException {
339: // Use JDO 1.0 newObjectIdInstance(Class, String) method, if available.
340: if (newObjectIdInstanceMethod != null) {
341: Object id = ReflectionUtils.invokeMethod(
342: newObjectIdInstanceMethod, pm,
343: new Object[] { entityClass,
344: idValue.toString() });
345: return pm.getObjectById(id, true);
346: }
347: // Use JDO 2.0 getObjectById(Class, Object) method.
348: return pm.getObjectById(entityClass, idValue);
349: }
350: }, true);
351: }
352:
353: public void evict(final Object entity) throws DataAccessException {
354: execute(new JdoCallback() {
355: public Object doInJdo(PersistenceManager pm)
356: throws JDOException {
357: pm.evict(entity);
358: return null;
359: }
360: }, true);
361: }
362:
363: public void evictAll(final Collection entities)
364: throws DataAccessException {
365: execute(new JdoCallback() {
366: public Object doInJdo(PersistenceManager pm)
367: throws JDOException {
368: pm.evictAll(entities);
369: return null;
370: }
371: }, true);
372: }
373:
374: public void evictAll() throws DataAccessException {
375: execute(new JdoCallback() {
376: public Object doInJdo(PersistenceManager pm)
377: throws JDOException {
378: pm.evictAll();
379: return null;
380: }
381: }, true);
382: }
383:
384: public void refresh(final Object entity) throws DataAccessException {
385: execute(new JdoCallback() {
386: public Object doInJdo(PersistenceManager pm)
387: throws JDOException {
388: pm.refresh(entity);
389: return null;
390: }
391: }, true);
392: }
393:
394: public void refreshAll(final Collection entities)
395: throws DataAccessException {
396: execute(new JdoCallback() {
397: public Object doInJdo(PersistenceManager pm)
398: throws JDOException {
399: pm.refreshAll(entities);
400: return null;
401: }
402: }, true);
403: }
404:
405: public void refreshAll() throws DataAccessException {
406: execute(new JdoCallback() {
407: public Object doInJdo(PersistenceManager pm)
408: throws JDOException {
409: pm.refreshAll();
410: return null;
411: }
412: }, true);
413: }
414:
415: public void makePersistent(final Object entity)
416: throws DataAccessException {
417: execute(new JdoCallback() {
418: public Object doInJdo(PersistenceManager pm)
419: throws JDOException {
420: return ReflectionUtils.invokeMethod(
421: makePersistentMethod, pm,
422: new Object[] { entity });
423: }
424: }, true);
425: }
426:
427: public void makePersistentAll(final Collection entities)
428: throws DataAccessException {
429: execute(new JdoCallback() {
430: public Object doInJdo(PersistenceManager pm)
431: throws JDOException {
432: return ReflectionUtils.invokeMethod(
433: makePersistentAllMethod, pm,
434: new Object[] { entities });
435: }
436: }, true);
437: }
438:
439: public void deletePersistent(final Object entity)
440: throws DataAccessException {
441: execute(new JdoCallback() {
442: public Object doInJdo(PersistenceManager pm)
443: throws JDOException {
444: pm.deletePersistent(entity);
445: return null;
446: }
447: }, true);
448: }
449:
450: public void deletePersistentAll(final Collection entities)
451: throws DataAccessException {
452: execute(new JdoCallback() {
453: public Object doInJdo(PersistenceManager pm)
454: throws JDOException {
455: pm.deletePersistentAll(entities);
456: return null;
457: }
458: }, true);
459: }
460:
461: public Object detachCopy(final Object entity) {
462: return execute(new JdoCallback() {
463: public Object doInJdo(PersistenceManager pm)
464: throws JDOException {
465: return getJdoDialect().detachCopy(pm, entity);
466: }
467: }, true);
468: }
469:
470: public Collection detachCopyAll(final Collection entities) {
471: return (Collection) execute(new JdoCallback() {
472: public Object doInJdo(PersistenceManager pm)
473: throws JDOException {
474: return getJdoDialect().detachCopyAll(pm, entities);
475: }
476: }, true);
477: }
478:
479: public Object attachCopy(final Object detachedEntity) {
480: return execute(new JdoCallback() {
481: public Object doInJdo(PersistenceManager pm)
482: throws JDOException {
483: return getJdoDialect().attachCopy(pm, detachedEntity);
484: }
485: }, true);
486: }
487:
488: public Collection attachCopyAll(final Collection detachedEntities) {
489: return (Collection) execute(new JdoCallback() {
490: public Object doInJdo(PersistenceManager pm)
491: throws JDOException {
492: return getJdoDialect().attachCopyAll(pm,
493: detachedEntities);
494: }
495: }, true);
496: }
497:
498: public void flush() throws DataAccessException {
499: execute(new JdoCallback() {
500: public Object doInJdo(PersistenceManager pm)
501: throws JDOException {
502: getJdoDialect().flush(pm);
503: return null;
504: }
505: }, true);
506: }
507:
508: //-------------------------------------------------------------------------
509: // Convenience finder methods
510: //-------------------------------------------------------------------------
511:
512: public Collection find(Class entityClass)
513: throws DataAccessException {
514: return find(entityClass, null, null);
515: }
516:
517: public Collection find(Class entityClass, String filter)
518: throws DataAccessException {
519: return find(entityClass, filter, null);
520: }
521:
522: public Collection find(final Class entityClass,
523: final String filter, final String ordering)
524: throws DataAccessException {
525:
526: return (Collection) execute(new JdoCallback() {
527: public Object doInJdo(PersistenceManager pm)
528: throws JDOException {
529: Query query = (filter != null ? pm.newQuery(
530: entityClass, filter) : pm.newQuery(entityClass));
531: prepareQuery(query);
532: if (ordering != null) {
533: query.setOrdering(ordering);
534: }
535: return query.execute();
536: }
537: }, true);
538: }
539:
540: public Collection find(Class entityClass, String filter,
541: String parameters, Object[] values)
542: throws DataAccessException {
543:
544: return find(entityClass, filter, parameters, values, null);
545: }
546:
547: public Collection find(final Class entityClass,
548: final String filter, final String parameters,
549: final Object[] values, final String ordering)
550: throws DataAccessException {
551:
552: return (Collection) execute(new JdoCallback() {
553: public Object doInJdo(PersistenceManager pm)
554: throws JDOException {
555: Query query = pm.newQuery(entityClass, filter);
556: prepareQuery(query);
557: query.declareParameters(parameters);
558: if (ordering != null) {
559: query.setOrdering(ordering);
560: }
561: return query.executeWithArray(values);
562: }
563: }, true);
564: }
565:
566: public Collection find(Class entityClass, String filter,
567: String parameters, Map values) throws DataAccessException {
568:
569: return find(entityClass, filter, parameters, values, null);
570: }
571:
572: public Collection find(final Class entityClass,
573: final String filter, final String parameters,
574: final Map values, final String ordering)
575: throws DataAccessException {
576:
577: return (Collection) execute(new JdoCallback() {
578: public Object doInJdo(PersistenceManager pm)
579: throws JDOException {
580: Query query = pm.newQuery(entityClass, filter);
581: prepareQuery(query);
582: query.declareParameters(parameters);
583: if (ordering != null) {
584: query.setOrdering(ordering);
585: }
586: return query.executeWithMap(values);
587: }
588: }, true);
589: }
590:
591: public Collection find(final String language,
592: final Object queryObject) throws DataAccessException {
593: return (Collection) execute(new JdoCallback() {
594: public Object doInJdo(PersistenceManager pm)
595: throws JDOException {
596: Query query = pm.newQuery(language, queryObject);
597: prepareQuery(query);
598: return query.execute();
599: }
600: }, true);
601: }
602:
603: public Collection find(final String queryString)
604: throws DataAccessException {
605: return (Collection) execute(new JdoCallback() {
606: public Object doInJdo(PersistenceManager pm)
607: throws JDOException {
608: Query query = pm.newQuery(queryString);
609: prepareQuery(query);
610: return query.execute();
611: }
612: }, true);
613: }
614:
615: public Collection find(final String queryString,
616: final Object[] values) throws DataAccessException {
617: return (Collection) execute(new JdoCallback() {
618: public Object doInJdo(PersistenceManager pm)
619: throws JDOException {
620: Query query = pm.newQuery(queryString);
621: prepareQuery(query);
622: return query.executeWithArray(values);
623: }
624: }, true);
625: }
626:
627: public Collection find(final String queryString, final Map values)
628: throws DataAccessException {
629: return (Collection) execute(new JdoCallback() {
630: public Object doInJdo(PersistenceManager pm)
631: throws JDOException {
632: Query query = pm.newQuery(queryString);
633: prepareQuery(query);
634: return query.executeWithMap(values);
635: }
636: }, true);
637: }
638:
639: public Collection findByNamedQuery(final Class entityClass,
640: final String queryName) throws DataAccessException {
641: return (Collection) execute(new JdoCallback() {
642: public Object doInJdo(PersistenceManager pm)
643: throws JDOException {
644: Query query = getJdoDialect().newNamedQuery(pm,
645: entityClass, queryName);
646: prepareQuery(query);
647: return query.execute();
648: }
649: }, true);
650: }
651:
652: public Collection findByNamedQuery(final Class entityClass,
653: final String queryName, final Object[] values)
654: throws DataAccessException {
655:
656: return (Collection) execute(new JdoCallback() {
657: public Object doInJdo(PersistenceManager pm)
658: throws JDOException {
659: Query query = getJdoDialect().newNamedQuery(pm,
660: entityClass, queryName);
661: prepareQuery(query);
662: return query.executeWithArray(values);
663: }
664: }, true);
665: }
666:
667: public Collection findByNamedQuery(final Class entityClass,
668: final String queryName, final Map values)
669: throws DataAccessException {
670:
671: return (Collection) execute(new JdoCallback() {
672: public Object doInJdo(PersistenceManager pm)
673: throws JDOException {
674: Query query = getJdoDialect().newNamedQuery(pm,
675: entityClass, queryName);
676: prepareQuery(query);
677: return query.executeWithMap(values);
678: }
679: }, true);
680: }
681:
682: /**
683: * Prepare the given JDO query object. To be used within a JdoCallback.
684: * Applies a transaction timeout, if any. If you don't use such timeouts,
685: * the call is a no-op.
686: * <p>In general, prefer a proxied PersistenceManager instead, which will
687: * automatically apply the transaction timeout (through the use of a special
688: * PersistenceManager proxy). You need to set the "exposeNativePersistenceManager"
689: * property to "false" to activate this. Note that you won't be able to cast
690: * to a provider-specific JDO PersistenceManager class anymore then.
691: * @param query the JDO query object
692: * @throws JDOException if the query could not be properly prepared
693: * @see JdoCallback#doInJdo
694: * @see PersistenceManagerFactoryUtils#applyTransactionTimeout
695: * @see #setExposeNativePersistenceManager
696: */
697: public void prepareQuery(Query query) throws JDOException {
698: PersistenceManagerFactoryUtils.applyTransactionTimeout(query,
699: getPersistenceManagerFactory(), getJdoDialect());
700: }
701:
702: /**
703: * Invocation handler that suppresses close calls on JDO PersistenceManagers.
704: * Also prepares returned Query objects.
705: * @see javax.jdo.PersistenceManager#close()
706: */
707: private class CloseSuppressingInvocationHandler implements
708: InvocationHandler {
709:
710: private final PersistenceManager target;
711:
712: public CloseSuppressingInvocationHandler(
713: PersistenceManager target) {
714: this .target = target;
715: }
716:
717: public Object invoke(Object proxy, Method method, Object[] args)
718: throws Throwable {
719: // Invocation on PersistenceManager interface (or provider-specific extension) coming in...
720:
721: if (method.getName().equals("equals")) {
722: // Only consider equal when proxies are identical.
723: return (proxy == args[0] ? Boolean.TRUE : Boolean.FALSE);
724: } else if (method.getName().equals("hashCode")) {
725: // Use hashCode of PersistenceManager proxy.
726: return new Integer(hashCode());
727: } else if (method.getName().equals("close")) {
728: // Handle close method: suppress, not valid.
729: return null;
730: }
731:
732: // Invoke method on target PersistenceManager.
733: try {
734: Object retVal = method.invoke(this .target, args);
735:
736: // If return value is a JDO Query object, apply transaction timeout.
737: if (retVal instanceof Query) {
738: prepareQuery(((Query) retVal));
739: }
740:
741: return retVal;
742: } catch (InvocationTargetException ex) {
743: throw ex.getTargetException();
744: }
745: }
746: }
747:
748: }
|