cleanup
[cdmlib.git] / cdmlib-persistence / src / main / java / eu / etaxonomy / cdm / persistence / hibernate / TaxonGraphHibernateListener.java
index 7420285e910c8f28497fb0f92f6d8be5b802b7c9..1be88c30206c3c8fe974bf1f63100867a41f5562 100644 (file)
 */
 package eu.etaxonomy.cdm.persistence.hibernate;
 
-import org.apache.commons.lang.ArrayUtils;
-import org.apache.log4j.Logger;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.hibernate.action.spi.BeforeTransactionCompletionProcess;
 import org.hibernate.event.spi.PostInsertEvent;
-import org.hibernate.event.spi.PostInsertEventListener;
 import org.hibernate.event.spi.PostUpdateEvent;
-import org.hibernate.event.spi.PostUpdateEventListener;
+import org.hibernate.event.spi.PreDeleteEvent;
 import org.hibernate.persister.entity.EntityPersister;
 
+import eu.etaxonomy.cdm.config.CdmHibernateListenerConfiguration;
+import eu.etaxonomy.cdm.model.name.NomenclaturalSource;
 import eu.etaxonomy.cdm.model.name.TaxonName;
-import eu.etaxonomy.cdm.model.reference.Reference;
-import eu.etaxonomy.cdm.persistence.dao.taxonGraph.ITaxonGraphDao;
-import eu.etaxonomy.cdm.persistence.dao.taxonGraph.TaxonGraphException;
 
 /**
+ * The {@code TaxonGraphHibernateListener} it the implementation of the
+ * according interface. The listener in initially empty and thus will do nothing
+ * unless configured with the {@link TaxonGraphBeforeTransactionCompleteProces}
+ *
+ * @see <a href=
+ *      "https://dev.e-taxonomy.eu/redmine/issues/7648">https://dev.e-taxonomy.eu/redmine/issues/7648</a>
+ * @see {@link TaxonGraphBeforeTransactionCompleteProcess}
+ * @see {@link CdmHibernateListenerConfiguration}
+ *
  * @author a.kohlbecker
  * @since Sep 27, 2018
- *
  */
