001    package railo.runtime.tag;
002    
003    
004    import java.sql.Connection;
005    
006    import javax.servlet.jsp.JspException;
007    
008    import railo.runtime.db.DataSourceManager;
009    import railo.runtime.db.DatasourceManagerImpl;
010    import railo.runtime.exp.DatabaseException;
011    import railo.runtime.exp.PageException;
012    import railo.runtime.ext.tag.BodyTagTryCatchFinallyImpl;
013    
014    /**
015     * Transaction class
016     */
017    public final class Transaction extends BodyTagTryCatchFinallyImpl {
018            
019        private static final int ACTION_NONE = 0;
020    
021            private static final int ACTION_BEGIN = 1;
022    
023            private static final int ACTION_COMMIT = 2;
024    
025            private static final int ACTION_ROLLBACK = 4;
026    
027            private static final int ACTION_SET_SAVEPOINT = 8;
028        
029            //private boolean hasBody;
030        private int isolation=Connection.TRANSACTION_NONE;
031        private int action=ACTION_NONE;
032        private boolean innerTag=false;
033    
034            private boolean ignore=false;
035        
036        @Override
037        public void release() {
038            //hasBody=false;
039            isolation=Connection.TRANSACTION_NONE;
040            action=ACTION_NONE;
041            innerTag=false;
042            super.release();
043            ignore=false;
044        }
045    
046        /**
047         * @param action The action to set.
048         * @throws DatabaseException 
049         */
050        public void setAction(String strAction) throws DatabaseException {
051            strAction = strAction.trim().toLowerCase();
052            if(strAction.equals("begin")) action=ACTION_BEGIN;
053            else if(strAction.equals("commit")) action=ACTION_COMMIT;
054            else if(strAction.equals("rollback")) action=ACTION_ROLLBACK;
055            else if(strAction.equals("setsavepoint")) action=ACTION_SET_SAVEPOINT;
056            else {
057                throw new DatabaseException("attribute action has an invalid value, valid values are [begin,commit,setsavepoint and rollback]",null,null,null);
058            }
059            
060            
061        }
062    
063        /**
064         * @param isolation The isolation to set.
065         * @throws DatabaseException 
066         */
067        public void setIsolation(String isolation) throws DatabaseException {
068            isolation=isolation.trim().toLowerCase();
069            if(isolation.equals("read_uncommitted"))    this.isolation=Connection.TRANSACTION_READ_UNCOMMITTED;
070            else if(isolation.equals("read_committed")) this.isolation=Connection.TRANSACTION_READ_COMMITTED;
071            else if(isolation.equals("repeatable_read"))this.isolation=Connection.TRANSACTION_REPEATABLE_READ;
072            else if(isolation.equals("serializable"))   this.isolation=Connection.TRANSACTION_SERIALIZABLE;
073            else if(isolation.equals("none"))           this.isolation=Connection.TRANSACTION_NONE;
074            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);
075        }
076    
077        @Override
078        public int doStartTag() throws PageException {
079            DataSourceManager manager = pageContext.getDataSourceManager();
080            // first transaction
081            if(manager.isAutoCommit()) {
082                //if(!hasBody)throw new DatabaseException("transaction tag with no end Tag can only be used inside a transaction tag",null,null,null);
083                manager.begin(isolation);
084                return EVAL_BODY_INCLUDE;
085            }
086            // inside transaction
087            innerTag=true;
088            switch(action){
089            /* nested transaction no longer throw a exception, they are simply ignored
090            case ACTION_NONE:
091                    throw new DatabaseException("you can't have a nested transaction with no action defined",null,null,null);
092            case ACTION_BEGIN:
093                throw new DatabaseException("you can't start a transaction inside a transaction tag",null,null,null);
094            */
095            case ACTION_NONE:
096            case ACTION_BEGIN:
097                ignore=true;
098                break;
099            
100            case ACTION_COMMIT:
101                    manager.commit();
102            break;
103            case ACTION_ROLLBACK:
104                    manager.rollback();
105            break;
106            case ACTION_SET_SAVEPOINT:
107                     ((DatasourceManagerImpl)manager).savepoint();
108            break;
109            }
110            
111            return EVAL_BODY_INCLUDE;
112        }
113        
114        @Override
115        public void doCatch(Throwable t) throws Throwable {
116            if(innerTag || ignore) throw t;
117            
118            DataSourceManager manager = pageContext.getDataSourceManager();
119            try {
120                manager.rollback();
121            } catch (DatabaseException e) {
122                    //print.printST(e);
123            }
124            throw t;
125        }
126    
127        /**
128         * @param hasBody
129         */
130        public void hasBody(boolean hasBody) {//print.out("hasBody"+hasBody);
131            //this.hasBody=hasBody;
132        }
133    
134        @Override
135        public void doFinally() {
136            if(!ignore && !innerTag) {
137                    pageContext.getDataSourceManager().end();
138            }
139            super.doFinally();
140        }
141    
142        @Override
143        public int doAfterBody() throws JspException {
144            
145            if(!ignore && !innerTag) {
146                    pageContext.getDataSourceManager().commit();
147            }
148            return super.doAfterBody();
149        }
150    }