1 package org.weda.hibernate;
2
3 import java.io.FileInputStream;
4 import java.io.IOException;
5 import java.io.InputStream;
6 import java.io.Serializable;
7 import java.sql.Blob;
8 import java.sql.Connection;
9 import java.sql.DatabaseMetaData;
10 import java.sql.Savepoint;
11 import java.util.ArrayList;
12 import java.util.Collection;
13 import java.util.HashMap;
14 import java.util.Iterator;
15 import java.util.List;
16 import java.util.Map;
17 import java.util.Properties;
18 import java.util.Set;
19 import javax.servlet.ServletContext;
20 import org.apache.commons.lang.ObjectUtils;
21 import org.apache.commons.logging.Log;
22 import org.hibernate.EntityMode;
23 import org.hibernate.Hibernate;
24 import org.hibernate.HibernateException;
25 import org.hibernate.LockMode;
26 import org.hibernate.Session;
27 import org.hibernate.SessionFactory;
28 import org.hibernate.StaleStateException;
29 import org.hibernate.Transaction;
30 import org.hibernate.cfg.AnnotationConfiguration;
31 import org.hibernate.event.PostDeleteEvent;
32 import org.hibernate.event.PostDeleteEventListener;
33 import org.hibernate.event.PostInsertEvent;
34 import org.hibernate.event.PostInsertEventListener;
35 import org.hibernate.event.PostUpdateEvent;
36 import org.hibernate.event.PostUpdateEventListener;
37 import org.hibernate.event.PreInsertEvent;
38 import org.hibernate.event.PreUpdateEvent;
39 import org.hibernate.event.PreUpdateEventListener;
40 import org.hibernate.metadata.ClassMetadata;
41 import org.hibernate.persister.entity.EntityPersister;
42 import org.weda.converter.ValueTypeConverter;
43 import org.weda.domain.ObjectModification;
44 import org.weda.domain.ObjectModificationPk;
45 import org.weda.domain.ObjectModificationType;
46 import org.weda.domain.PropertyModification;
47 import org.weda.property.ObjectDescriptorRegistry;
48 import org.weda.property.PropertyGetOperationListener;
49 import org.weda.property.PropertyListenerException;
50 import org.weda.property.PropertyListenerResult;
51 import org.weda.property.PropertyValue;
52 import org.weda.property.PropertyValueException;
53 import org.weda.store.AuditOperation;
54 import org.weda.store.AuditRuntimeException;
55 import org.weda.store.Auditable;
56 import org.weda.store.AuditableObjectRegistry;
57 import org.weda.store.ObjectStoreException;
58 import org.weda.store.Query;
59 import org.weda.store.QueryFilterElement;
60 import org.weda.cache.CacheManager;
61 import org.weda.store.ObjectSource;
62 import org.weda.store.ObjectStore;
63 import org.weda.store.QueryFilterElement.ExpressionType;
64 import org.weda.store.QueryFilterElement.OperatorType;
65
66
67 /**
68 *
69 * @author tim
70 */
71 public class HibernateObjectStore
72 implements
73 ObjectStore, PropertyGetOperationListener
74 , PostInsertEventListener, PreUpdateEventListener
75 , PostDeleteEventListener
76 {
77 private final static String ID =
78 HibernateObjectStore.class.getName();
79 private final static String PARAM_PREFIX="param";
80 private final static long LOCK_TIMEOUT = 10000;
81
82 private AuditableObjectRegistry auditReg;
83 private ValueTypeConverter converter;
84 private ObjectDescriptorRegistry descriptorRegistry;
85
86 private SessionFactory sessionFactory;
87 private AnnotationConfiguration config;
88 private Log log;
89 private CacheManager cacheManager;
90 private ServletContext servletContext;
91 private PropertyValue propertyValue;
92 private Map<String, PropertyMapping[]> propertyMappings;
93 private ThreadLocal<List> auditObjectsToSave =
94 new ThreadLocal<List>(){
95 protected List initialValue() {
96 return new ArrayList();
97 }
98 };
99 private boolean autoCommit = true;
100
101
102 /** Creates a new instance of HibernateProvider */
103 public HibernateObjectStore() {
104 config = new AnnotationConfiguration();
105 }
106
107 public void init() throws ObjectStoreException {
108 try{
109
110 sessionFactory=config.buildSessionFactory();
111 for (Object obj: sessionFactory.getAllClassMetadata().values()){
112 ClassMetadata classMeta = (ClassMetadata)obj;
113 if (classMeta.hasProxy()){
114 Class entityClass =
115 classMeta.getMappedClass(EntityMode.POJO);
116 boolean[] lazyProps = classMeta.getPropertyLaziness();
117 for (int i=0; i<lazyProps.length; i++)
118 if (lazyProps[i]){
119 propertyValue.addGetOperationListener(
120 entityClass
121 , classMeta.getPropertyNames()[i]
122 , this);
123 }
124 }
125 }
126 }catch(PropertyValueException e){
127 throw new ObjectStoreException(
128 "Can't initialize persistent service", e);
129 }
130 }
131
132 private void initAudit() {
133 for (AuditOperation operation: AuditOperation.values()){
134 Set<Class> auditableClasses =
135 auditReg.getAuditaleObjectsClasses(operation);
136 if (operation == AuditOperation.ADD)
137 config.getEventListeners().setPostInsertEventListeners(
138 new PostInsertEventListener[]{this});
139 else if (operation == AuditOperation.UPDATE)
140 config.getEventListeners().setPreUpdateEventListeners(
141 new PreUpdateEventListener[]{this});
142 else if (operation == AuditOperation.REMOVE)
143 config.getEventListeners().setPostDeleteEventListeners(
144 new PostDeleteEventListener[]{this});
145 }
146 }
147
148 public void setLog(Log log){
149 this.log=log;
150 }
151
152 public void setConfiguration(String path){
153 config.configure(path);
154 }
155
156 public void setConfig(List<Class> classes) throws Exception {
157 for (Class entityClass: classes)
158 config.addAnnotatedClass(entityClass);
159 }
160
161 public void setServletContext(List<ServletContext> list){
162 servletContext = list.get(0);
163 }
164
165 public void setProperties(Map<String, String> locations) throws Exception {
166 String path=null;
167 String pathType=null;
168 for (Map.Entry entry: locations.entrySet()){
169 path=(String)entry.getKey();
170 pathType=(String)entry.getValue();
171 }
172 log.debug("seting properties. path: "+path+", pathType: "+pathType);
173 if ( "file".equals(pathType))
174 loadPropertiesFromFile(path);
175 else if ("resource".equals(pathType))
176 loadPropertiesFromResource(path);
177 else if ("web-resource".equals(pathType))
178 loadPropertiesFromServletContext(path);
179 }
180
181 private void loadPropertiesFromFile(String path) throws Exception {
182 InputStream is = new FileInputStream(path);
183 loadPropertiesFromStream(is);
184 }
185
186 private void loadPropertiesFromResource(String path) throws Exception{
187 InputStream is =
188 this.getClass().getClassLoader().getResourceAsStream(path);
189 loadPropertiesFromStream(is);
190 }
191
192 private void loadPropertiesFromServletContext(String path) throws Exception{
193 InputStream is=servletContext.getResourceAsStream(path);
194 loadPropertiesFromStream(is);
195 }
196
197 private void loadPropertiesFromStream(InputStream is) throws Exception{
198 try{
199 Properties props = new Properties();
200 props.load(is);
201 config.addProperties(props);
202 }finally{
203 is.close();
204 }
205 }
206
207 public void save(Object obj) throws ObjectStoreException {
208 if (log.isDebugEnabled())
209 log.debug(obj);
210 try{
211 beginTransaction();
212 Session sess = getSession();
213 saveObjectState(obj);
214 sess.saveOrUpdate(obj);
215 if (auditObjectsToSave.get().size()>0){
216 for (Object auditObj: auditObjectsToSave.get())
217 sess.saveOrUpdate(auditObj);
218 auditObjectsToSave.get().clear();
219 }
220 commit();
221 }catch(Exception e){
222 rollback();
223 throw new ObjectStoreException(
224 String.format(
225 "Can't save object (%s) of class (%s)"
226 , obj.toString(), obj.getClass().getName())
227 , e);
228 }
229 }
230
231 private void saveObjectState(Object obj){
232 Set<String> props =
233 auditReg.getAuditableObjectProperties(
234 AuditOperation.UPDATE, obj.getClass());
235 if (props!=null){
236
237 }
238
239 }
240
241 public Object load(Class clazz, Serializable id)
242 throws ObjectStoreException
243 {
244 Object res=null;
245 try{
246 beginTransaction();
247 Session sess = getSession();
248 res=sess.get(clazz, id);
249 commit();
250 }catch(Exception e){
251 rollback();
252 throw new ObjectStoreException(
253 String.format(
254 "Can't load object whith id (%s) of class (%s)"
255 , id.toString(), clazz.getName())
256 , e);
257 }
258 return res;
259 }
260
261 public void refresh(Object obj) throws ObjectStoreException{
262 try{
263 beginTransaction();
264 Session session = getSession();
265 session.refresh(obj);
266 commit();
267 }catch(Exception e){
268 rollback();
269 throw new ObjectStoreException(
270 "Can't refresh object state", e);
271 }
272 }
273
274 public Serializable getObjectId(Object obj) throws ObjectStoreException {
275 try {
276 beginTransaction();
277 try{
278 Session session = getSession();
279 session.refresh(obj);
280 return session.getIdentifier(obj);
281 }finally{
282 commit();
283 }
284 } catch (Exception e) {
285 throw new ObjectStoreException(
286 "Error getting object identificator", e);
287 }
288 }
289
290 protected HibernateCacheEntity getCacheEntity()
291 throws ObjectStoreException
292 {
293 HibernateCacheEntity state=
294 (HibernateCacheEntity)cacheManager.getCacheService().get(ID);
295 if (state==null){
296 state = new HibernateCacheEntity();
297 cacheManager.getCacheService().setCacheEntity(ID, state);
298 }
299 return state;
300 }
301
302 protected Session getSession() throws ObjectStoreException {
303 return getCacheEntity().getSession();
304 }
305
306 public String toString(){
307 return "Hibernate persitent service";
308 }
309
310 public CacheManager getCacheManager() {
311 return cacheManager;
312 }
313
314 public void setCacheManager(CacheManager cacheManager) {
315 this.cacheManager = cacheManager;
316 }
317
318 private void formQueryString (
319 StringBuffer queryString
320 , Map<String, Object> paramValues
321 , ParameterNameConstructor nameConstructor
322 , Collection<QueryFilterElement>... elementsList)
323 {
324 for (Collection<QueryFilterElement> elements: elementsList)
325 for (QueryFilterElement element: elements){
326 if (!element.isEnabled() || element.getParameterName()!=null)
327 continue;
328 if (element.getExpressionType()!=ExpressionType.EMPTY){
329 queryString.append(" AND "+element.getObjectAlias());
330 if (!element.isUseAliasAsProperty())
331 queryString.append("."+element.getProperty());
332 if (element.getExpressionType()
333 == ExpressionType.COMPLETE)
334 {
335 queryString.append(" "+element.getValue());
336 }else if (element.getExpressionType()
337 == ExpressionType.OPERATOR)
338 {
339 OperatorType type = element.getOperatorType();
340 if ( type==OperatorType.SIMPLE
341 || type==OperatorType.LIKE)
342 {
343 String paramName=nameConstructor.getNextName();
344 String operator =
345 type==OperatorType.LIKE?
346 "LIKE":element.getOperator();
347 queryString.append(" "+operator+" "+":"+paramName);
348 paramValues.put(
349 paramName, element.getParameterValue());
350 }else{
351 Object[] vals = (Object[])element.getValue();
352 if (type==OperatorType.BETWEEN){
353 String param1 = nameConstructor.getNextName();
354 String param2 = nameConstructor.getNextName();
355 queryString.append(
356 " BETWEEN "+":"+param1+" AND "+":"+param2);
357 paramValues.put(param1, vals[0]);
358 paramValues.put(param2, vals[1]);
359 }else{
360 queryString.append(" IN (");
361 boolean firstVal = true;
362 for (Object val: vals){
363 String param =
364 nameConstructor.getNextName();
365 if (firstVal)
366 firstVal = false;
367 else
368 queryString.append(',');
369 queryString.append(":"+param);
370 paramValues.put(param, val);
371 }
372 queryString.append(")");
373 }
374 }
375 }
376 queryString.append("\n");
377 }
378 }
379 }
380
381 private org.hibernate.Query constructQuery(ObjectSource dataSource)
382 throws ObjectStoreException
383 {
384 org.hibernate.Query hquery=null;
385 try{
386 Query query = dataSource.getQuery();
387
388 Map<String, Object> paramValues=new HashMap<String, Object>();
389 Collection<QueryFilterElement> staticElements=
390 dataSource.getQueryFilter().getStaticFilterElements();
391 Collection<QueryFilterElement> elements=
392 dataSource.getQueryFilter().getFilterElements();
393
394 Map<String, Object> reservedParameters =
395 new HashMap<String, Object>();
396 extractReservedParameterNames(
397 reservedParameters, staticElements, elements);
398
399 StringBuffer whereClause = new StringBuffer();
400 ParameterNameConstructor nameConstructor =
401 new ParameterNameConstructor(reservedParameters.keySet());
402 formQueryString(
403 whereClause, paramValues, nameConstructor
404 , staticElements, elements);
405 String queryString = query.getQuery();
406 int insertMarkerPos =
407 queryString.indexOf(FILTER_INSERT_POSITION_MARKER);
408 if (insertMarkerPos>0) {
409 queryString =
410 queryString.replace(
411 FILTER_INSERT_POSITION_MARKER, whereClause);
412 }else
413 queryString = queryString + "\nWHERE 1=1\n"+whereClause;
414 if (log.isDebugEnabled())
415 log.debug("QUERY: "+queryString);
416 hquery=getSession().createQuery(queryString);
417 hquery.setMaxResults(query.getMaxRows());
418 hquery.setFetchSize(query.getFetchSize());
419 hquery.setTimeout(query.getTimeout());
420 setParameterValues(hquery, paramValues, reservedParameters);
421 }catch(HibernateException e){
422 throw new ObjectStoreException(String.format(
423 "Can't construct select query for dataSource (%s)"
424 , dataSource.getName())
425 , e);
426 }
427 return hquery;
428
429 }
430
431 private void extractReservedParameterNames(
432 Map<String, Object> reservedParameters
433 , Collection<QueryFilterElement>... elementsList)
434 {
435 for (Collection<QueryFilterElement> elements: elementsList)
436 for (QueryFilterElement element: elements)
437 if (element.getParameterName()!=null)
438 reservedParameters.put(
439 element.getParameterName()
440 , element.getParameterValue());
441 }
442
443 private void setParameterValues(
444 org.hibernate.Query hquery, Map<String, Object>... valuesList)
445 {
446 for (Map<String, Object> paramValues: valuesList)
447 for (Map.Entry<String, Object> entry: paramValues.entrySet())
448 hquery.setParameter(entry.getKey(), entry.getValue());
449 }
450
451 public boolean isPropertyValueNull(Object obj, String propertyName)
452 throws ObjectStoreException
453 {
454 try{
455 beginTransaction();
456 String query =
457 "select count(*) from "+obj.getClass().getName()+" as obj "
458 +"where obj = :param and "+propertyName+" is null";
459 org.hibernate.Query hquery = getSession().createQuery(query);
460 hquery.setParameter("param", obj);
461 int count = ((Number)hquery.list().get(0)).intValue();
462 return count != 0;
463 }finally{
464 commit();
465 }
466 }
467
468
469 public List<Object[]> queryAllRows(ObjectSource dataSource)
470 throws ObjectStoreException
471 {
472 List<Object[]> res=null;
473 try{
474 beginTransaction();
475 Session session=getSession();
476
477 org.hibernate.Query hquery=constructQuery(dataSource);
478 List rows=hquery.list();
479 if (rows.size()==0 || !(rows.get(0) instanceof Object[])){
480 res=new ArrayList<Object[]>();
481 for (Object obj: rows)
482 res.add(new Object[]{obj});
483 }else
484 res=(List<Object[]>)rows;
485 }finally{
486 commit();
487 }
488 return res;
489 }
490
491 public Iterator query(ObjectSource dataSource) {
492 return null;
493 }
494
495 public void delete(Object obj) throws ObjectStoreException {
496 beginTransaction();
497 try{
498 Session sess = getSession();
499 sess.delete(obj);
500 commit();
501 }catch(Exception e){
502 rollback();
503 throw new ObjectStoreException(
504 String.format(
505 "Can't delete object (%s) of class (%s)"
506 , obj.toString(), obj.getClass().getName())
507 , e);
508 }
509 }
510
511 public void deleteAll(Class objectClass) throws ObjectStoreException{
512 try{
513 beginTransaction();
514 Session sess = getSession();
515 org.hibernate.Query query =
516 sess.createQuery("delete from "+objectClass.getName());
517 query.executeUpdate();
518 commit();
519 }catch(Exception e){
520 rollback();
521 throw new ObjectStoreException(
522 String.format(
523 "Can't delete all objects of class (%s)"
524 , objectClass.getName())
525 , e);
526 }
527 }
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544 public void commit() throws ObjectStoreException, HibernateException{
545 endTransaction(true);
546 }
547
548 public void rollback()
549 throws ObjectStoreException, HibernateException
550 {
551 endTransaction(false);
552 }
553
554 private void endTransaction(boolean commit)
555 throws ObjectStoreException, HibernateException
556 {
557 String operation = commit ? "commit" : "rollback";
558 HibernateCacheEntity sessionState = getCacheEntity();
559 try{
560 int callCounter = sessionState.getCallCounter();
561 if (callCounter==1){
562 Transaction tx = getCacheEntity().getSession().getTransaction();
563 if (tx==null || !tx.isActive()){
564 sessionState.setEndTransactionError(true);
565 throw new ObjectStoreException(
566 String.format(
567 "Can't %s transaction, because of no active " +
568 "transaction"
569 , operation));
570 }
571 if (commit)
572 tx.commit();
573 else
574 tx.rollback();
575 if (log.isDebugEnabled())
576 log.debug("Transaction " + operation + " operation done");
577 reset();
578 }else if (callCounter>1){
579 sessionState.setCallCounter(callCounter-1);
580 sessionState.setEndTransactionError(false);
581 if (sessionState.isSupportsSavepoint()){
582 Connection con = sessionState.getSession().connection();
583 Savepoint savepoint = sessionState.getSavepoints().pop();
584 if (commit){
585 if (sessionState.isSupportsReleaseSavepoint())
586 con.releaseSavepoint(savepoint);
587 }else
588 con.rollback(savepoint);
589 }
590
591 }else
592 throw new ObjectStoreException(
593 String.format(
594 "Can't %s transaction, because of no active " +
595 "transaction"
596 , operation));
597 }catch(Exception e){
598 sessionState.setEndTransactionError(true);
599 if (e instanceof StaleStateException)
600 throw (StaleStateException)e;
601 throw new ObjectStoreException(
602 String.format("Can't %s hibernate transaction", operation)
603 , e);
604 }
605 }
606
607 public void beginTransaction() throws ObjectStoreException{
608 try{
609 HibernateCacheEntity sessionState = getCacheEntity();
610 int callCounter = sessionState.getCallCounter();
611 Session session = sessionState.getSession();
612 if (callCounter == 0){
613 session = sessionFactory.openSession();
614 session.beginTransaction();
615 sessionState.setSession(session);
616 DatabaseMetaData meta = session.connection().getMetaData();
617 sessionState.setSupportsSavepoint(meta.supportsSavepoints());
618 sessionState.setSupportsReleaseSavepoint(
619 !"Oracle".equals(meta.getDatabaseProductName()));
620 if (log.isWarnEnabled() && !sessionState.isSupportsSavepoint())
621 log.warn("Database not supports savepoints");
622 if (log.isDebugEnabled())
623 log.debug("Transaction started");
624 }
625 if (!sessionState.isEndTransactionError()){
626 if (callCounter!=0 && sessionState.isSupportsSavepoint())
627 sessionState.getSavepoints().push(
628 session.connection().setSavepoint());
629 sessionState.setCallCounter(callCounter+1);
630 }
631 }catch(Exception e){
632 throw new ObjectStoreException(
633 "Can't start hibernate transaction", e);
634 }
635 }
636
637 private void showTransactionStatus() throws ObjectStoreException {
638 if (log.isDebugEnabled()){
639 HibernateCacheEntity sessionState = getCacheEntity();
640 Session session = sessionState.getSession();
641
642 }
643 }
644
645
646
647
648
649
650
651
652 public boolean isAutoCommit() {
653 return autoCommit;
654 }
655
656 public void setAutoCommit(boolean autoCommit) {
657 this.autoCommit = autoCommit;
658 }
659
660 public void reset() {
661 cacheManager.getCacheService().release(ID);
662 }
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682 public void lock(Object obj) throws ObjectStoreException {
683 try{
684 Session session = getSession();
685 LockerThread locker = new LockerThread(session, obj);
686 locker.start();
687 locker.join(LOCK_TIMEOUT);
688 boolean lockError = false;
689 if (locker.isAlive()){
690 session.cancelQuery();
691 locker.join();
692 lockError = true;
693 }
694 if (locker.getException()!=null)
695 throw locker.getException();
696 if (locker.isInterrupted())
697 lockError = true;
698 if (lockError)
699 throw new ObjectStoreException(
700 "Object locked for update by other session");
701 }catch(Exception e){
702 if (!(e instanceof ObjectStoreException))
703 throw new ObjectStoreException(
704 "Object locked for update by other session", e);
705 else throw (ObjectStoreException)e;
706 }
707 }
708
709 public void flushUpdates() throws ObjectStoreException {
710 try{
711 getSession().flush();
712 }catch(HibernateException e){
713 throw new ObjectStoreException(
714 "Can't flush the session updates", e);
715 }
716 }
717
718 private class LockerThread extends Thread {
719 private Session session;
720 private Object object;
721 private HibernateException exception;
722
723 public LockerThread(Session session, Object obj){
724 this.session = session;
725 this.object = obj;
726 }
727
728
729
730
731
732
733
734
735 public void run() {
736 try{
737 session.refresh(object, LockMode.UPGRADE_NOWAIT);
738
739
740
741
742
743
744
745
746
747 }catch(HibernateException e){
748 exception = e;
749 }
750 }
751
752 public HibernateException getException() {
753 return exception;
754 }
755 }
756
757 public void setPropertyValue(PropertyValue propertyValue) {
758 this.propertyValue = propertyValue;
759 }
760
761 public PropertyListenerResult beforeGet(Object propOwner, String propName)
762 throws PropertyListenerException
763 {
764 try{
765 beginTransaction();
766 refresh(propOwner);
767 return null;
768 }catch(ObjectStoreException e){
769 throw new PropertyListenerException("", e);
770 }
771 }
772
773 public PropertyListenerResult afterGet(
774 Object propOwner, String propName, Object propValue)
775 throws PropertyListenerException
776 {
777 try{
778 commit();
779 return null;
780 }catch(ObjectStoreException e){
781 throw new PropertyListenerException("", e);
782 }
783 }
784
785 public Blob createBlob(InputStream inStream) throws ObjectStoreException {
786 try {
787 return Hibernate.createBlob(inStream);
788 } catch (IOException ex) {
789 throw new ObjectStoreException("Error while creating BLOB", ex);
790 }
791 }
792
793 public void onPostInsert(PostInsertEvent event) {
794 try{
795 Set<String> props =
796 auditReg.getAuditableObjectProperties(
797 AuditOperation.ADD, event.getEntity().getClass());
798 if (props!=null){
799 ObjectModificationType objModType =
800 getObjectModificationType(
801 AuditOperation.ADD, event.getEntity());
802 PropertyMapping[] mappings =
803 getPropertiesMapping(
804 props
805 , AuditOperation.ADD
806 , event.getPersister()
807 , event.getEntity().getClass());
808 for (int i=0; i<mappings.length; ++i){
809 String value =
810 (String)converter.convert(
811 String.class
812 , event.getState()[
813 mappings[i].positionInPersistor]
814 , mappings[i].pattern);
815 PropertyModification propMod =
816 new PropertyModification(
817 objModType
818 , mappings[i].propertyName
819 , value);
820 auditObjectsToSave.get().add(propMod);
821 }
822 }
823 }catch(Exception e){
824 throw new AuditRuntimeException(
825 "Error while processing audit operation", e);
826 }
827 }
828
829 public void onPostDelete(PostDeleteEvent event) {
830 try{
831 Set<String> props =
832 auditReg.getAuditableObjectProperties(
833 AuditOperation.REMOVE, event.getEntity().getClass());
834 if (props!=null){
835 ObjectModificationType objModType =
836 getObjectModificationType(
837 AuditOperation.REMOVE, event.getEntity());
838 PropertyMapping[] mappings =
839 getPropertiesMapping(
840 props
841 , AuditOperation.REMOVE
842 , event.getPersister()
843 , event.getEntity().getClass());
844 for (int i=0; i<mappings.length; ++i){
845 String value =
846 (String)converter.convert(
847 String.class
848 , event.getDeletedState()[
849 mappings[i].positionInPersistor]
850 , mappings[i].pattern);
851 PropertyModification propMod =
852 new PropertyModification(
853 objModType
854 , mappings[i].propertyName
855 , value);
856 auditObjectsToSave.get().add(propMod);
857 }
858 }
859 }catch(Exception e){
860 throw new AuditRuntimeException(
861 "Error while processing audit operation", e);
862 }
863 }
864
865 public boolean onPreUpdate(PreUpdateEvent event) {
866 try{
867 Set<String> props =
868 auditReg.getAuditableObjectProperties(
869 AuditOperation.UPDATE, event.getEntity().getClass());
870 if (props!=null){
871 ObjectModificationType objModType =
872 getObjectModificationType(
873 AuditOperation.UPDATE, event.getEntity());
874 PropertyMapping[] mappings =
875 getPropertiesMapping(
876 props
877 , AuditOperation.UPDATE
878 , event.getPersister()
879 , event.getEntity().getClass());
880 for (int i=0; i<mappings.length; ++i){
881 int entityPos = mappings[i].positionInPersistor;
882 if (!ObjectUtils.equals(
883 event.getState()[entityPos]
884 , event.getOldState()[entityPos]))
885 {
886 String value =
887 (String)converter.convert(
888 String.class
889 , event.getState()[entityPos]
890 , mappings[i].pattern);
891 PropertyModification propMod =
892 new PropertyModification(
893 objModType
894 , mappings[i].propertyName
895 , value);
896 auditObjectsToSave.get().add(propMod);
897 }
898 }
899 }
900 return true;
901 }catch(Exception e){
902 throw new AuditRuntimeException(
903 "Error while processing audit operation", e);
904 }
905 }
906
907
908 private ObjectModificationType getObjectModificationType(
909 AuditOperation operation, Object entity) throws Exception
910 {
911 Auditable audObj = (Auditable)entity;
912 ObjectModificationPk pk =
913 new ObjectModificationPk(
914 audObj.getId(), audObj.getClass().getName());
915 ObjectModification objMod =
916 (ObjectModification)load(ObjectModification.class, pk);
917 boolean newObjMod=objMod==null;
918 if (objMod==null){
919 objMod = new ObjectModification(pk);
920 auditObjectsToSave.get().add(objMod);
921 }
922 ObjectModificationType objModType =
923 new ObjectModificationType(objMod, operation);
924 auditObjectsToSave.get().add(objModType);
925 return objModType;
926 }
927
928 private synchronized PropertyMapping[] getPropertiesMapping(
929 Set<String> props
930 , AuditOperation operation
931 , EntityPersister persister
932 , Class entityClass)
933 throws Exception
934 {
935 if (propertyMappings==null)
936 propertyMappings = new HashMap<String, PropertyMapping[]>();
937 String id = entityClass.getName()+"."+operation;
938 PropertyMapping[] mappings = propertyMappings.get(id);
939 if (mappings==null){
940 int i=0;
941 mappings = new PropertyMapping[props.size()];
942 String[] entityPropertyNames = persister.getPropertyNames();
943 for (String propertyName: props)
944 for (int j=0; j<entityPropertyNames.length; ++j)
945 if (propertyName.equals(entityPropertyNames[j])){
946 PropertyMapping mapping = new PropertyMapping();
947 mapping.propertyName=propertyName;
948 mapping.positionInPersistor=j;
949 mapping.pattern=
950 descriptorRegistry.getPropertyDescriptor(
951 entityClass, propertyName).getPattern();
952 mappings[i++]=mapping;
953 break;
954 }
955 propertyMappings.put(id, mappings);
956 }
957 return mappings;
958 }
959
960 public void setAuditReg(AuditableObjectRegistry auditReg) {
961 this.auditReg = auditReg;
962 }
963
964 public void setConverter(ValueTypeConverter converter) {
965 this.converter = converter;
966 }
967
968 public void setDescriptorRegistry(ObjectDescriptorRegistry descriptorRegistry) {
969 this.descriptorRegistry = descriptorRegistry;
970 }
971
972 private static class ParameterNameConstructor {
973 private Set<String> reservedNames;
974 private int parameterNumber = 1;
975
976 public ParameterNameConstructor(Set<String> reservedNames){
977 this.reservedNames = reservedNames;
978 }
979
980 public String getNextName(){
981 String nextName=null;
982 do{
983 nextName = PARAM_PREFIX+(parameterNumber++);
984 }while (reservedNames.contains(nextName));
985 return nextName;
986 }
987 }
988
989 private static class PropertyMapping {
990 public String propertyName;
991 public int positionInPersistor;
992 public String pattern;
993 }
994
995 }