*/
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;
* 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();
}
* @return the merged bean, this bean is <b>not reloaded</b> from the
* persistent storage.
*/
- public EntityChangeEvent saveBean(T bean, AbstractView view) {
+ 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);
+ }
- // NOTE: saveOrUpdate is really needed here even if we to a merge before
- // repo.getCommonService().saveOrUpdate(mergedBean);
- session.flush();
- commitTransction();
- return new EntityChangeEvent(mergedBean, changeEventType, view);
+ /**
+ * @param txStatus
+ */
+ public void transactionRollbackIfNotCompleted(TransactionStatus txStatus) {
+ if(!txStatus.isCompleted()){
+ repo.getTransactionManager().rollback(txStatus);
+ }
}
/**
* @param bean
* @return a EntityChangeEvent in case the deletion was successful otherwise <code>null</code>.
*/
- public final EntityChangeEvent deleteBean(T bean, AbstractView view) {
-
- logger.trace(this._toString() + ".onEditorPreSaveEvent - starting transaction");
-
- startTransaction();
- logger.trace(this._toString() + ".deleteBean - deleting" + bean.toString());
- DeleteResult result = service.delete(bean);
- if (result.isOk()) {
- getSession().flush();
- commitTransction();
- logger.trace(this._toString() + ".deleteBean - transaction comitted");
- return new EntityChangeEvent(bean, Type.REMOVED, view);
- } else {
- handleDeleteresultInError(result);
- txStatus = null;
+ public final <T extends CdmBase> EntityChangeEvent deleteBean(T bean, AbstractView view) {
+
+ IService<T> typeSpecificService = serviceFor(bean);
+
+ 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 {
+ handleDeleteresultInError(result);
+ }
+ } finally {
+ repo.clearSession(); // #7559
}
+
return null;
}
notification.show(UI.getCurrent().getPage());
}
-
- protected void commitTransction() {
-
-// if(conversationHolder != null){
-// conversationHolder.commit();
-// } else {
- repo.commitTransaction(txStatus);
- txStatus = null;
-// }
+ @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.");
+ }
}
- /**
- * @param entityId
- */
- public T loadBean(int entityId) {
-// conversationHolder.startTransaction();
- return service.find(entityId);
- }
-
- public S getService() {
- return service;
- }
-
-
}