package railo.runtime.tag; import java.sql.Connection; import javax.servlet.jsp.JspException; import railo.runtime.db.DataSourceManager; import railo.runtime.db.DatasourceManagerImpl; import railo.runtime.exp.DatabaseException; import railo.runtime.exp.PageException; import railo.runtime.ext.tag.BodyTagTryCatchFinallyImpl; /** * Transaction class */ public final class Transaction extends BodyTagTryCatchFinallyImpl { private static final int ACTION_NONE = 0; private static final int ACTION_BEGIN = 1; private static final int ACTION_COMMIT = 2; private static final int ACTION_ROLLBACK = 4; private static final int ACTION_SET_SAVEPOINT = 8; //private boolean hasBody; private int isolation=Connection.TRANSACTION_NONE; private int action=ACTION_NONE; private boolean innerTag=false; private boolean ignore=false; @Override public void release() { //hasBody=false; isolation=Connection.TRANSACTION_NONE; action=ACTION_NONE; innerTag=false; super.release(); ignore=false; } /** * @param action The action to set. * @throws DatabaseException */ public void setAction(String strAction) throws DatabaseException { strAction = strAction.trim().toLowerCase(); if(strAction.equals("begin")) action=ACTION_BEGIN; else if(strAction.equals("commit")) action=ACTION_COMMIT; else if(strAction.equals("rollback")) action=ACTION_ROLLBACK; else if(strAction.equals("setsavepoint")) action=ACTION_SET_SAVEPOINT; else { throw new DatabaseException("attribute action has an invalid value, valid values are [begin,commit,setsavepoint and rollback]",null,null,null); } } /** * @param isolation The isolation to set. * @throws DatabaseException */ public void setIsolation(String isolation) throws DatabaseException { isolation=isolation.trim().toLowerCase(); if(isolation.equals("read_uncommitted")) this.isolation=Connection.TRANSACTION_READ_UNCOMMITTED; else if(isolation.equals("read_committed")) this.isolation=Connection.TRANSACTION_READ_COMMITTED; else if(isolation.equals("repeatable_read"))this.isolation=Connection.TRANSACTION_REPEATABLE_READ; else if(isolation.equals("serializable")) this.isolation=Connection.TRANSACTION_SERIALIZABLE; else if(isolation.equals("none")) this.isolation=Connection.TRANSACTION_NONE; else throw new DatabaseException("transaction has an invalid isolation level (attribute isolation, valid values are [read_uncommitted,read_committed,repeatable_read,serializable])",null,null,null); } @Override public int doStartTag() throws PageException { DataSourceManager manager = pageContext.getDataSourceManager(); // first transaction if(manager.isAutoCommit()) { //if(!hasBody)throw new DatabaseException("transaction tag with no end Tag can only be used inside a transaction tag",null,null,null); manager.begin(isolation); return EVAL_BODY_INCLUDE; } // inside transaction innerTag=true; switch(action){ /* nested transaction no longer throw a exception, they are simply ignored case ACTION_NONE: throw new DatabaseException("you can't have a nested transaction with no action defined",null,null,null); case ACTION_BEGIN: throw new DatabaseException("you can't start a transaction inside a transaction tag",null,null,null); */ case ACTION_NONE: case ACTION_BEGIN: ignore=true; break; case ACTION_COMMIT: manager.commit(); break; case ACTION_ROLLBACK: manager.rollback(); break; case ACTION_SET_SAVEPOINT: ((DatasourceManagerImpl)manager).savepoint(); break; } return EVAL_BODY_INCLUDE; } @Override public void doCatch(Throwable t) throws Throwable { if(innerTag || ignore) throw t; DataSourceManager manager = pageContext.getDataSourceManager(); try { manager.rollback(); } catch (DatabaseException e) { //print.printST(e); } throw t; } /** * @param hasBody */ public void hasBody(boolean hasBody) {//print.out("hasBody"+hasBody); //this.hasBody=hasBody; } @Override public void doFinally() { if(!ignore && !innerTag) { pageContext.getDataSourceManager().end(); } super.doFinally(); } @Override public int doAfterBody() throws JspException { if(!ignore && !innerTag) { pageContext.getDataSourceManager().commit(); } return super.doAfterBody(); } }