001/*
002 * jPOS Project [http://jpos.org]
003 * Copyright (C) 2000-2026 jPOS Software SRL
004 *
005 * This program is free software: you can redistribute it and/or modify
006 * it under the terms of the GNU Affero General Public License as
007 * published by the Free Software Foundation, either version 3 of the
008 * License, or (at your option) any later version.
009 *
010 * This program 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
013 * GNU Affero General Public License for more details.
014 *
015 * You should have received a copy of the GNU Affero General Public License
016 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
017 */
018
019package org.jpos.transaction.participant;
020
021import bsh.EvalError;
022import org.jdom2.Element;
023import org.jpos.core.ConfigurationException;
024import org.jpos.core.XmlConfigurable;
025import org.jpos.q2.QFactory;
026import org.jpos.transaction.AbortParticipant;
027import org.jpos.transaction.TransactionParticipant;
028import org.jpos.util.LogEvent;
029import org.jpos.util.Logger;
030import org.jpos.util.SimpleLogSource;
031
032import java.io.IOException;
033import java.io.Serializable;
034import java.util.HashMap;
035import java.util.Map;
036
037/** A TransactionParticipant whose prepare, commit and abort methods can be 
038 *  specified through beanshell scripts. <BR>
039 *
040 *  To indicate what code to execute for any of the methods just add an element 
041 *  named 'prepare', 'commit' or 'abort' contained in that of the participant. <BR>
042 *
043 *  See BSHMethod for details on the syntax of these elements. The value to return 
044 *  in the prepare method should be stored in the script variable named "result".
045 *  None of these tags are mandatory. <BR>
046 *
047 *  You can subclass BSHTransactionParticipant and override the default... 
048 *  methods. That way you can provide default behaviour for a participant and 
049 *  override it at deploy time through scripts. 
050 * 
051 * @see BSHMethod
052 * @author  AMarques
053 */
054@SuppressWarnings("unchecked")
055public class BSHTransactionParticipant extends SimpleLogSource
056    implements TransactionParticipant, AbortParticipant, XmlConfigurable 
057{
058    
059    protected BSHMethod prepareMethod;
060    protected BSHMethod prepareForAbortMethod;
061    protected BSHMethod commitMethod;
062    protected BSHMethod abortMethod;
063
064    boolean trace;
065    
066    /** Creates a new instance of BSHTransactionParticipant */
067    public BSHTransactionParticipant() {
068        super();
069    }
070    
071    public void abort(long id, java.io.Serializable context) {
072        LogEvent ev = new LogEvent(this, "abort");
073        if (abortMethod != null) {
074            try {
075                executeMethod(abortMethod, id, context, ev, "");
076            } catch (Exception ex) {
077                ev.addMessage(ex);
078            }
079        } else {
080            defaultAbort(id, context, ev);
081        }
082        if (trace)
083            Logger.log(ev);
084    }
085    
086    protected void defaultAbort(long id, Serializable context, LogEvent ev) {}
087    
088    public void commit(long id, java.io.Serializable context) {
089        LogEvent ev = new LogEvent(this, "commit");
090        if (commitMethod != null) {
091            try {
092                executeMethod(commitMethod, id, context, ev, "");
093            } catch (Exception ex) {
094                ev.addMessage(ex);
095            }
096        } else {
097            defaultCommit(id, context, ev);
098        }
099        if (trace)
100            Logger.log(ev);
101    }
102    
103    protected void defaultCommit(long id, Serializable context, LogEvent ev) {}
104    
105    public int prepare(long id, java.io.Serializable context) {
106        LogEvent ev = new LogEvent(this, "prepare");
107        int result = ABORTED | READONLY;
108        if (prepareMethod != null) {
109            try {
110                result = (Integer) executeMethod(prepareMethod, id, context, ev, "result");
111            } catch (Exception ex) {
112                ev.addMessage(ex);
113            }
114        } else {
115            result = defaultPrepare(id, context, ev);
116        }
117        ev.addMessage("result", Integer.toBinaryString(result));
118        if (trace)
119            Logger.log(ev);
120        return result;
121    }
122
123    public int prepareForAbort(long id, java.io.Serializable context) {
124        LogEvent ev = new LogEvent(this, "prepare-for-abort");
125        int result = ABORTED | READONLY;
126        if (prepareForAbortMethod != null) {
127            try {
128                result = (Integer) executeMethod(prepareForAbortMethod, id, context, ev, "result");
129            } catch (Exception ex) {
130                ev.addMessage(ex);
131            }
132        } 
133        ev.addMessage("result", Integer.toBinaryString(result));
134        if (trace)
135            Logger.log(ev);
136        return result;
137    }
138    
139    protected int defaultPrepare(long id, Serializable context, LogEvent ev) {
140        return PREPARED | READONLY;
141    }
142    
143    public void setConfiguration(Element e) throws ConfigurationException {
144        try {
145            prepareMethod = BSHMethod.createBshMethod(e.getChild("prepare"));
146            prepareForAbortMethod = BSHMethod.createBshMethod(e.getChild("prepare-for-abort"));
147            commitMethod = BSHMethod.createBshMethod(e.getChild("commit"));
148            abortMethod = BSHMethod.createBshMethod(e.getChild("abort"));
149            trace = "yes".equals (QFactory.getAttributeValue (e, "trace"));
150        } catch (Exception ex) {
151            throw new ConfigurationException(ex.getMessage(), ex);
152        }
153    }
154    
155    protected Object executeMethod(BSHMethod m, long id, Serializable context, LogEvent evt, String resultName) 
156    throws EvalError, IOException {
157        Map params = new HashMap();
158        params.put("context", context);
159        params.put("id", id);
160        params.put("evt", evt);
161        params.put("self", this);
162        return m.execute(params, resultName);
163    }
164}
165