001package co.codewizards.cloudstore.local; 002 003import static co.codewizards.cloudstore.core.util.Util.*; 004 005import java.util.concurrent.locks.Lock; 006 007import javax.jdo.PersistenceManager; 008import javax.jdo.PersistenceManagerFactory; 009import javax.jdo.Transaction; 010 011import co.codewizards.cloudstore.core.repo.local.LocalRepoManager; 012import co.codewizards.cloudstore.local.persistence.DAO; 013import co.codewizards.cloudstore.local.persistence.LocalRepository; 014import co.codewizards.cloudstore.local.persistence.LocalRepositoryDAO; 015 016public class LocalRepoTransactionImpl implements co.codewizards.cloudstore.core.repo.local.LocalRepoTransaction { 017 018 private final LocalRepoManager localRepoManager; 019 private final PersistenceManagerFactory persistenceManagerFactory; 020 private final boolean write; 021 private PersistenceManager persistenceManager; 022 private Transaction jdoTransaction; 023 private Lock lock; 024 private long localRevision = -1; 025 026 private final AutoTrackLifecycleListener autoTrackLifecycleListener = new AutoTrackLifecycleListener(this); 027 028 public LocalRepoTransactionImpl(LocalRepoManagerImpl localRepoManager, boolean write) { 029 this.localRepoManager = assertNotNull("localRepoManager", localRepoManager); 030 this.persistenceManagerFactory = assertNotNull("localRepoManager.persistenceManagerFactory", localRepoManager.getPersistenceManagerFactory()); 031 this.write = write; 032 begin(); 033 } 034 035 private synchronized void begin() { 036 if (isActive()) 037 throw new IllegalStateException("Transaction is already active!"); 038 039 if (write) 040 lock(); 041 042 persistenceManager = persistenceManagerFactory.getPersistenceManager(); 043 hookLifecycleListeners(); 044 jdoTransaction = persistenceManager.currentTransaction(); 045 jdoTransaction.begin(); 046 autoTrackLifecycleListener.onBegin(); 047 } 048 049 private void hookLifecycleListeners() { 050 persistenceManager.addInstanceLifecycleListener(autoTrackLifecycleListener, (Class[]) null); 051 } 052 053 @Override 054 public synchronized void commit() { 055 if (!isActive()) 056 throw new IllegalStateException("Transaction is not active!"); 057 058 autoTrackLifecycleListener.onCommit(); 059 persistenceManager.flush(); 060 jdoTransaction.commit(); 061 persistenceManager.close(); 062 jdoTransaction = null; 063 persistenceManager = null; 064 localRevision = -1; 065 unlock(); 066 } 067 068 @Override 069 public synchronized boolean isActive() { 070 return jdoTransaction != null && jdoTransaction.isActive(); 071 } 072 073 @Override 074 public synchronized void rollback() { 075 if (!isActive()) 076 throw new IllegalStateException("Transaction is not active!"); 077 078 autoTrackLifecycleListener.onRollback(); 079 jdoTransaction.rollback(); 080 persistenceManager.close(); 081 jdoTransaction = null; 082 persistenceManager = null; 083 localRevision = -1; 084 unlock(); 085 } 086 087 @Override 088 public synchronized void rollbackIfActive() { 089 if (isActive()) 090 rollback(); 091 } 092 093 public PersistenceManager getPersistenceManager() { 094 if (!isActive()) { 095 throw new IllegalStateException("Transaction is not active!"); 096 } 097 return persistenceManager; 098 } 099 100 @Override 101 public long getLocalRevision() { 102 if (localRevision < 0) { 103 if (!write) 104 throw new IllegalStateException("This is a read-only transaction!"); 105 106 jdoTransaction.setSerializeRead(true); 107 LocalRepository lr = getDAO(LocalRepositoryDAO.class).getLocalRepositoryOrFail(); 108 jdoTransaction.setSerializeRead(null); 109 localRevision = lr.getRevision() + 1; 110 lr.setRevision(localRevision); 111 persistenceManager.flush(); 112 } 113 return localRevision; 114 } 115 116 private synchronized void lock() { 117 if (lock == null) { 118 lock = localRepoManager.getLock(); 119 lock.lock(); 120 } 121 } 122 123 private synchronized void unlock() { 124 if (lock != null) { 125 lock.unlock(); 126 lock = null; 127 } 128 } 129 130 @Override 131 public LocalRepoManager getLocalRepoManager() { 132 return localRepoManager; 133 } 134 135 @Override 136 public <D> D getDAO(Class<D> daoClass) { 137 final PersistenceManager pm = getPersistenceManager(); 138 D dao; 139 try { 140 dao = daoClass.newInstance(); 141 } catch (InstantiationException e) { 142 throw new RuntimeException(e); 143 } catch (IllegalAccessException e) { 144 throw new RuntimeException(e); 145 } 146 147 if (!(dao instanceof DAO)) 148 throw new IllegalStateException(String.format("dao class %s does not extend DAO!", daoClass.getName())); 149 150 ((DAO<?, ?>)dao).setPersistenceManager(pm); 151 return dao; 152 } 153 154 @Override 155 public void flush() { 156 final PersistenceManager pm = getPersistenceManager(); 157 pm.flush(); 158 } 159}