1 package org.collectionspace.services.common.storage.jpa;
3 import javax.persistence.EntityManager;
4 import javax.persistence.EntityManagerFactory;
5 import javax.persistence.Query;
6 import javax.persistence.RollbackException;
8 import org.collectionspace.services.common.context.ServiceContext;
9 import org.collectionspace.services.common.document.InconsistentStateException;
10 import org.collectionspace.services.common.document.TransactionException;
11 import org.collectionspace.services.common.storage.TransactionContext;
13 import org.slf4j.Logger;
14 import org.slf4j.LoggerFactory;
16 @SuppressWarnings("rawtypes")
17 public class JPATransactionContext extends TransactionContext {
19 private final Logger logger = LoggerFactory.getLogger(TransactionContext.class);
21 private int transactionRefCount = 0;
22 private boolean aclTablesUpdatedFlag = false;
24 EntityManagerFactory emf;
27 public JPATransactionContext(ServiceContext ctx) {
28 emf = JpaStorageUtils.getEntityManagerFactory();
29 em = emf.createEntityManager();
33 public JPATransactionContext() {
34 emf = JpaStorageUtils.getEntityManagerFactory();
35 em = emf.createEntityManager();
38 protected EntityManagerFactory getEntityManagerFactory() {
42 protected EntityManager getEntityManager() {
47 * Set to 'true' if (and only if) a change has been made AND successfully committed to the Spring Security tables.
49 * Since we can't include Spring Security table changes and JPA changes in a single transaction, we
50 * keep track of changes to the Spring Security tables here. We'll use this flag to log a critical error if
51 * we think there is a chance the JPA tables and Spring Security tables get out of sync.
55 public void setAclTablesUpdateFlag(boolean flag) {
56 aclTablesUpdatedFlag = flag;
59 protected boolean getAclTablesUpdateFlag() {
60 return aclTablesUpdatedFlag;
64 public ServiceContext getServiceContext() {
69 public void markForRollback() {
70 if (em.getTransaction().isActive() == true) {
71 em.getTransaction().setRollbackOnly();
73 String msg = "Attemped to mark an inactive transaction for rollback.";
79 public void close() throws TransactionException {
80 if (em.getTransaction().isActive() == true && em.getTransaction().getRollbackOnly() == true) {
81 if (getAclTablesUpdateFlag() == false) {
83 // Since there were no changes committed to the Spring Security tables, we can just rollback and continue
85 em.getTransaction().rollback();
87 String msg = handleInconsistentState();
88 throw new InconsistentStateException(msg);
90 } else if (em.getTransaction().isActive() == true) {
92 close(); // NOTE: Recursive call.
93 throw new JPATransactionException("There was an active transaction. You must commit the active transaction prior to calling this close method.");
97 JpaStorageUtils.releaseEntityManagerFactory(emf);
100 private String handleInconsistentState() throws InconsistentStateException {
102 // If we've modified the Spring Tables and need to rollback this JPA transaction, we now have a potentially critical inconsistent state in the system
104 String msg = "\n#\n# CRITICAL: The Spring Security tables just became inconsistent with CollectionSpace JPA AuthN and AuthZ tables. Contact your CollectionSpace administrator immediately.\n#";
107 // Finish by rolling back the JPA transaction, closing the connection, and throwing an exception
110 em.getTransaction().rollback();
112 JpaStorageUtils.releaseEntityManagerFactory(emf);
118 synchronized public void beginTransaction() {
119 if (transactionRefCount == 0) {
120 em.getTransaction().begin();
122 transactionRefCount++;
126 public void persist(Object entity) {
131 public Object merge(Object entity) {
132 return em.merge(entity);
135 @SuppressWarnings("unchecked")
137 public Object find(Class entityClass, Object primaryKey) {
138 return em.find(entityClass, primaryKey);
141 @SuppressWarnings("unchecked")
143 public Object find(Class entityClass, String id) {
144 return em.find(entityClass, id);
148 public Query createQuery(String qlString) {
149 return em.createQuery(qlString);
153 public void remove(Object entity) {
158 public boolean isTransactionActive() {
159 return em.getTransaction().isActive();
163 public void flush() {
168 public void commitTransaction() throws TransactionException {
169 if (transactionRefCount == 0) {
170 throw new JPATransactionException("There is no active transaction to commit.");
172 if (--transactionRefCount == 0) {
173 em.getTransaction().commit();