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        /**
037         * @see railo.runtime.ext.tag.BodyTagImpl#release()
038         */
039        public void release() {
040            //hasBody=false;
041            isolation=Connection.TRANSACTION_NONE;
042            action=ACTION_NONE;
043            innerTag=false;
044            super.release();
045            ignore=false;
046        }
047    
048        /**
049         * @param action The action to set.
050         * @throws DatabaseException 
051         */
052        public void setAction(String strAction) throws DatabaseException {
053            strAction = strAction.trim().toLowerCase();
054            if(strAction.equals("begin")) action=ACTION_BEGIN;
055            else if(strAction.equals("commit")) action=ACTION_COMMIT;
056            else if(strAction.equals("rollback")) action=ACTION_ROLLBACK;
057            else if(strAction.equals("setsavepoint")) action=ACTION_SET_SAVEPOINT;
058            else {
059                throw new DatabaseException("attribute action has a invalid value, valid values are [begin,commit,setsavepoint and rollback]",null,null,null);
060            }
061            
062            
063        }
064    
065        /**
066         * @param isolation The isolation to set.
067         * @throws DatabaseException 
068         */
069        public void setIsolation(String isolation) throws DatabaseException {
070            isolation=isolation.trim().toLowerCase();
071            if(isolation.equals("read_uncommitted"))    this.isolation=Connection.TRANSACTION_READ_UNCOMMITTED;
072            else if(isolation.equals("read_committed")) this.isolation=Connection.TRANSACTION_READ_COMMITTED;
073            else if(isolation.equals("repeatable_read"))this.isolation=Connection.TRANSACTION_REPEATABLE_READ;
074            else if(isolation.equals("serializable"))   this.isolation=Connection.TRANSACTION_SERIALIZABLE;
075            else if(isolation.equals("none"))           this.isolation=Connection.TRANSACTION_NONE;
076            else throw new DatabaseException("transaction has a invalid isolation level (attribute isolation, valid values are [read_uncommitted,read_committed,repeatable_read,serializable])",null,null,null);
077        }
078    
079        /**
080         * @see railo.runtime.ext.tag.TagImpl#doStartTag()
081         */
082        public int doStartTag() throws PageException {
083            DataSourceManager manager = pageContext.getDataSourceManager();
084            // first transaction
085            if(manager.isAutoCommit()) {
086                //if(!hasBody)throw new DatabaseException("transaction tag with no end Tag can only be used inside a transaction tag",null,null,null);
087                manager.begin(isolation);
088                return EVAL_BODY_INCLUDE;
089            }
090            // inside transaction
091            innerTag=true;
092            switch(action){
093            /* nested transaction no longer throw a exception, they are simply ignored
094            case ACTION_NONE:
095                    throw new DatabaseException("you can't have a nested transaction with no action defined",null,null,null);
096            case ACTION_BEGIN:
097                throw new DatabaseException("you can't start a transaction inside a transaction tag",null,null,null);
098            */
099            case ACTION_NONE:
100            case ACTION_BEGIN:
101                ignore=true;
102                break;
103            
104            case ACTION_COMMIT:
105                    manager.commit();
106            break;
107            case ACTION_ROLLBACK:
108                    manager.rollback();
109            break;
110            case ACTION_SET_SAVEPOINT:
111                     ((DatasourceManagerImpl)manager).savepoint();
112            break;
113            }
114            
115            return EVAL_BODY_INCLUDE;
116        }
117        
118        /**
119         * @see railo.runtime.ext.tag.BodyTagTryCatchFinallyImpl#doCatch(java.lang.Throwable)
120         */
121        public void doCatch(Throwable t) throws Throwable {
122            if(innerTag || ignore) throw t;
123            
124            DataSourceManager manager = pageContext.getDataSourceManager();
125            try {
126                manager.rollback();
127            } catch (DatabaseException e) {
128                    //print.printST(e);
129            }
130            throw t;
131        }
132    
133        /**
134         * @param hasBody
135         */
136        public void hasBody(boolean hasBody) {//print.out("hasBody"+hasBody);
137            //this.hasBody=hasBody;
138        }
139    
140        /**
141         * @see railo.runtime.ext.tag.BodyTagTryCatchFinallyImpl#doFinally()
142         */
143        public void doFinally() {
144            if(!ignore && !innerTag) {
145                    pageContext.getDataSourceManager().end();
146            }
147            super.doFinally();
148        }
149    
150        /**
151         * @see railo.runtime.ext.tag.BodyTagImpl#doAfterBody()
152         */
153        public int doAfterBody() throws JspException {
154            
155            if(!ignore && !innerTag) {
156                    pageContext.getDataSourceManager().commit();
157            }
158            return super.doAfterBody();
159        }
160    }