From af045033b094cfeb778b6658d8d0880632420cae Mon Sep 17 00:00:00 2001 From: srosse <none@none> Date: Tue, 28 May 2013 16:28:35 +0200 Subject: [PATCH] no-jira: hardened publisher select for update (at the cost of a query), better rollback suppport in DBImpl (possible lost rollback in a hibernate session) --- .../olat/core/commons/persistence/DBImpl.java | 66 ++++++++++--------- .../NotificationsManagerImpl.java | 52 +++++++-------- 2 files changed, 57 insertions(+), 61 deletions(-) diff --git a/src/main/java/org/olat/core/commons/persistence/DBImpl.java b/src/main/java/org/olat/core/commons/persistence/DBImpl.java index e840fbbe9f0..34b80dd853a 100644 --- a/src/main/java/org/olat/core/commons/persistence/DBImpl.java +++ b/src/main/java/org/olat/core/commons/persistence/DBImpl.java @@ -36,6 +36,7 @@ import java.util.Map; import javax.persistence.EntityManager; import javax.persistence.EntityManagerFactory; import javax.persistence.EntityTransaction; +import javax.persistence.RollbackException; import org.hibernate.HibernateException; import org.hibernate.Query; @@ -275,27 +276,7 @@ public class DBImpl extends LogDelegator implements DB, Destroyable { logWarn("beginTransaction bulk-change, too many db access for one transaction, could be a performance problem (add closeSession/createSession in loop) logObject=" + logObject, null); getData().resetAccessCounter(); } - - EntityTransaction transaction = em.getTransaction(); - if(transaction == null) { - logError("Call beginTransaction but transaction is null", null); - } else if (transaction.isActive() && transaction.getRollbackOnly()) { - logError("Call beginTransaction but transaction is already rollbacked", null); - } else if (!transaction.isActive()) { - logError("Call beginTransaction but transaction is already completed", null); - } } - /* - private void createSession2() { - ThreadLocalData data = getData(); - if (data == null) { - if (isLogDebugEnabled()) logDebug("createSession start...", null); - data.resetAccessCounter(); - data.resetCommitCounter(); - } - setInitialized(true); - if (isLogDebugEnabled()) logDebug("createSession end...", null); - }*/ /** * Close the database session. @@ -317,15 +298,14 @@ public class DBImpl extends LogDelegator implements DB, Destroyable { if(s != null) { EntityTransaction trx = s.getTransaction(); if(trx.isActive()) { - if(trx.getRollbackOnly()) { + try { + trx.commit(); + } catch (RollbackException ex) { + //possible if trx setRollbackonly + logWarn("Close session with transaction set with setRollbackOnly", ex); + } catch (Exception e) { + logError("", e); trx.rollback(); - } else { - try { - trx.commit(); - } catch (Exception e) { - logError("", e); - trx.rollback(); - } } } @@ -778,10 +758,16 @@ public class DBImpl extends LogDelegator implements DB, Destroyable { EntityTransaction trx = getCurrentEntityManager().getTransaction(); if(trx != null) { + logWarn("Commit commit", new Exception()); trx.commit(); } if (isLogDebugEnabled()) logDebug("Commit DONE hasTransaction()=" + hasTransaction(), null); + } else if(hasTransaction() && isError()) { + EntityTransaction trx = getCurrentEntityManager().getTransaction(); + if(trx != null && trx.isActive()) { + throw new DBRuntimeException("Try to commit a transaction in error status"); + } } else { if (isLogDebugEnabled()) logDebug("Call commit without starting transaction", null ); } @@ -800,8 +786,16 @@ public class DBImpl extends LogDelegator implements DB, Destroyable { txManager.rollback(status); EntityTransaction trx = getCurrentEntityManager().getTransaction(); - if(trx != null) { - trx.rollback(); + if(trx != null && trx.isActive()) { + if(trx.getRollbackOnly()) { + try { + trx.commit(); + } catch (RollbackException e1) { + //we wait for this exception + } + } else { + trx.rollback(); + } } } } catch (Error er) { @@ -829,8 +823,16 @@ public class DBImpl extends LogDelegator implements DB, Destroyable { txManager.rollback(status); EntityTransaction trx = getCurrentEntityManager().getTransaction(); - if(trx != null) { - trx.rollback(); + if(trx != null && trx.isActive()) { + if(trx.getRollbackOnly()) { + try { + trx.commit(); + } catch (RollbackException e) { + //we wait for this exception + } + } else { + trx.rollback(); + } } } catch (Exception ex) { diff --git a/src/main/java/org/olat/notifications/NotificationsManagerImpl.java b/src/main/java/org/olat/notifications/NotificationsManagerImpl.java index 983bfd843e3..dbc6286cb50 100644 --- a/src/main/java/org/olat/notifications/NotificationsManagerImpl.java +++ b/src/main/java/org/olat/notifications/NotificationsManagerImpl.java @@ -591,30 +591,10 @@ public class NotificationsManagerImpl extends NotificationsManager implements Us */ @Override public Publisher getPublisher(SubscriptionContext subsContext) { - return getPublisher(subsContext.getResName(), subsContext.getResId(), subsContext.getSubidentifier(), false); - } - - private Publisher getPublisherForUpdate(SubscriptionContext subsContext) { - return getPublisher(subsContext.getResName(), subsContext.getResId(), subsContext.getSubidentifier(), true); - } - - @Override - public List<Publisher> getAllPublisher() { - String q = "select pub from org.olat.notifications.PublisherImpl pub"; - return DBFactory.getInstance().getCurrentEntityManager().createQuery(q, Publisher.class) - .getResultList(); - } - - /** - * return the publisher for the given composite primary key ores + - * subidentifier. - */ - private Publisher getPublisher(String resName, Long resId, String subidentifier, boolean forUpdate) { - StringBuilder q = new StringBuilder(); q.append("select pub from ").append(PublisherImpl.class.getName()).append(" pub ") .append(" where pub.resName=:resName and pub.resId = :resId"); - if(StringHelper.containsNonWhitespace(subidentifier)) { + if(StringHelper.containsNonWhitespace(subsContext.getSubidentifier())) { q.append(" and pub.subidentifier=:subidentifier"); } else { q.append(" and (pub.subidentifier='' or pub.subidentifier is null)"); @@ -622,20 +602,34 @@ public class NotificationsManagerImpl extends NotificationsManager implements Us TypedQuery<Publisher> query = DBFactory.getInstance().getCurrentEntityManager() .createQuery(q.toString(), Publisher.class) - .setParameter("resName", resName) - .setParameter("resId", resId.longValue()); + .setParameter("resName", subsContext.getResName()) + .setParameter("resId", subsContext.getResId()); - if(StringHelper.containsNonWhitespace(subidentifier)) { - query.setParameter("subidentifier", subidentifier); - } - if(forUpdate) { - query.setLockMode(LockModeType.PESSIMISTIC_WRITE); + if(StringHelper.containsNonWhitespace(subsContext.getSubidentifier())) { + query.setParameter("subidentifier", subsContext.getSubidentifier()); } List<Publisher> res = query.getResultList(); if (res.isEmpty()) return null; if (res.size() != 1) throw new AssertException("only one subscriber per person and publisher!!"); return res.get(0); } + + private Publisher getPublisherForUpdate(SubscriptionContext subsContext) { + Publisher pub = getPublisher(subsContext); + if(pub != null && pub.getKey() != null) { + //prevent optimistic lock issue + DBFactory.getInstance().getCurrentEntityManager().detach(pub); + pub = DBFactory.getInstance().getCurrentEntityManager().find(PublisherImpl.class, pub.getKey(), LockModeType.PESSIMISTIC_WRITE); + } + return pub; + } + + @Override + public List<Publisher> getAllPublisher() { + String q = "select pub from org.olat.notifications.PublisherImpl pub"; + return DBFactory.getInstance().getCurrentEntityManager().createQuery(q, Publisher.class) + .getResultList(); + } /** * @param resName @@ -843,7 +837,7 @@ public class NotificationsManagerImpl extends NotificationsManager implements Us // to make sure: ignore if no subscriptionContext if (subscriptionContext == null) return; - Publisher toUpdate = getPublisher(subscriptionContext.getResName(), subscriptionContext.getResId(), subscriptionContext.getSubidentifier(), true); + Publisher toUpdate = getPublisherForUpdate(subscriptionContext); if(toUpdate == null) { return; } -- GitLab