package pt.ist.fenixframework.pstm; import org.apache.ojb.broker.PersistenceBroker; import org.apache.ojb.broker.PersistenceBrokerFactory; import pt.ist.fenixframework.DomainObject; public abstract class Transaction extends jvstm.Transaction { public final static TransactionStatistics STATISTICS = new TransactionStatistics(); private final static FenixCache cache = new FenixCache(); static { try { Class.forName("pt.ist.fenixframework.FenixFrameworkInitializer"); } catch (final ClassNotFoundException e) { System.out.println("No application specific initialization method."); } DomainClassInfo.initializeClassInfos(0); jvstm.Transaction.setTransactionFactory(new jvstm.TransactionFactory() { public jvstm.Transaction makeTopLevelTransaction(jvstm.ActiveTransactionsRecord record) { return new TopLevelTransaction(record); } public jvstm.Transaction makeReadOnlyTopLevelTransaction(jvstm.ActiveTransactionsRecord record) { return new ReadOnlyTopLevelTransaction(record); } }); // initialize transaction system int maxTx = TransactionChangeLogs.initializeTransactionSystem(); if (maxTx >= 0) { System.out.println("Setting the last committed TX number to " + maxTx); mostRecentRecord = new jvstm.ActiveTransactionsRecord(maxTx, null); } else { throw new Error("Couldn't determine the last transaction number"); } } static jvstm.ActiveTransactionsRecord getActiveRecordForNewTransaction() { return mostRecentRecord.getRecordForNewTransaction(); } private Transaction() { // this is never to be used!!! super(0); } private static final ThreadLocal<Boolean> DEFAULT_READ_ONLY = new ThreadLocal<Boolean>() { protected Boolean initialValue() { return Boolean.FALSE; } }; public static boolean getDefaultReadOnly() { return DEFAULT_READ_ONLY.get().booleanValue(); } public static void setDefaultReadOnly(boolean readOnly) { DEFAULT_READ_ONLY.set(readOnly ? Boolean.TRUE : Boolean.FALSE); } public static jvstm.Transaction begin() { return Transaction.begin(getDefaultReadOnly()); } public static jvstm.Transaction begin(boolean readOnly) { return jvstm.Transaction.begin(readOnly); } // This method is here just as a quick solution for providing a // way to create read-only transactions that do not open a // connection to the database, but this should be done in a better // way, probably by changing the API provided by the JVSTM, so // that the call to the Transaction.begin(...) method may receive // an argument specifying the kind of transaction that we want. public static jvstm.Transaction beginReadOnlyPossiblyInThePast(long maxMillisInThePast) { // use normal ReadOnlyTopLevelTransaction if no dbConnection was made // recently if (!TopLevelTransaction.lastDbConnectionWithin(maxMillisInThePast)) { return begin(true); } jvstm.Transaction parent = current.get(); if (parent != null) { throw new Error("This kind of transactions cannot be nested"); } jvstm.ActiveTransactionsRecord activeRecord = mostRecentRecord.getRecordForNewTransaction(); jvstm.Transaction tx = new ReadOnlyTopLevelTransactionPossiblyInThePast(activeRecord); tx.start(); return tx; } public static void forceFinish() { if (current() != null) { try { commit(); } catch (Throwable t) { System.out .println("Aborting from Transaction.forceFinish(). If being called from CloseTransactionFilter it will leave an open transaction."); abort(); } } } public static void abort() { STATISTICS.incAborts(); jvstm.Transaction.abort(); } public static FenixTransaction currentFenixTransaction() { return (FenixTransaction) current(); } protected static DBChanges currentDBChanges() { return currentFenixTransaction().getDBChanges(); } public static DomainObject readDomainObject(String classname, Integer oid) { return (oid == null) ? null : currentFenixTransaction().readDomainObject(classname, oid); } @Deprecated public static <T extends DomainObject> T getObjectForOID(long oid) { return AbstractDomainObject.<T> fromOID(oid); } public static void logAttrChange(DomainObject obj, String attrName) { currentDBChanges().logAttrChange(obj, attrName); } public static void storeNewObject(DomainObject obj) { currentDBChanges().storeNewObject(obj); ((jvstm.cps.ConsistentTransaction) current()).registerNewObject(obj); } public static void storeObject(DomainObject obj, String attrName) { currentDBChanges().storeObject(obj, attrName); } public static void deleteObject(Object obj) { currentDBChanges().deleteObject(obj); } public static void addRelationTuple(String relation, DomainObject obj1, String colNameOnObj1, DomainObject obj2, String colNameOnObj2) { currentDBChanges().addRelationTuple(relation, obj1, colNameOnObj1, obj2, colNameOnObj2); } public static void removeRelationTuple(String relation, DomainObject obj1, String colNameOnObj1, DomainObject obj2, String colNameOnObj2) { currentDBChanges().removeRelationTuple(relation, obj1, colNameOnObj1, obj2, colNameOnObj2); } public static PersistenceBroker getOJBBroker() { return currentFenixTransaction().getOJBBroker(); } // This method is temporary. It will be used only to remove the dependencies // on OJB from the remaining code. After that, it should either be removed // or replaced by something else public static java.sql.Connection getCurrentJdbcConnection() { try { return getOJBBroker().serviceConnectionManager().getConnection(); } catch (org.apache.ojb.broker.accesslayer.LookupException le) { throw new Error("Couldn't find a JDBC connection"); } } // This method is temporary. It will be used only to remove the dependencies // on OJB from the remaining code. After that, it should either be removed // or replaced by something else public static java.sql.Connection getNewJdbcConnection() { try { return PersistenceBrokerFactory.defaultPersistenceBroker().serviceConnectionManager().getConnection(); } catch (org.apache.ojb.broker.accesslayer.LookupException le) { throw new Error("Couldn't find a JDBC connection"); } } public static FenixCache getCache() { return cache; } public static void withTransaction(jvstm.TransactionalCommand command) { withTransaction(false, command); } public static void withTransaction(boolean readOnly, jvstm.TransactionalCommand command) { while (true) { Transaction.begin(readOnly); boolean txFinished = false; try { command.doIt(); Transaction.commit(); txFinished = true; return; } catch (jvstm.CommitException ce) { System.out.println("Restarting TX because of a conflict"); Transaction.abort(); txFinished = true; } catch (AbstractDomainObject.UnableToDetermineIdException unableToDetermineIdException) { System.out.println("Restaring TX: unable to determine id"); Transaction.abort(); txFinished = true; } finally { if (!txFinished) { Transaction.abort(); } } } } }