cleanup
[cdm-vaadin.git] / src / main / java / eu / etaxonomy / cdm / service / CdmStore.java
index 460b3e00c933d6d90eeed6c911d1a2fd08335f1e..06e2e2cc896ddbdd07dece5758e2afcfda37fbb4 100644 (file)
@@ -8,22 +8,33 @@
 */
 package eu.etaxonomy.cdm.service;
 
-import org.apache.log4j.Logger;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
 import org.hibernate.Session;
-import org.hibernate.engine.spi.SessionImplementor;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Qualifier;
 import org.springframework.transaction.TransactionStatus;
-import org.springframework.transaction.support.DefaultTransactionDefinition;
 
+import com.vaadin.spring.annotation.SpringComponent;
+import com.vaadin.spring.annotation.ViewScope;
 import com.vaadin.ui.Notification;
 import com.vaadin.ui.UI;
 
 import eu.etaxonomy.cdm.api.application.CdmRepository;
 import eu.etaxonomy.cdm.api.service.DeleteResult;
 import eu.etaxonomy.cdm.api.service.IService;
+import eu.etaxonomy.cdm.model.agent.AgentBase;
 import eu.etaxonomy.cdm.model.common.CdmBase;
 import eu.etaxonomy.cdm.model.common.IdentifiableEntity;
+import eu.etaxonomy.cdm.model.name.NameTypeDesignation;
+import eu.etaxonomy.cdm.model.name.Registration;
+import eu.etaxonomy.cdm.model.name.TaxonName;
+import eu.etaxonomy.cdm.model.occurrence.Collection;
+import eu.etaxonomy.cdm.model.occurrence.SpecimenOrObservationBase;
+import eu.etaxonomy.cdm.model.reference.Reference;
 import eu.etaxonomy.cdm.vaadin.event.EntityChangeEvent;
 import eu.etaxonomy.cdm.vaadin.event.EntityChangeEvent.Type;
+import eu.etaxonomy.vaadin.mvp.AbstractView;
 
 /**
  * @author a.kohlbecker
@@ -32,143 +43,16 @@ import eu.etaxonomy.cdm.vaadin.event.EntityChangeEvent.Type;
  * TODO better naming of this class, ServiceWrapper, ServiceOperator, ...?
  *
  */
