001: /*
002: * This file is part of the WfMOpen project.
003: * Copyright (C) 2001-2003 Danet GmbH (www.danet.de), GS-AN.
004: * All rights reserved.
005: *
006: * This program is free software; you can redistribute it and/or modify
007: * it under the terms of the GNU General Public License as published by
008: * the Free Software Foundation; either version 2 of the License, or
009: * (at your option) any later version.
010: *
011: * This program is distributed in the hope that it will be useful,
012: * but WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
014: * GNU General Public License for more details.
015: *
016: * You should have received a copy of the GNU General Public License
017: * along with this program; if not, write to the Free Software
018: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
019: *
020: * $Id: MethodInvocationBatch.java,v 1.5 2006/09/29 12:32:07 drmlipp Exp $
021: *
022: * $Log: MethodInvocationBatch.java,v $
023: * Revision 1.5 2006/09/29 12:32:07 drmlipp
024: * Consistently using WfMOpen as projct name now.
025: *
026: * Revision 1.4 2005/09/30 21:48:22 mlipp
027: * Added simple result retrieval.
028: *
029: * Revision 1.3 2005/09/09 20:48:39 mlipp
030: * Fixed JDK 5.0 warning.
031: *
032: * Revision 1.2 2005/01/17 22:53:57 mlipp
033: * Added possibility to stop when an exception occurs.
034: *
035: * Revision 1.1.1.1 2003/06/30 20:05:13 drmlipp
036: * Initial import
037: *
038: * Revision 1.3 2003/06/27 08:51:46 lipp
039: * Fixed copyright/license information.
040: *
041: * Revision 1.2 2003/03/03 13:58:13 schlue
042: * IPR7 (comment for execute enhanced)
043: *
044: * Revision 1.1 2002/12/10 11:21:05 lipp
045: * Added batch processing as "generic DTO".
046: *
047: */
048: package de.danet.an.workflow.api;
049:
050: import java.io.Serializable;
051:
052: import java.util.ArrayList;
053: import java.util.Date;
054: import java.util.Iterator;
055: import java.util.List;
056:
057: import java.lang.reflect.InvocationTargetException;
058: import java.lang.reflect.Method;
059:
060: /**
061: * This class provides a {@link Batch <code>Batch</code>}
062: * implementation that executes several invocations of remote objects
063: * on the server in a single transaction and returns the results. This
064: * class can be thought of as a "generic DTO".
065: *
066: * @author <a href="mailto:lipp@danet.de"></a>
067: * @version $Revision: 1.5 $
068: */
069: public class MethodInvocationBatch implements Batch, Serializable {
070:
071: private boolean stopOnException = false;
072: private List invoks = new ArrayList();
073:
074: /**
075: * Create a new empty method invocation batch. Equivalent to
076: * <code>MethodInvocationBatch(false)</code>.
077: */
078: public MethodInvocationBatch() {
079: this (false);
080: }
081:
082: /**
083: * Create a new empty method invocation batch. The flag passed as
084: * parameter controls if an exception stops the batch execution.
085: * @param stopOnException if <code>true</code> execution is
086: * interrupted on the first encountered exception
087: */
088: public MethodInvocationBatch(boolean stopOnException) {
089: this .stopOnException = stopOnException;
090: }
091:
092: /**
093: * Adds a method invocation to the batch.
094: * @param obj the objects whose method is to be invoked.
095: * @param method the method name.
096: * @param argTypes the argument types as strings suitable for
097: * <code>ClassLoader.loadClass</code>. May be <code>null</code>
098: * which is interpreted as "no parameters".
099: * @param args the actual arguments. May be <code>null</code>
100: * which is interpreted as "no parameters".
101: */
102: public void addInvocation(Object obj, String method,
103: String[] argTypes, Object[] args) {
104: invoks.add(new Object[] { obj, method, argTypes, args });
105: }
106:
107: /**
108: * Adds a method invocation on a previous result to the batch.
109: * @param result <b>relative</b> index of the result to be used
110: * for method invovation, i.e. <code>-1</code> is the previous result.
111: * @param method the method name.
112: * @param argTypes the argument types as strings suitable for
113: * <code>ClassLoader.loadClass</code>. May be <code>null</code>
114: * which is interpreted as "no parameters".
115: * @param args the actual arguments. May be <code>null</code>
116: * which is interpreted as "no parameters".
117: * @param discard if <code>true</code> the referenced result will
118: * be removed from the result list.
119: */
120: public void addInvocation(int result, String method,
121: String[] argTypes, Object[] args, boolean discard) {
122: invoks.add(new Object[] { new Integer(result), method,
123: argTypes, args, new Boolean(discard) });
124: }
125:
126: /**
127: * The result of an execution of this kind of batch.
128: */
129: public class Result implements Serializable {
130: private Object[] results;
131: private boolean exceptions;
132:
133: /**
134: * Construct a new <code>Result</code> objects with the given
135: * attributes.
136: * @param theResults the results attribute.
137: * @param exceptionsOccured the exceptions attribute.
138: */
139: public Result(Object[] theResults, boolean exceptionsOccured) {
140: results = theResults;
141: exceptions = exceptionsOccured;
142: }
143:
144: /**
145: * The results as an array of objects. Each entry in the array
146: * corresponds to a method call and is either a result (with
147: * java primitive types wrapped in corresponding objects) or
148: * an exception thrown by the invoked method.
149: * @return the results.
150: */
151: public Object[] results() {
152: return results;
153: }
154:
155: /**
156: * Returns the result with the given index.
157: * @param i the index into the result array.
158: * @return result.
159: */
160: public Object result(int i) {
161: return results[i];
162: }
163:
164: /**
165: * Returns the result with the given index as <code>String</code>.
166: * @param i the index into the result array.
167: * @return result as <code>String</code>.
168: */
169: public String resultAsString(int i) {
170: return (String) results[i];
171: }
172:
173: /**
174: * Returns the result with the given index as <code>Date</code>.
175: * @param i the index into the result array.
176: * @return result as <code>Date</code>.
177: */
178: public Date resultAsDate(int i) {
179: return (Date) results[i];
180: }
181:
182: /**
183: * Returns the result with the given index as <code>int</code>.
184: * @param i the index into the result array.
185: * @return result as <code>int</code>.
186: */
187: public int resultAsInt(int i) {
188: return ((Integer) results[i]).intValue();
189: }
190:
191: /**
192: * Return <code>true</code> if any exceptions have occured
193: * during batch execution.
194: * @return <code>true</code> if execeptions have occured.
195: */
196: public boolean hasExceptions() {
197: return exceptions;
198: }
199:
200: /**
201: * Return the first exception in the result list.
202: * @return the first exception.
203: * @throws IllegalStateException if the result includes no exceptions.
204: */
205: public Exception firstException() throws IllegalStateException {
206: for (int i = 0; i < results.length; i++) {
207: if (results[i] instanceof Exception) {
208: return (Exception) results[i];
209: }
210: }
211: throw new IllegalStateException("No exceptions in result");
212: }
213: }
214:
215: /**
216: * Executes the registered method invocations one by one in a
217: * single transaction. Note that execution is terminated if an
218: * invoked method sets rollback only.
219: *
220: * @param ctx the execution context.
221: * @return the execution result, which is of type {@link Result
222: * <code>Result</code>}.
223: */
224: public Object execute(Context ctx) {
225: if (ctx == null) {
226: throw new IllegalArgumentException();
227: }
228: boolean gotExc = false;
229: List resVals = new ArrayList();
230: for (Iterator i = invoks.iterator(); i.hasNext()
231: && !ctx.isRollbackOnly();) {
232: try {
233: Object[] ips = (Object[]) i.next();
234: Object obj = null;
235: Integer discard = null;
236: if (ips.length == 4) {
237: obj = ips[0];
238: } else {
239: int idx = resVals.size()
240: + ((Integer) ips[0]).intValue();
241: obj = resVals.get(idx);
242: if (((Boolean) ips[4]).booleanValue()) {
243: discard = new Integer(idx);
244: }
245: }
246: String method = (String) ips[1];
247: String[] argTypes = (String[]) ips[2];
248: Object[] args = (Object[]) ips[3];
249: Method m = null;
250: if (argTypes != null) {
251: Class[] argCls = new Class[argTypes.length];
252: ClassLoader cl = Thread.currentThread()
253: .getContextClassLoader();
254: for (int aci = 0; aci < argTypes.length; aci++) {
255: argCls[aci] = cl.loadClass(argTypes[aci]);
256: }
257: m = obj.getClass().getMethod(method, argCls);
258: } else {
259: m = obj.getClass()
260: .getMethod(method, (Class[]) null);
261: }
262: resVals.add(m.invoke(obj, args));
263: if (discard != null) {
264: resVals.remove(discard.intValue());
265: }
266: } catch (Exception exc) {
267: gotExc = true;
268: Throwable e = exc;
269: if (e instanceof InvocationTargetException) {
270: e = ((InvocationTargetException) e)
271: .getTargetException();
272: }
273: resVals.add(e);
274: if (stopOnException) {
275: break;
276: }
277: }
278: }
279: return new Result(resVals.toArray(), gotExc);
280: }
281: }
|