-public class TaxonGraphHibernateListener implements PostInsertEventListener, PostUpdateEventListener {
+public class TaxonGraphHibernateListener implements ITaxonGraphHibernateListener {
 
-    private static final long serialVersionUID = 5062518307839173935L;
+    private static final Logger logger = LogManager.getLogger(TaxonGraphHibernateListener.class);
 
-    private static TaxonGraphHibernateListener instance;
+    private static final long serialVersionUID = 5062518307839173935L;
 
-    private ITaxonGraphDao taxonGraphDao;
+    private Map<Class<? extends BeforeTransactionCompletionProcess>, ProcessConstructorData<? extends BeforeTransactionCompletionProcess>> beforeTransactionCompletionProcessTypes = new HashMap<>();
 
-    public void setTaxonGraphDao(ITaxonGraphDao taxonGraphDao){
-        this.taxonGraphDao = taxonGraphDao;
+    @Override
+    public void registerProcessClass(Class<? extends BeforeTransactionCompletionProcess> processClass, Object[] constructorArgs, Class<?>[] paramterTypes) throws NoSuchMethodException, SecurityException{
+        if(constructorArgs == null){
+            constructorArgs = new Object[]{};
+        }
+       beforeTransactionCompletionProcessTypes.put(processClass, new ProcessConstructorData(processClass, constructorArgs, paramterTypes));
     }
 
-    private String[] NAMEPARTS_OR_RANK_PROPS = new String[]{"genusOrUninomial", "specificEpithet", "rank"};
-    private String[] NOMREF_PROP = new String[]{"nomenclaturalReference"};
-
-
-    private int checkStateChange(PostUpdateEvent event, String[] propertyNamesToCheck){
+    @Override
+    public void unRegisterProcessClass(Class<? extends BeforeTransactionCompletionProcess> processClass){
+        beforeTransactionCompletionProcessTypes.remove(processClass);
+    }
 
-        String[] propertyNames = event.getPersister().getPropertyNames();
-        Object[] oldState = event.getOldState();
-        Object[] state = event.getState();
+    @Override
+    public void onPostUpdate(PostUpdateEvent event) {
 
-        int propsCheckedCnt = 0;
-        for(int i = 0; i < propertyNames.length; i++){
-            if(ArrayUtils.contains(propertyNamesToCheck, propertyNames[i])){
-                propsCheckedCnt++;
-                if(!oldState[i].equals(state[i])){
-                    return i;
-                }
-                if(propsCheckedCnt == propertyNamesToCheck.length){
-                    return -1;
+        if(event.getEntity() instanceof TaxonName || event.getEntity() instanceof NomenclaturalSource){
+            for(Class<? extends BeforeTransactionCompletionProcess> type : beforeTransactionCompletionProcessTypes.keySet()){
+                try {
+                    ProcessConstructorData<? extends BeforeTransactionCompletionProcess> pcd = beforeTransactionCompletionProcessTypes.get(type);
+                    BeforeTransactionCompletionProcess processorInstance = pcd.postUpdateEventConstructor.newInstance(pcd.buildConstructorArgs(event));
+                    event.getSession().getActionQueue().registerProcess(processorInstance);
+                } catch (InstantiationException | IllegalAccessException | IllegalArgumentException
+                        | InvocationTargetException | SecurityException e) {
+                    logger.error("Error creating new instance of " + type.toString(), e);
                 }
             }
         }
-        // this execption should be raised during the unit tests already and thus will never occur in production
-        throw new RuntimeException("TaxonName class misses at least one property of: " + ArrayUtils.toString(propertyNamesToCheck));
     }
 
     @Override
-    public void onPostUpdate(PostUpdateEvent event) {
-        if(taxonGraphDao == null){
-            return;
-        }
-        try {
-            if(event.getEntity() instanceof TaxonName){
-                if(checkStateChange(event, NAMEPARTS_OR_RANK_PROPS) > -1){
-                    taxonGraphDao.onNameOrRankChange((TaxonName) event.getEntity());
-                }
-                int changedNomRefIndex = checkStateChange(event, NOMREF_PROP);
-                if(changedNomRefIndex > -1){
-                    taxonGraphDao.onNomReferenceChange((TaxonName) event.getEntity(), (Reference)event.getOldState()[changedNomRefIndex]);
+    public void onPostInsert(PostInsertEvent event) {
+
+        if(event.getEntity() instanceof TaxonName || event.getEntity() instanceof NomenclaturalSource){
+            for(Class<? extends BeforeTransactionCompletionProcess> type : beforeTransactionCompletionProcessTypes.keySet()){
+                try {
+                    ProcessConstructorData<? extends BeforeTransactionCompletionProcess> pcd = beforeTransactionCompletionProcessTypes.get(type);
+                    BeforeTransactionCompletionProcess processorInstance = pcd.postInsertEventConstructor.newInstance(pcd.buildConstructorArgs(event));
+                    event.getSession().getActionQueue().registerProcess(processorInstance);
+                } catch (InstantiationException | IllegalAccessException | IllegalArgumentException
+                        | InvocationTargetException | SecurityException e) {
+                    logger.error("Error creating new instance of " + type.toString(), e);
                 }
             }
-        } catch (TaxonGraphException e) {
-            Logger.getLogger(this.getClass()).error(e);
         }
-
     }
 
     @Override
-    public void onPostInsert(PostInsertEvent event) {
-        if(taxonGraphDao == null){
-            return;
-        }
-        try {
-            if(event.getEntity() instanceof TaxonName){
-                taxonGraphDao.onNewTaxonName((TaxonName) event.getEntity());
+    public boolean onPreDelete(PreDeleteEvent event) {
+
+        if(event.getEntity() instanceof TaxonName || event.getEntity() instanceof NomenclaturalSource){
+            for(Class<? extends BeforeTransactionCompletionProcess> type : beforeTransactionCompletionProcessTypes.keySet()){
+                try {
+                    ProcessConstructorData<? extends BeforeTransactionCompletionProcess> pcd = beforeTransactionCompletionProcessTypes.get(type);
+                    BeforeTransactionCompletionProcess processorInstance = pcd.preDeleteEventConstructor.newInstance(pcd.buildConstructorArgs(event));
+                    event.getSession().getActionQueue().registerProcess(processorInstance);
+                } catch (InstantiationException | IllegalAccessException | IllegalArgumentException
+                        | InvocationTargetException | SecurityException e) {
+                    logger.error("Error creating new instance of " + type.toString(), e);
+                }
             }
-        } catch (TaxonGraphException e) {
-            Logger.getLogger(this.getClass()).error(e);
         }
+        return false; // no veto
     }
 
     @Override
@@ -104,14 +116,43 @@ public class TaxonGraphHibernateListener implements PostInsertEventListener, Pos
         return true;
     }
 
-    /**
-     * @return
-     */
-    public static TaxonGraphHibernateListener instance() {
-        if(instance == null){
-            instance = new TaxonGraphHibernateListener();
+    class ProcessConstructorData<T extends BeforeTransactionCompletionProcess> {
+
+        Constructor<T> postInsertEventConstructor;
+        Constructor<T> postUpdateEventConstructor;
+        Constructor<T> preDeleteEventConstructor;
+        Object[] constructorArgs;
+
+        public ProcessConstructorData(Class<T> type, Object[] constructorArgs, Class<?>[] paramterTypes) throws NoSuchMethodException, SecurityException {
+
+            this.constructorArgs = constructorArgs;
+
+            Class<?>[] postInsertEventConstructorArgTypes = new Class<?>[constructorArgs.length + 1];
+            Class<?>[] postUpdateEventConstructorArgTypes = new Class<?>[constructorArgs.length + 1];
+            Class<?>[] preDeleteEventConstructorArgTypes = new Class<?>[constructorArgs.length + 1];
+            postInsertEventConstructorArgTypes[0] = PostInsertEvent.class;
+            postUpdateEventConstructorArgTypes[0] = PostUpdateEvent.class;
+            preDeleteEventConstructorArgTypes[0] = PreDeleteEvent.class;
+            int i = 1;
+            for(Class<?> ptype : paramterTypes){
+                postInsertEventConstructorArgTypes[i] = ptype;
+                postUpdateEventConstructorArgTypes[i] = ptype;
+                preDeleteEventConstructorArgTypes[i] = ptype;
+                i++;
+            }
+            postInsertEventConstructor = type.getConstructor(postInsertEventConstructorArgTypes);
+            postUpdateEventConstructor = type.getConstructor(postUpdateEventConstructorArgTypes);
+            preDeleteEventConstructor = type.getConstructor(preDeleteEventConstructorArgTypes);
         }
-        return instance;
-    }
 
+        public Object[] buildConstructorArgs(Object firstArg){
+            Object[] cargs = new Object[constructorArgs.length + 1];
+            cargs[0] = firstArg;
+            int i = 1;
+            for(Object arg : constructorArgs){
+                cargs[i++] = arg;
+            }
+            return cargs;
+        }
+    }
 }