*/
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
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;
+ }
+ }
}