001: /*
002: * Copyright (c) 2001 - 2005 ivata limited.
003: * All rights reserved.
004: * -----------------------------------------------------------------------------
005: * ivata groupware may be redistributed under the GNU General Public
006: * License as published by the Free Software Foundation;
007: * version 2 of the License.
008: *
009: * These programs are free software; you can redistribute them and/or
010: * modify them under the terms of the GNU General Public License
011: * as published by the Free Software Foundation; version 2 of the License.
012: *
013: * These programs are distributed in the hope that they will be useful,
014: * but WITHOUT ANY WARRANTY; without even the implied warranty of
015: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
016: *
017: * See the GNU General Public License in the file LICENSE.txt for more
018: * details.
019: *
020: * If you would like a copy of the GNU General Public License write to
021: *
022: * Free Software Foundation, Inc.
023: * 59 Temple Place - Suite 330
024: * Boston, MA 02111-1307, USA.
025: *
026: *
027: * To arrange commercial support and licensing, contact ivata at
028: * http://www.ivata.com/contact.jsp
029: * -----------------------------------------------------------------------------
030: * $Log: HibernateInterceptor.java,v $
031: * Revision 1.11 2005/10/12 18:36:46 colinmacleod
032: * Standardized format of Logger declaration - to make it easier to find instances
033: * which are not both static and final.
034: *
035: * Revision 1.10 2005/10/11 18:51:38 colinmacleod
036: * Fixed some checkstyle and javadoc issues.
037: *
038: * Revision 1.9 2005/10/03 10:21:14 colinmacleod
039: * Fixed some style and javadoc issues.
040: *
041: * Revision 1.8 2005/10/02 14:08:56 colinmacleod
042: * Added/improved log4j logging.
043: *
044: * Revision 1.7 2005/09/29 13:51:38 colinmacleod
045: * Moved PersistenceListener interfaces to ivata masks.
046: *
047: * Revision 1.6 2005/09/15 10:23:24 colinmacleod
048: * Upgraded Maven to 1.1 (beta-2).
049: * Upgraded Hibernate to 3.0.5.
050: *
051: * Revision 1.5 2005/09/14 15:20:29 colinmacleod
052: * Removed unused local and class variables.
053: * Added serialVersionUID.
054: *
055: * Revision 1.4 2005/05/01 08:44:39 colinmacleod
056: * Fix for ID type reverted to Integer.
057: *
058: * Revision 1.3 2005/04/10 20:09:42 colinmacleod
059: * Added new themes.
060: * Changed id type to String.
061: * Changed i tag to em and b tag to strong.
062: * Improved PicoContainerFactory with NanoContainer scripts.
063: *
064: * Revision 1.2 2005/04/09 17:19:37 colinmacleod
065: * Changed copyright text to GPL v2 explicitly.
066: *
067: * Revision 1.1 2005/03/10 19:23:04 colinmacleod
068: * Moved to ivata groupware.
069: *
070: * Revision 1.1 2004/07/13 19:42:44 colinmacleod
071: * Moved project to POJOs from EJBs.
072: * Applied PicoContainer to services layer (replacing session EJBs).
073: * Applied Hibernate to persistence layer (replacing entity EJBs).
074: * -----------------------------------------------------------------------------
075: */
076: package com.ivata.groupware.container.persistence.hibernate;
077:
078: import java.io.Serializable;
079: import java.lang.reflect.InvocationTargetException;
080: import java.util.HashMap;
081: import java.util.Iterator;
082: import java.util.List;
083: import java.util.Map;
084: import java.util.Set;
085: import java.util.Vector;
086:
087: import org.apache.commons.beanutils.PropertyUtils;
088: import org.apache.log4j.Logger;
089: import org.hibernate.CallbackException;
090: import org.hibernate.EntityMode;
091: import org.hibernate.HibernateException;
092: import org.hibernate.Interceptor;
093: import org.hibernate.SessionFactory;
094: import org.hibernate.Transaction;
095: import org.hibernate.type.Type;
096: import org.picocontainer.MutablePicoContainer;
097: import org.picocontainer.PicoContainer;
098: import org.picocontainer.defaults.DefaultPicoContainer;
099:
100: import com.ivata.groupware.container.PicoContainerFactory;
101: import com.ivata.mask.persistence.PersistenceException;
102: import com.ivata.mask.persistence.listener.AddPersistenceListener;
103: import com.ivata.mask.persistence.listener.AmendPersistenceListener;
104: import com.ivata.mask.persistence.listener.RemovePersistenceListener;
105: import com.ivata.mask.util.SystemException;
106: import com.ivata.mask.valueobject.ValueObject;
107:
108: /**
109: * This class is responsible for handling the <em>ivata grouwpare</em>
110: * persistence manager listeners. These let other classes listen in on value
111: * objects as they are added, amended or removed.
112: *
113: * @author Colin MacLeod
114: * <a href='mailto:colin.macleod@ivata.com'>colin.macleod@ivata.com</a>
115: * @since ivata groupware 0.9 (May 31, 2004)
116: * @version $Revision: 1.11 $
117: */
118: public class HibernateInterceptor implements Interceptor, Serializable {
119: /**
120: * Stores all the instances of {@link AddPersistenceListener}.
121: */
122: private static Map addListeners = new HashMap();
123: /**
124: * Stores all the instances of {@link AmendPersistenceListener}.
125: */
126: private static Map amendListeners = new HashMap();
127: /**
128: * Logger for this class.
129: */
130: private static final Logger logger = Logger
131: .getLogger(HibernateInterceptor.class);
132: /**
133: * Stores all the instances of {@link RemovePersistenceListener}.
134: */
135: private static Map removeListeners = new HashMap();
136: /**
137: * Serialization version (for <code>Serializable</code> interface).
138: */
139: private static final long serialVersionUID = 1L;
140:
141: /**
142: * <copyDoc>Refer to
143: * {@link com.ivata.mask.persistence.QueryPersistenceManager#addAddListener
144: * QueryPersistenceManager.addAddListener}.</copyDoc>
145: *
146: * @param dOClass
147: * <copyDoc>Refer to
148: * {@link com.ivata.mask.persistence.QueryPersistenceManager#addAddListener
149: * QueryPersistenceManager.addAddListener}.</copyDoc>
150: * @param listener
151: * <copyDoc>Refer to
152: * {@link com.ivata.mask.persistence.QueryPersistenceManager#addAddListener
153: * QueryPersistenceManager.addAddListener}.</copyDoc>
154: */
155: public static synchronized void addAddListener(final Class dOClass,
156: final AddPersistenceListener listener) {
157: if (logger.isDebugEnabled()) {
158: logger.debug("addAddListener: " + listener);
159: }
160: List addListenersThisClass = (List) addListeners.get(dOClass
161: .getName());
162: if (addListenersThisClass == null) {
163: addListenersThisClass = new Vector();
164: addListeners.put(dOClass.getName(), addListenersThisClass);
165: }
166: addListenersThisClass.add(listener);
167:
168: if (logger.isDebugEnabled()) {
169: logger
170: .debug("addAddListener(Class, AddPersistenceListener) - end");
171: }
172: }
173:
174: /**
175: * <copyDoc>Refer to {@link
176: * com.ivata.mask.persistence.QueryPersistenceManager#addAmendListener
177: * QueryPersistenceManager.addAmendListener}.</copyDoc>
178: *
179: * @param dOClass
180: * <copyDoc>Refer to {@link
181: * com.ivata.mask.persistence.QueryPersistenceManager#addAmendListener
182: * QueryPersistenceManager.addAmendListener}.</copyDoc>
183: * @param listener
184: * <copyDoc>Refer to {@link
185: * com.ivata.mask.persistence.QueryPersistenceManager#addAmendListener
186: * QueryPersistenceManager.addAmendListener}.</copyDoc>
187: */
188: public static synchronized void addAmendListener(
189: final Class dOClass, final AmendPersistenceListener listener) {
190: if (logger.isDebugEnabled()) {
191: logger.debug("addAmendListener: " + listener);
192: }
193: List amendListenersThisClass = (List) amendListeners
194: .get(dOClass.getName());
195: if (amendListenersThisClass == null) {
196: amendListenersThisClass = new Vector();
197: amendListeners.put(dOClass.getName(),
198: amendListenersThisClass);
199: }
200: amendListenersThisClass.add(listener);
201:
202: if (logger.isDebugEnabled()) {
203: logger.debug("addAmendListener - end");
204: }
205: }
206:
207: /**
208: * <copyDoc>Refer to {@link
209: * com.ivata.mask.persistence.QueryPersistenceManager#addRemoveListener
210: * QueryPersistenceManager.addRemoveListener}.</copyDoc>
211: *
212: * @param dOClass
213: * <copyDoc>Refer to {@link
214: * com.ivata.mask.persistence.QueryPersistenceManager#addRemoveListener
215: * QueryPersistenceManager.addRemoveListener}.</copyDoc>
216: * @param listener
217: * <copyDoc>Refer to {@link
218: * com.ivata.mask.persistence.QueryPersistenceManager#addRemoveListener
219: * QueryPersistenceManager.addRemoveListener}.</copyDoc>
220: */
221: public static synchronized void addRemoveListener(
222: final Class dOClass,
223: final RemovePersistenceListener listener) {
224: if (logger.isDebugEnabled()) {
225: logger.debug("addRemoveListener: " + listener);
226: }
227: List removeListenersThisClass = (List) removeListeners
228: .get(dOClass.getName());
229: if (removeListenersThisClass == null) {
230: removeListenersThisClass = new Vector();
231: removeListeners.put(dOClass.getName(),
232: removeListenersThisClass);
233: }
234: removeListenersThisClass.add(listener);
235:
236: if (logger.isDebugEnabled()) {
237: logger.debug("addRemoveListener - end");
238: }
239: }
240:
241: /**
242: * <copyDoc>Refer to {@link HibernateInterceptor()}.</copyDoc>
243: */
244: private HibernateSession hibernateSession;
245: /**
246: * <copyDoc>Refer to {@link HibernateInterceptor()}.</copyDoc>
247: */
248: private SessionFactory sessionFactory;
249:
250: /**
251: * Constructor.
252: *
253: * @param hibernateManagerParam System persistence manager.
254: * @param sessionFactoryParam Used for retrieving meta data about persisted
255: * classes.
256: */
257: public HibernateInterceptor(
258: final HibernateManager hibernateManagerParam,
259: final SessionFactory sessionFactoryParam) {
260: this .sessionFactory = sessionFactoryParam;
261: }
262:
263: /**
264: * {@inheritDoc}
265: *
266: * @param arg0Param {@inheritDoc}
267: */
268: public void afterTransactionBegin(final Transaction arg0Param) {
269: if (logger.isDebugEnabled()) {
270: logger.debug("beforeTransactionBegin called");
271: }
272:
273: if (logger.isDebugEnabled()) {
274: logger.debug("afterTransactionBegin(Transaction) - end");
275: }
276: }
277:
278: /**
279: * {@inheritDoc}
280: *
281: * @param arg0Param {@inheritDoc}
282: */
283: public void afterTransactionCompletion(final Transaction arg0Param) {
284: if (logger.isDebugEnabled()) {
285: logger.debug("afterTransactionCompletion called");
286: }
287:
288: if (logger.isDebugEnabled()) {
289: logger
290: .debug("afterTransactionCompletion(Transaction) - end");
291: }
292: }
293:
294: /**
295: * {@inheritDoc}
296: *
297: * @param arg0Param {@inheritDoc}
298: */
299: public void beforeTransactionCompletion(final Transaction arg0Param) {
300: if (logger.isDebugEnabled()) {
301: logger.debug("beforeTransactionCompletion called");
302: }
303:
304: if (logger.isDebugEnabled()) {
305: logger
306: .debug("beforeTransactionCompletion(Transaction) - end");
307: }
308: }
309:
310: /**
311: * {@inheritDoc}
312: *
313: * @param object {@inheritDoc}
314: * @param arg1 {@inheritDoc}
315: * @param arg2 {@inheritDoc}
316: * @param arg3 {@inheritDoc}
317: * @param arg4 {@inheritDoc}
318: * @param arg5 {@inheritDoc}
319: * @return This implementation always returns <code>null</code>.
320: */
321: public int[] findDirty(final Object object,
322: final Serializable arg1, final Object[] arg2,
323: final Object[] arg3, final String[] arg4, final Type[] arg5) {
324: if (logger.isDebugEnabled()) {
325: logger.debug("findDirty: " + object);
326: }
327:
328: if (logger.isDebugEnabled()) {
329: logger.debug("findDirty - end - return value = " + null);
330: }
331: return null;
332: }
333:
334: /**
335: * {@inheritDoc}
336: *
337: * @param arg0Param {@inheritDoc}
338: * @param arg1Param {@inheritDoc}
339: * @return This implementation always returns <code>null</code>.
340: */
341: public Object getEntity(final String arg0Param,
342: final Serializable arg1Param) {
343: if (logger.isDebugEnabled()) {
344: logger.debug("getEntity: returning null");
345: }
346:
347: if (logger.isDebugEnabled()) {
348: logger.debug("getEntity - end - return value = " + null);
349: }
350: return null;
351: }
352:
353: /**
354: * {@inheritDoc}
355: *
356: * @param arg0Param {@inheritDoc}
357: * @return This implementation always returns <code>null</code>.
358: */
359: public String getEntityName(final Object arg0Param) {
360: if (logger.isDebugEnabled()) {
361: logger.debug("getEntityName: returning null");
362: }
363:
364: if (logger.isDebugEnabled()) {
365: logger
366: .debug("getEntityName(Object) - end - return value = "
367: + null);
368: }
369: return null;
370: }
371:
372: /**
373: * Get the string name of the unique identifier property for the given
374: * class.
375: *
376: * @param theClass Class for which to find the identifier property.
377: * @return The string name of the unique identifier property for the given
378: * class.
379: */
380: private String getIdProperty(final Class theClass) {
381: if (logger.isDebugEnabled()) {
382: logger.debug("getIdProperty(Class theClass = " + theClass
383: + ") - start");
384: }
385:
386: try {
387: String returnString = sessionFactory.getClassMetadata(
388: theClass).getIdentifierPropertyName();
389: if (logger.isDebugEnabled()) {
390: logger
391: .debug("getIdProperty(Class) - end - return value = "
392: + returnString);
393: }
394: return returnString;
395: } catch (HibernateException e) {
396: logger.error("getIdProperty(Class)", e);
397:
398: throw new CallbackException(
399: "Error getting identifier property for class "
400: + theClass, e);
401: }
402: }
403:
404: /**
405: * Get a list of listener class names which are superclasses of the data
406: * object class provided.
407: *
408: * @param listeners all listeners of the type (add/amend/remove) to be got.
409: * @param dOClass the subclass to look for.
410: * @return a <code>List</code> of <code>String</code> instances
411: * @throws PersistenceException If a class cannot be found.
412: * @see org.hibernate.Interceptor#getListenerClasses
413: */
414: private List getListenerClasses(final Map listeners,
415: final Class dOClass) throws PersistenceException {
416: if (logger.isDebugEnabled()) {
417: logger.debug("getListenerClasses: getting for " + dOClass);
418: }
419: List listenerClasses = new Vector();
420: Set allClasses = listeners.keySet();
421: for (Iterator iterator = allClasses.iterator(); iterator
422: .hasNext();) {
423: String className = (String) iterator.next();
424: Class listenerClass;
425: try {
426: listenerClass = Class.forName(className);
427: } catch (ClassNotFoundException e) {
428: logger.error("getListenerClasses(Map, Class)", e);
429:
430: throw new PersistenceException(e);
431: }
432: if (listenerClass.isAssignableFrom(dOClass)) {
433: listenerClasses.add(className);
434: }
435: }
436: if (logger.isDebugEnabled()) {
437: logger.debug("getListenerClasses: found "
438: + listenerClasses.size() + " classes.");
439: }
440: return listenerClasses;
441: }
442:
443: /**
444: * {@inheritDoc}
445: *
446: * @param className {@inheritDoc}
447: * @param arg1Param {@inheritDoc}
448: * @param key {@inheritDoc}
449: * @return {@inheritDoc}
450: */
451: public Object instantiate(final String className,
452: final EntityMode arg1Param, final Serializable key) {
453: if (logger.isDebugEnabled()) {
454: logger.debug("instantiate(String className = " + className
455: + ", EntityMode arg1Param = " + arg1Param
456: + ", Serializable key = " + key + ") - start");
457: }
458:
459: Class dOClass;
460: try {
461: dOClass = Class.forName(className);
462: } catch (ClassNotFoundException e1) {
463: logger
464: .error(
465: "instantiate(String, EntityMode, Serializable)",
466: e1);
467:
468: throw new CallbackException(e1);
469: }
470: if (logger.isDebugEnabled()) {
471: logger.debug("instantiate: looking for instance of "
472: + dOClass);
473: }
474: PicoContainer globalContainer;
475: try {
476: globalContainer = PicoContainerFactory.getInstance()
477: .getGlobalContainer();
478: } catch (SystemException e) {
479: logger.error(
480: "instantiate(String, EntityMode, Serializable)", e);
481:
482: throw new CallbackException(e);
483: }
484: MutablePicoContainer tempContainer = new DefaultPicoContainer(
485: globalContainer);
486: tempContainer.registerComponentImplementation(dOClass);
487:
488: Object instance = tempContainer.getComponentInstance(dOClass);
489: try {
490: Integer id = null;
491: if (key != null) {
492: id = new Integer(key.toString());
493: }
494: PropertyUtils.setProperty(instance, getIdProperty(dOClass),
495: id);
496: } catch (CallbackException e) {
497: logger.error(e);
498: throw new CallbackException(
499: "Error setting property for key '" + key + "'", e);
500: } catch (IllegalAccessException e) {
501: logger.error(e);
502: throw new CallbackException(
503: "Error setting property for key '" + key + "'", e);
504: } catch (InvocationTargetException e) {
505: logger.error(e);
506: throw new CallbackException(
507: "Error setting property for key '" + key + "'", e);
508: } catch (NoSuchMethodException e) {
509: logger.error(e);
510: throw new CallbackException(
511: "Error setting property for key '" + key + "'", e);
512: }
513:
514: if (logger.isDebugEnabled()) {
515: logger.debug("instantiate: returning " + instance);
516: }
517: return instance;
518: }
519:
520: /**
521: * {@inheritDoc}
522: *
523: * @param object {@inheritDoc}
524: * @return {@inheritDoc}
525: */
526: public Boolean isTransient(final Object object) {
527: if (logger.isDebugEnabled()) {
528: logger.debug("isTransient(Object object = " + object
529: + ") - start");
530: }
531:
532: // if it is a value object, whether or not it is saved depends on the
533: // state of the id field
534: if (object instanceof ValueObject) {
535: ValueObject valueObject = (ValueObject) object;
536: boolean isUnsaved = (valueObject.getIdString() == null);
537: if (logger.isDebugEnabled()) {
538: logger.debug("isUnsaved: returning " + isUnsaved + ".");
539: }
540: return new Boolean(isUnsaved);
541: }
542: if (logger.isDebugEnabled()) {
543: logger.debug("isUnsaved: returning null.");
544: }
545: return null;
546: }
547:
548: /**
549: * {@inheritDoc}
550: *
551: * @param object {@inheritDoc}
552: * @param id {@inheritDoc}
553: * @param state {@inheritDoc}
554: * @param propertyNames {@inheritDoc}
555: * @param types {@inheritDoc}
556: */
557: public void onDelete(final Object object, final Serializable id,
558: final Object[] state, final String[] propertyNames,
559: final Type[] types) {
560: if (logger.isDebugEnabled()) {
561: logger.debug("In onDelete: " + object.toString());
562: }
563: // only interested in our value objects!
564: if (!(object instanceof ValueObject)) {
565: if (logger.isDebugEnabled()) {
566: logger
567: .debug("In onDelete: object is not a value object.");
568: }
569: return;
570: }
571: ValueObject valueObject = (ValueObject) object;
572: Class valueObjectClass = valueObject.getClass();
573: List listenerClasses;
574: try {
575: listenerClasses = getListenerClasses(removeListeners,
576: valueObjectClass);
577: } catch (PersistenceException e) {
578: logger.error(
579: "onDelete - error getting the listener classes for '"
580: + valueObjectClass + "'", e);
581: throw new CallbackException(e);
582: }
583: for (Iterator classNameIterator = listenerClasses.iterator(); classNameIterator
584: .hasNext();) {
585: String super className = (String) classNameIterator.next();
586: List listeners = (List) removeListeners.get(super className);
587: if (logger.isDebugEnabled()) {
588: logger.debug("In onDelete: processing "
589: + listeners.size()
590: + " listener(s) of superclass "
591: + super className);
592: }
593: for (Iterator listenerIterator = listeners.iterator(); listenerIterator
594: .hasNext();) {
595: RemovePersistenceListener listener = (RemovePersistenceListener) listenerIterator
596: .next();
597: try {
598: listener.onRemove(hibernateSession, valueObject);
599: } catch (PersistenceException e) {
600: logger.error("onDelete - error calling onRemove.",
601: e);
602: throw new CallbackException(e);
603: }
604: }
605: }
606: if (logger.isDebugEnabled()) {
607: logger.debug("Leaving onDelete.");
608: }
609: }
610:
611: /**
612: * {@inheritDoc}
613: *
614: * @param object {@inheritDoc}
615: * @param id {@inheritDoc}
616: * @param currentState {@inheritDoc}
617: * @param previousState {@inheritDoc}
618: * @param propertyNames {@inheritDoc}
619: * @param types {@inheritDoc}
620: * @return {@inheritDoc}
621: */
622: public boolean onFlushDirty(final Object object,
623: final Serializable id, final Object[] currentState,
624: final Object[] previousState, final String[] propertyNames,
625: final Type[] types) {
626: if (logger.isDebugEnabled()) {
627: logger.debug("In onFlushDirty: " + object.toString());
628: }
629: // only interested in our value objects!
630: if (!(object instanceof ValueObject)) {
631: if (logger.isDebugEnabled()) {
632: logger
633: .debug("onFlushDirty - object is not a value object.");
634: logger.debug("onFlushDirty - end - returning false");
635: }
636: return false;
637: }
638: ValueObject valueObject = (ValueObject) object;
639: List listenerClasses;
640: try {
641: listenerClasses = getListenerClasses(amendListeners,
642: valueObject.getClass());
643: } catch (PersistenceException e) {
644: logger.error(
645: "onFlushDirty - exception getting listener classes for '"
646: + valueObject.getClass() + "'", e);
647: throw new CallbackException(e);
648: }
649: if (listenerClasses.isEmpty()) {
650: if (logger.isDebugEnabled()) {
651: logger
652: .debug("Leaving onFlushDirty: no listeners found for class "
653: + valueObject.getClass());
654: }
655: return false;
656: }
657: for (Iterator classNameIterator = listenerClasses.iterator(); classNameIterator
658: .hasNext();) {
659: String super className = (String) classNameIterator.next();
660: List listeners = (List) amendListeners.get(super className);
661: if (logger.isDebugEnabled()) {
662: logger.debug("In onFlushDirty: processing "
663: + listeners.size()
664: + " listener(s) of superclass "
665: + super className);
666: }
667: for (Iterator listenerIterator = listeners.iterator(); listenerIterator
668: .hasNext();) {
669: AmendPersistenceListener listener = (AmendPersistenceListener) listenerIterator
670: .next();
671: try {
672: listener.onAmend(hibernateSession, valueObject);
673: } catch (PersistenceException e) {
674: logger.error("onFlushDirty - error in onAmend", e);
675:
676: throw new CallbackException(e);
677: }
678: // no idea why this is necessary! why not let me modify the
679: // object and let Hibernate worry about the state?!
680: // FIXME: not working - throws ClassCastException on property
681: // type if collection
682: // updateState("onFlushDirty", valueObject, currentState,
683: // propertyNames);
684: }
685: }
686: if (logger.isDebugEnabled()) {
687: logger.debug("Leaving onFlushDirty: returning true.");
688: }
689: return true;
690: }
691:
692: /**
693: * {@inheritDoc}
694: *
695: * @param arg0 {@inheritDoc}
696: * @param arg1 {@inheritDoc}
697: * @param arg2 {@inheritDoc}
698: * @param arg3 {@inheritDoc}
699: * @param arg4 {@inheritDoc}
700: * @return Always returns <code>false</code>.
701: */
702: public boolean onLoad(final Object arg0, final Serializable arg1,
703: final Object[] arg2, final String[] arg3, final Type[] arg4) {
704: if (logger.isDebugEnabled()) {
705: logger.debug("onLoad - start");
706: }
707:
708: if (logger.isDebugEnabled()) {
709: logger.debug("onLoad - end - return value = " + false);
710: }
711: return false;
712: }
713:
714: /**
715: * {@inheritDoc}
716: *
717: * @param object {@inheritDoc}
718: * @param id {@inheritDoc}
719: * @param state {@inheritDoc}
720: * @param propertyNames {@inheritDoc}
721: * @param types {@inheritDoc}
722: * @return {@inheritDoc}
723: */
724: public boolean onSave(final Object object, final Serializable id,
725: final Object[] state, final String[] propertyNames,
726: final Type[] types) {
727: if (logger.isDebugEnabled()) {
728: logger.debug("In onSave: " + object.toString());
729: }
730: // only interested in our value objects!
731: if (!(object instanceof ValueObject)) {
732: if (logger.isDebugEnabled()) {
733: logger
734: .debug("Leaving onSave: object is not a value object.");
735: }
736: return false;
737: }
738: ValueObject valueObject = (ValueObject) object;
739: List listenerClasses;
740: try {
741: listenerClasses = getListenerClasses(addListeners,
742: valueObject.getClass());
743: } catch (PersistenceException e) {
744: logger
745: .error(
746: "onSave(Object, Serializable, Object[], String[], Type[])",
747: e);
748:
749: throw new CallbackException(e);
750: }
751: if (listenerClasses.isEmpty()) {
752: if (logger.isDebugEnabled()) {
753: logger
754: .debug("Leaving onSave: no listeners found for class "
755: + valueObject.getClass());
756: }
757: return false;
758: }
759: for (Iterator classNameIterator = listenerClasses.iterator(); classNameIterator
760: .hasNext();) {
761: String super className = (String) classNameIterator.next();
762: List listeners = (List) addListeners.get(super className);
763: if (logger.isDebugEnabled()) {
764: logger.debug("In onSave: processing "
765: + listeners.size()
766: + " listener(s) of superclass "
767: + super className);
768: }
769: for (Iterator listenerIterator = listeners.iterator(); listenerIterator
770: .hasNext();) {
771: AddPersistenceListener listener = (AddPersistenceListener) listenerIterator
772: .next();
773: try {
774: listener.onAdd(hibernateSession, valueObject);
775: } catch (PersistenceException e) {
776: logger.error("onSave - error in onAdd", e);
777: throw new CallbackException(e);
778: }
779: // no idea why this is necessary! why not let me modify the
780: // object and let Hibernate worry about the state?!
781: updateState("onSave", valueObject, state, propertyNames);
782: }
783: }
784:
785: if (logger.isDebugEnabled()) {
786: logger.debug("Leaving onSave: returning true.");
787: }
788: return true;
789: }
790:
791: /**
792: * {@inheritDoc}
793: *
794: * @param arg0 {@inheritDoc}
795: */
796: public void postFlush(final Iterator arg0) {
797: if (logger.isDebugEnabled()) {
798: logger.debug("postFlush - doing nothing");
799: }
800:
801: if (logger.isDebugEnabled()) {
802: logger.debug("postFlush(Iterator) - end");
803: }
804: }
805:
806: /**
807: * {@inheritDoc}
808: *
809: * @param arg0 {@inheritDoc}
810: */
811: public void preFlush(final Iterator arg0) {
812: if (logger.isDebugEnabled()) {
813: logger.debug("preFlush - doing nothing");
814: }
815:
816: if (logger.isDebugEnabled()) {
817: logger.debug("preFlush(Iterator) - end");
818: }
819: }
820:
821: /**
822: * Chicken/egg situation - can't put this in the constructor.
823: * @param hibernateSessionParam
824: * The current Hibernate session for which this interceptor was made.
825: */
826: public void setHibernateSession(
827: final HibernateSession hibernateSessionParam) {
828: if (logger.isDebugEnabled()) {
829: logger.debug("setHibernateSession: "
830: + hibernateSessionParam);
831: }
832: this .hibernateSession = hibernateSessionParam;
833:
834: if (logger.isDebugEnabled()) {
835: logger.debug("setHibernateSession(HibernateSession) - end");
836: }
837: }
838:
839: /**
840: * Helper method to avoid repetition. Updates the current state from the
841: * value object.
842: *
843: * @param methodName Name of the method which called this method.
844: * @param valueObject Value object to be read from.
845: * @param state State to read from the value object.
846: * @param propertyNames Names of all the properties to update from the value
847: * object.
848: */
849: private void updateState(final String methodName,
850: final ValueObject valueObject, final Object[] state,
851: final String[] propertyNames) {
852: if (logger.isDebugEnabled()) {
853: logger.debug("updateState(String methodName = "
854: + methodName + ", ValueObject valueObject = "
855: + valueObject + ", Object[] state = " + state
856: + ", String[] propertyNames = " + propertyNames
857: + ") - start");
858: }
859:
860: for (int i = 0; i < propertyNames.length; i++) {
861: String propertyName = propertyNames[i];
862: try {
863: Object value = PropertyUtils.getProperty(valueObject,
864: propertyName);
865: if (logger.isDebugEnabled()) {
866: logger.debug(methodName + ": property '"
867: + propertyName + "' was '" + state[i]
868: + "', now '" + value + "'");
869: }
870: state[i] = value;
871: } catch (IllegalAccessException e1) {
872: logger.error("error getting property '" + propertyName
873: + "'", e1);
874: throw new CallbackException(e1);
875: } catch (InvocationTargetException e1) {
876: logger.error("error getting property '" + propertyName
877: + "'", e1);
878: throw new CallbackException(e1);
879: } catch (NoSuchMethodException e1) {
880: logger.error("error getting property '" + propertyName
881: + "'", e1);
882: throw new CallbackException(e1);
883: }
884: }
885:
886: if (logger.isDebugEnabled()) {
887: logger.debug("updateState - end");
888: }
889: }
890: }
|