001/**
002 *
003 * Copyright (c) 2014, the Railo Company Ltd. All rights reserved.
004 *
005 * This library is free software; you can redistribute it and/or
006 * modify it under the terms of the GNU Lesser General Public
007 * License as published by the Free Software Foundation; either 
008 * version 2.1 of the License, or (at your option) any later version.
009 * 
010 * This library is distributed in the hope that it will be useful,
011 * but WITHOUT ANY WARRANTY; without even the implied warranty of
012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
013 * Lesser General Public License for more details.
014 * 
015 * You should have received a copy of the GNU Lesser General Public 
016 * License along with this library.  If not, see <http://www.gnu.org/licenses/>.
017 * 
018 **/
019package lucee.runtime.tag;
020
021
022import java.sql.Connection;
023
024import javax.servlet.jsp.JspException;
025
026import lucee.runtime.db.DataSourceManager;
027import lucee.runtime.db.DatasourceManagerImpl;
028import lucee.runtime.exp.DatabaseException;
029import lucee.runtime.exp.PageException;
030import lucee.runtime.ext.tag.BodyTagTryCatchFinallyImpl;
031
032/**
033 * Transaction class
034 */
035public final class Transaction extends BodyTagTryCatchFinallyImpl {
036        
037    private static final int ACTION_NONE = 0;
038
039        private static final int ACTION_BEGIN = 1;
040
041        private static final int ACTION_COMMIT = 2;
042
043        private static final int ACTION_ROLLBACK = 4;
044
045        private static final int ACTION_SET_SAVEPOINT = 8;
046    
047        //private boolean hasBody;
048    private int isolation=Connection.TRANSACTION_NONE;
049    private int action=ACTION_NONE;
050    private boolean innerTag=false;
051
052        private boolean ignore=false;
053    
054    @Override
055    public void release() {
056        //hasBody=false;
057        isolation=Connection.TRANSACTION_NONE;
058        action=ACTION_NONE;
059        innerTag=false;
060        super.release();
061        ignore=false;
062    }
063
064    /**
065     * @param action The action to set.
066     * @throws DatabaseException 
067     */
068    public void setAction(String strAction) throws DatabaseException {
069        strAction = strAction.trim().toLowerCase();
070        if(strAction.equals("begin")) action=ACTION_BEGIN;
071        else if(strAction.equals("commit")) action=ACTION_COMMIT;
072        else if(strAction.equals("rollback")) action=ACTION_ROLLBACK;
073        else if(strAction.equals("setsavepoint")) action=ACTION_SET_SAVEPOINT;
074        else {
075            throw new DatabaseException("attribute action has an invalid value, valid values are [begin,commit,setsavepoint and rollback]",null,null,null);
076        }
077        
078        
079    }
080
081    /**
082     * @param isolation The isolation to set.
083     * @throws DatabaseException 
084     */
085    public void setIsolation(String isolation) throws DatabaseException {
086        isolation=isolation.trim().toLowerCase();
087        if(isolation.equals("read_uncommitted"))    this.isolation=Connection.TRANSACTION_READ_UNCOMMITTED;
088        else if(isolation.equals("read_committed")) this.isolation=Connection.TRANSACTION_READ_COMMITTED;
089        else if(isolation.equals("repeatable_read"))this.isolation=Connection.TRANSACTION_REPEATABLE_READ;
090        else if(isolation.equals("serializable"))   this.isolation=Connection.TRANSACTION_SERIALIZABLE;
091        else if(isolation.equals("none"))           this.isolation=Connection.TRANSACTION_NONE;
092        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);
093    }
094
095    @Override
096    public int doStartTag() throws PageException {
097        DataSourceManager manager = pageContext.getDataSourceManager();
098        // first transaction
099        if(manager.isAutoCommit()) {
100            //if(!hasBody)throw new DatabaseException("transaction tag with no end Tag can only be used inside a transaction tag",null,null,null);
101            manager.begin(isolation);
102            return EVAL_BODY_INCLUDE;
103        }
104        // inside transaction
105        innerTag=true;
106        switch(action){
107        /* nested transaction no longer throw a exception, they are simply ignored
108        case ACTION_NONE:
109                throw new DatabaseException("you can't have a nested transaction with no action defined",null,null,null);
110        case ACTION_BEGIN:
111            throw new DatabaseException("you can't start a transaction inside a transaction tag",null,null,null);
112        */
113        case ACTION_NONE:
114        case ACTION_BEGIN:
115            ignore=true;
116            break;
117        
118        case ACTION_COMMIT:
119                manager.commit();
120        break;
121        case ACTION_ROLLBACK:
122                manager.rollback();
123        break;
124        case ACTION_SET_SAVEPOINT:
125                 ((DatasourceManagerImpl)manager).savepoint();
126        break;
127        }
128        
129        return EVAL_BODY_INCLUDE;
130    }
131    
132    @Override
133    public void doCatch(Throwable t) throws Throwable {
134        if(innerTag || ignore) throw t;
135        
136        DataSourceManager manager = pageContext.getDataSourceManager();
137        try {
138            manager.rollback();
139        } catch (DatabaseException e) {
140                //print.printST(e);
141        }
142        throw t;
143    }
144
145    /**
146     * @param hasBody
147     */
148    public void hasBody(boolean hasBody) {//print.out("hasBody"+hasBody);
149        //this.hasBody=hasBody;
150    }
151
152    @Override
153    public void doFinally() {
154        if(!ignore && !innerTag) {
155                pageContext.getDataSourceManager().end();
156        }
157        super.doFinally();
158    }
159
160    @Override
161    public int doAfterBody() throws JspException {
162        
163        if(!ignore && !innerTag) {
164                pageContext.getDataSourceManager().commit();
165        }
166        return super.doAfterBody();
167    }
168}