-public class CdmStore<T extends CdmBase, S extends IService<T>> {
+@SpringComponent
+@ViewScope
+public class CdmStore {
 
-    private static final Logger logger = Logger.getLogger(CdmStore.class);
+    private static final Logger logger = LogManager.getLogger();
 
+    @Autowired
+    @Qualifier("cdmRepository")
     private CdmRepository repo;
 
-    private S service;
-
-    TransactionStatus txStatus = null;
-
-//    ConversationHolder conversationHolder = null;
-//
-//    /**
-//     * @return the conversationHolder
-//     */
-//    public ConversationHolder getConversationHolder() {
-//        return conversationHolder;
-//    }
-
-    protected DefaultTransactionDefinition txDefinition = null;
-
-    /**
-     *
-     * @param repo
-     * @param service
-     *            may be <code>null</code>, but delete operations will fail with
-     *            a NullPointerException in this case.
-     */
-    public CdmStore(CdmRepository repo, S service) {
-
-        this.repo = repo;
-        this.service = service;
-
-    }
-
-//    /**
-//     * constructor which takes a ConversationHolder. The supplying class of the conversationHolder needs
-//     * to care for <code>bind()</code>, <code>unbind()</code> and <code>close()</code> since the store is
-//     * only responsible for starting and committing of transactions.
-//     *
-//     * @param repo
-//     * @param service
-//     * @param conversationHolder
-//     */
-//    public CdmStore(CdmRepository repo, S service, ConversationHolder conversationHolder) {
-//
-//        this.repo = repo;
-//        this.service = service;
-//        this.conversationHolder = conversationHolder;
-//
-//    }
-
-    /**
-     * @return
-     *
-     */
-    public TransactionStatus startTransaction() {
-//        if(conversationHolder != null && !conversationHolder.isTransactionActive()){
-//            //conversationHolder.setDefinition(getTransactionDefinition());
-//            return conversationHolder.startTransaction();
-//        } else {
-            checkExistingTransaction();
-            txStatus = repo.startTransaction();
-            return txStatus;
-//        }
-    }
-
-    /**
-     *
-     */
-    protected void checkExistingTransaction() {
-        if (txStatus != null) {
-            // @formatter:off
-            // holding the TransactionStatus as state is not good design. we
-            // should change the save operation
-            // in the EditorView so that the presenter can process the save in
-            // one method call.
-            // Problems:
-            // 1. the fieldGroup needs a open session and read transaction
-            // during the validation, otherwise
-            // LazyInitialisationExceptions occur.
-            // 2. passing the TransactionState to the view also doesn't seem
-            // like a good idea.
-            // @formatter:on
-            throw new RuntimeException("Opening a second transaction in the same" + this.getClass().getSimpleName() + " is not supported");
-        }
-    }
-
-    /**
-     * If the bean is contained in the session it is being updated by doing an
-     * evict and merge. The fieldGroup is updated with the merged bean.
-     *
-     *
-     * @param bean
-     * @return The bean merged to the session or original bean in case a merge
-     *         was not necessary.
-     */
-    public T mergedBean(T bean) {
-
-        Session session = getSession();
-
-        // session.clear();
-        if (session.contains(bean)) {
-            // evict bean before merge to avoid duplicate beans in same session
-            logger.trace(this._toString() + ".mergedBean() - evict " + bean.toString());
-            session.evict(bean);
-        }
-
-        logger.trace(this._toString() + ".mergedBean() - doing merge of" + bean.toString());
-        // to avoid merge problems as described in
-        // https://dev.e-taxonomy.eu/redmine/issues/6687
-        // we are set the hibernate property
-        // hibernate.event.merge.entity_copy_observer=allow
-        @SuppressWarnings("unchecked")
-        T mergedBean = (T) session.merge(bean);
-        logger.trace(this._toString() + ".mergedBean() - bean after merge " + bean.toString());
-        return mergedBean;
-
-    }
-
-    /**
-     * @return
-     */
-    private Session getSession() {
-
-        Session session;
-//        if(conversationHolder != null){
-//            session = conversationHolder.getSession();
-//        } else {
-            session = repo.getSession();
-//        }
-        logger.trace(this._toString() + ".getSession() - session:" + session.hashCode() + ", persistenceContext: "
-                + ((SessionImplementor) session).getPersistenceContext() + " - " + session.toString());
-
-        return session;
-    }
-
     protected String _toString() {
         return this.getClass().getSimpleName() + "@" + this.hashCode();
     }
@@ -176,40 +60,53 @@ public class CdmStore<T extends CdmBase, S extends IService<T>> {
     /**
      *
      * @param bean
-
+     *
      * @return the merged bean, this bean is <b>not reloaded</b> from the
      *         persistent storage.
      */
-    public EntityChangeEvent saveBean(T bean) {
+    public <T extends CdmBase> EntityChangeEvent saveBean(T bean, AbstractView view) {
 
         Type changeEventType;
-        if(bean.getId() > 1){
+        if(bean.isPersited()){
             changeEventType = Type.MODIFIED;
         } else {
             changeEventType = Type.CREATED;
         }
 
-        Session session = getSession();
-        logger.trace(this._toString() + ".onEditorSaveEvent - session: " + session.hashCode());
-
-        if(txStatus == null
-//                || (conversationHolder != null && !conversationHolder.isTransactionActive())
-                ){
-            // no running transaction, start one ...
-            startTransaction();
+        try{
+            TransactionStatus txStatus = repo.startTransaction();
+            Session session = repo.getSession();
+            try {
+                logger.trace(this._toString() + ".onEditorSaveEvent - merging bean into session");
+                // merge the changes into the session, ...
+                if (session.contains(bean)) {
+                    // evict bean before merge to avoid duplicate beans in same session
+                    logger.trace(this._toString() + ".mergedBean() - evict " + bean.toString());
+                    session.evict(bean);
+                }
+                logger.trace(this._toString() + ".mergedBean() - doing merge of" + bean.toString());
+                @SuppressWarnings("unchecked")
+                T mergedBean = (T) session.merge(bean);
+                repo.commitTransaction(txStatus);
+                return new EntityChangeEvent(mergedBean, changeEventType, view);
+            } catch(Exception e){
+                transactionRollbackIfNotCompleted(txStatus);
+                throw e;
+            }
+        } finally {
+            repo.clearSession(); // #7559
         }
 
-        logger.trace(this._toString() + ".onEditorSaveEvent - merging bean into session");
-        // merge the changes into the session, ...
+    }
 
-        T mergedBean = mergedBean(bean);
-        //T mergedBean = bean;
-        // NOTE: saveOrUpdate is really needed here even if we to a merge before
-        repo.getCommonService().saveOrUpdate(mergedBean);
-        session.flush();
-        commitTransction();
 
-        return new EntityChangeEvent(mergedBean.getClass(), mergedBean.getId(), changeEventType);
+    /**
+     * @param txStatus
+     */
+    public void transactionRollbackIfNotCompleted(TransactionStatus txStatus) {
+        if(!txStatus.isCompleted()){
+            repo.getTransactionManager().rollback(txStatus);
+        }
     }
 
     /**
@@ -217,76 +114,83 @@ public class CdmStore<T extends CdmBase, S extends IService<T>> {
      * @param bean
      * @return a EntityChangeEvent in case the deletion was successful otherwise <code>null</code>.
      */
-    public final EntityChangeEvent deleteBean(T bean) {
-
-        logger.trace(this._toString() + ".onEditorPreSaveEvent - starting transaction");
+    public final <T extends CdmBase> EntityChangeEvent deleteBean(T bean, AbstractView view) {
 
-        startTransaction();
-        logger.trace(this._toString() + ".deleteBean - deleting" + bean.toString());
-        DeleteResult result = service.delete(bean);
-        if (result.isOk()) {
+        IService<T> typeSpecificService = serviceFor(bean);
 
-            getSession().flush();
-            commitTransction();
-            logger.trace(this._toString() + ".deleteBean - transaction comitted");
-            return new EntityChangeEvent(bean.getClass(), bean.getId(), Type.REMOVED);
-        } else {
-            String notificationTitle;
-            StringBuffer messageBody = new StringBuffer();
-            if (result.isAbort()) {
-                notificationTitle = "The delete operation as abborded by the system.";
+        try{
+            logger.trace(this._toString() + ".deleteBean - deleting" + bean.toString());
+            DeleteResult result = typeSpecificService.delete(bean);
+            if (result.isOk()) {
+                return new EntityChangeEvent(bean, Type.REMOVED, view);
             } else {
-                notificationTitle = "An error occured during the delete operation.";
-            }
-            if (!result.getExceptions().isEmpty()) {
-                messageBody.append("<h3>").append("Exceptions:").append("</h3>").append("<ul>");
-                result.getExceptions().forEach(e -> messageBody.append("<li>").append(e.getMessage()).append("</li>"));
-                messageBody.append("</ul>");
+                handleDeleteresultInError(result);
             }
-            if (!result.getRelatedObjects().isEmpty()) {
-                messageBody.append("<h3>").append("Related objects exist:").append("</h3>").append("<ul>");
-                result.getRelatedObjects().forEach(e -> {
-                    messageBody.append("<li>");
-                    if (IdentifiableEntity.class.isAssignableFrom(e.getClass())) {
-                        messageBody.append(((IdentifiableEntity) e).getTitleCache());
-                    } else {
-                        messageBody.append(e.toString());
-                    }
-                    messageBody.append("</li>");
-                });
-
-                messageBody.append("</ul>");
-            }
-            Notification notification = new Notification(notificationTitle, messageBody.toString(),
-                    com.vaadin.ui.Notification.Type.ERROR_MESSAGE, true);
-            notification.show(UI.getCurrent().getPage());
-            txStatus = null;
+        } finally {
+            repo.clearSession(); // #7559
         }
-        return null;
-    }
 
-
-    protected void commitTransction() {
-
-//        if(conversationHolder != null){
-//            conversationHolder.commit();
-//        } else {
-            repo.commitTransaction(txStatus);
-            txStatus = null;
-//        }
+        return null;
     }
 
     /**
-     * @param entityId
+     * @param result
      */
-    public T loadBean(int entityId) {
-//        conversationHolder.startTransaction();
-        return service.find(entityId);
+    public static void handleDeleteresultInError(DeleteResult result) {
+        String notificationTitle;
+        StringBuffer messageBody = new StringBuffer();
+        if (result.isAbort()) {
+            notificationTitle = "The delete operation as abborded by the system.";
+        } else {
+            notificationTitle = "An error occured during the delete operation.";
+        }
+        if (!result.getExceptions().isEmpty()) {
+            messageBody.append("<h3>").append("Exceptions:").append("</h3>").append("<ul>");
+            result.getExceptions().forEach(e -> messageBody.append("<li>").append(e.getMessage()).append("</li>"));
+            messageBody.append("</ul>");
+        }
+        if (!result.getRelatedObjects().isEmpty()) {
+            messageBody.append("<h3>").append("Related objects exist:").append("</h3>").append("<ul>");
+            result.getRelatedObjects().forEach(e -> {
+                messageBody.append("<li>");
+                if (IdentifiableEntity.class.isAssignableFrom(e.getClass())) {
+                    messageBody.append(((IdentifiableEntity) e).getTitleCache());
+                } else {
+                    messageBody.append(e.toString());
+                }
+                messageBody.append("</li>");
+            });
+
+            messageBody.append("</ul>");
+        }
+        Notification notification = new Notification(notificationTitle, messageBody.toString(),
+                com.vaadin.ui.Notification.Type.ERROR_MESSAGE, true);
+        notification.show(UI.getCurrent().getPage());
     }
 
-    public S getService() {
-        return service;
+    @SuppressWarnings("unchecked")
+    protected <T extends CdmBase> IService<T> serviceFor(T bean){
+         Class<? extends CdmBase> cdmType = bean.getClass();
+
+         if(Registration.class.isAssignableFrom(cdmType)){
+             return (IService<T>) repo.getRegistrationService();
+         } else if(TaxonName.class.isAssignableFrom(cdmType)){
+             return (IService<T>) repo.getNameService();
+         } else if(Reference.class.isAssignableFrom(cdmType)){
+             return (IService<T>) repo.getReferenceService();
+         } else if (NameTypeDesignation.class.isAssignableFrom(cdmType)){
+             throw new RuntimeException("no generic sercvice for NameTypeDesignation, use dedicated methods of NameService");
+         } else if (SpecimenOrObservationBase.class.isAssignableFrom(cdmType)){
+             return (IService<T>) repo.getOccurrenceService();
+         } else if (AgentBase.class.isAssignableFrom(cdmType)){
+             return (IService<T>) repo.getAgentService();
+         } else if (Collection.class.isAssignableFrom(cdmType)){
+             return (IService<T>) repo.getCollectionService();
+         } else if (Collection.class.isAssignableFrom(cdmType)){
+             return (IService<T>) repo.getCollectionService();
+         } else {
+             throw new RuntimeException("Implementation to find service for " + cdmType + " still missing.");
+         }
     }
 
-
 }