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 /** BeanShell method for the prepare phase. */ 060 protected BSHMethod prepareMethod; 061 /** BeanShell method for the prepare-for-abort phase. */ 062 protected BSHMethod prepareForAbortMethod; 063 /** BeanShell method for the commit phase. */ 064 protected BSHMethod commitMethod; 065 /** BeanShell method for the abort phase. */ 066 protected BSHMethod abortMethod; 067 068 /** Whether to log trace events. */ 069 boolean trace; 070 071 /** Creates a new instance of BSHTransactionParticipant */ 072 public BSHTransactionParticipant() { 073 super(); 074 } 075 076 public void abort(long id, java.io.Serializable context) { 077 LogEvent ev = new LogEvent(this, "abort"); 078 if (abortMethod != null) { 079 try { 080 executeMethod(abortMethod, id, context, ev, ""); 081 } catch (Exception ex) { 082 ev.addMessage(ex); 083 } 084 } else { 085 defaultAbort(id, context, ev); 086 } 087 if (trace) 088 Logger.log(ev); 089 } 090 091 /** 092 * Default abort handling when no BeanShell abort method is configured (no-op). 093 * @param id transaction id 094 * @param context transaction context 095 * @param ev log event 096 */ 097 protected void defaultAbort(long id, Serializable context, LogEvent ev) {} 098 099 public void commit(long id, java.io.Serializable context) { 100 LogEvent ev = new LogEvent(this, "commit"); 101 if (commitMethod != null) { 102 try { 103 executeMethod(commitMethod, id, context, ev, ""); 104 } catch (Exception ex) { 105 ev.addMessage(ex); 106 } 107 } else { 108 defaultCommit(id, context, ev); 109 } 110 if (trace) 111 Logger.log(ev); 112 } 113 114 /** Default commit implementation (no-op). 115 * @param id transaction id 116 * @param context transaction context 117 * @param ev log event 118 */ 119 protected void defaultCommit(long id, Serializable context, LogEvent ev) {} 120 121 public int prepare(long id, java.io.Serializable context) { 122 LogEvent ev = new LogEvent(this, "prepare"); 123 int result = ABORTED | READONLY; 124 if (prepareMethod != null) { 125 try { 126 result = (Integer) executeMethod(prepareMethod, id, context, ev, "result"); 127 } catch (Exception ex) { 128 ev.addMessage(ex); 129 } 130 } else { 131 result = defaultPrepare(id, context, ev); 132 } 133 ev.addMessage("result", Integer.toBinaryString(result)); 134 if (trace) 135 Logger.log(ev); 136 return result; 137 } 138 139 /** {@inheritDoc} */ 140 public int prepareForAbort(long id, java.io.Serializable context) { 141 LogEvent ev = new LogEvent(this, "prepare-for-abort"); 142 int result = ABORTED | READONLY; 143 if (prepareForAbortMethod != null) { 144 try { 145 result = (Integer) executeMethod(prepareForAbortMethod, id, context, ev, "result"); 146 } catch (Exception ex) { 147 ev.addMessage(ex); 148 } 149 } 150 ev.addMessage("result", Integer.toBinaryString(result)); 151 if (trace) 152 Logger.log(ev); 153 return result; 154 } 155 156 /** Default prepare implementation; returns PREPARED|READONLY. 157 * @param id transaction id 158 * @param context transaction context 159 * @param ev log event 160 * @return transaction result code 161 */ 162 protected int defaultPrepare(long id, Serializable context, LogEvent ev) { 163 return PREPARED | READONLY; 164 } 165 166 public void setConfiguration(Element e) throws ConfigurationException { 167 try { 168 prepareMethod = BSHMethod.createBshMethod(e.getChild("prepare")); 169 prepareForAbortMethod = BSHMethod.createBshMethod(e.getChild("prepare-for-abort")); 170 commitMethod = BSHMethod.createBshMethod(e.getChild("commit")); 171 abortMethod = BSHMethod.createBshMethod(e.getChild("abort")); 172 trace = "yes".equals (QFactory.getAttributeValue (e, "trace")); 173 } catch (Exception ex) { 174 throw new ConfigurationException(ex.getMessage(), ex); 175 } 176 } 177 178 /** 179 * Executes the given BSHMethod with the standard transaction parameters. 180 * @param m the BSHMethod to execute 181 * @param id transaction id 182 * @param context transaction context 183 * @param evt log event 184 * @param resultName the result variable name 185 * @return the value of resultName after execution 186 * @throws bsh.EvalError on BeanShell evaluation error 187 * @throws java.io.IOException if the script cannot be read 188 */ 189 protected Object executeMethod(BSHMethod m, long id, Serializable context, LogEvent evt, String resultName) 190 throws EvalError, IOException { 191 Map params = new HashMap(); 192 params.put("context", context); 193 params.put("id", id); 194 params.put("evt", evt); 195 params.put("self", this); 196 return m.execute(params, resultName); 197 } 198} 199