001: /*
002: * This file is part of the WfMOpen project.
003: * Copyright (C) 2001-2006 Danet GmbH (www.danet.de), BU BTS.
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: XmlRpcTool.java,v 1.2 2007/04/22 19:44:16 mlipp Exp $
021: *
022: * $Log: XmlRpcTool.java,v $
023: * Revision 1.2 2007/04/22 19:44:16 mlipp
024: * Added type handling.
025: *
026: * Revision 1.1 2007/04/22 16:15:13 mlipp
027: * New generic XMLRPC tool.
028: *
029: */
030: package de.danet.an.workflow.tools.xmlrpc;
031:
032: import java.io.InputStream;
033: import java.net.MalformedURLException;
034: import java.net.URL;
035: import java.rmi.RemoteException;
036: import java.util.HashMap;
037: import java.util.Map;
038: import java.util.TimeZone;
039:
040: import javax.xml.transform.Templates;
041: import javax.xml.transform.TransformerConfigurationException;
042: import javax.xml.transform.TransformerFactory;
043: import javax.xml.transform.sax.SAXTransformerFactory;
044: import javax.xml.transform.sax.TemplatesHandler;
045: import javax.xml.transform.stream.StreamSource;
046:
047: import org.apache.xmlrpc.XmlRpcException;
048: import org.apache.xmlrpc.client.XmlRpcClient;
049: import org.apache.xmlrpc.client.XmlRpcClientConfigImpl;
050: import org.xml.sax.SAXException;
051:
052: import de.danet.an.util.sax.SAXContentBuffer;
053: import de.danet.an.workflow.api.Activity;
054: import de.danet.an.workflow.api.FormalParameter;
055: import de.danet.an.workflow.api.SAXEventBuffer;
056: import de.danet.an.workflow.spis.aii.ApplicationNotStoppedException;
057: import de.danet.an.workflow.spis.aii.CannotExecuteException;
058: import de.danet.an.workflow.spis.aii.ResultProvider;
059: import de.danet.an.workflow.spis.aii.ToolAgent;
060: import de.danet.an.workflow.spis.aii.XMLArgumentTypeProvider;
061:
062: /**
063: * This class provides a tool agent for executing XMLRPCs.
064: *
065: * @author mnl
066: *
067: */
068: public class XmlRpcTool implements ToolAgent, ResultProvider,
069: XMLArgumentTypeProvider {
070:
071: private static final org.apache.commons.logging.Log logger = org.apache.commons.logging.LogFactory
072: .getLog(XmlRpcTool.class);
073:
074: private ThreadLocal result = new ThreadLocal();
075:
076: // Properties
077: private String timeZone = "GMT";
078: private String serverUrl = null;
079: private String operation = null;
080:
081: // Non-properties
082: private SAXTransformerFactory saxTransFactCache = null;
083: private SAXEventBuffer serializerTemplates = null;
084: private Templates serializerTemplatesCache = null;
085: private SAXEventBuffer parserTemplates = null;
086: private Templates parserTemplatesCache = null;
087:
088: private XmlRpcClient client = null;
089:
090: private SAXTransformerFactory saxTransFact()
091: throws TransformerConfigurationException {
092: synchronized (result) {
093: if (saxTransFactCache == null) {
094: saxTransFactCache = (SAXTransformerFactory) TransformerFactory
095: .newInstance();
096: }
097: return saxTransFactCache;
098: }
099: }
100:
101: /**
102: * Get the serializer templates.
103: */
104: private Templates serializerTemplates()
105: throws CannotExecuteException {
106: try {
107: synchronized (result) {
108: if (serializerTemplatesCache == null) {
109: if (serializerTemplates != null) {
110: TemplatesHandler templHand = saxTransFact()
111: .newTemplatesHandler();
112: serializerTemplates.emit(templHand);
113: // TemplatesHandler contains the xslt information
114: serializerTemplatesCache = templHand
115: .getTemplates();
116: } else {
117: InputStream xslIn = SAXContentBuffer.class
118: .getResourceAsStream("/de/danet/an/workflow/tools/xmlrpc/serializer.xsl");
119: serializerTemplatesCache = saxTransFact()
120: .newTemplates(new StreamSource(xslIn));
121: }
122: }
123: }
124: return serializerTemplatesCache;
125: } catch (TransformerConfigurationException e) {
126: String msg = "Error creating TransformerHandler: "
127: + e.getMessage();
128: logger.error(msg, e);
129: throw new CannotExecuteException(msg);
130: } catch (SAXException e) {
131: String msg = "Error creating TransformerHandler: "
132: + e.getMessage();
133: logger.error(msg, e);
134: throw new CannotExecuteException(msg);
135: }
136: }
137:
138: /**
139: * Get the parser templates.
140: */
141: private Templates parserTemplates() throws CannotExecuteException {
142: try {
143: synchronized (result) {
144: if (parserTemplatesCache == null) {
145: if (parserTemplates != null) {
146: TemplatesHandler templHand = saxTransFact()
147: .newTemplatesHandler();
148: parserTemplates.emit(templHand);
149: // TemplatesHandler contains the xslt information
150: parserTemplatesCache = templHand.getTemplates();
151: } else {
152: InputStream xslIn = SAXContentBuffer.class
153: .getResourceAsStream("/de/danet/an/workflow/tools/xmlrpc/parser.xsl");
154: parserTemplatesCache = saxTransFact()
155: .newTemplates(new StreamSource(xslIn));
156: }
157: }
158: }
159: return parserTemplatesCache;
160: } catch (TransformerConfigurationException e) {
161: String msg = "Error creating TransformerHandler: "
162: + e.getMessage();
163: logger.error(msg, e);
164: throw new CannotExecuteException(msg);
165: } catch (SAXException e) {
166: String msg = "Error creating TransformerHandler: "
167: + e.getMessage();
168: logger.error(msg, e);
169: throw new CannotExecuteException(msg);
170: }
171: }
172:
173: /**
174: * @param timeZone The timeZone to set.
175: */
176: public void setTimeZone(String timeZone) {
177: this .timeZone = timeZone;
178: }
179:
180: /**
181: * @param serverUrl The serverUrl to set.
182: */
183: public void setServerUrl(String serverUrl) {
184: this .serverUrl = serverUrl;
185: }
186:
187: /**
188: * @param operation The operation to set.
189: */
190: public void setOperation(String operation) {
191: this .operation = operation;
192: }
193:
194: /**
195: * Set the serializer templates. These templates are used to
196: * transform arbitrary XML to XMLRPC struct arguments.
197: *
198: * @param serializerTemplates the XSL definition
199: */
200: public void setSerializerTemplates(SAXEventBuffer xsl) {
201: serializerTemplates = (SAXEventBuffer) xsl;
202: }
203:
204: /**
205: * Set parser templates. These templates are used to transform
206: * struct XMLRPC results to arbitrary XML.
207: *
208: * @param serializerTemplates the XSL definition
209: */
210: public void setParserTemplates(SAXEventBuffer xsl) {
211: serializerTemplates = (SAXEventBuffer) xsl;
212: }
213:
214: /* (non-Javadoc)
215: * Comment copied from interface or superclass.
216: */
217: public void invoke(Activity activity,
218: FormalParameter[] formalParameters, Map actualParameters)
219: throws RemoteException, CannotExecuteException {
220: try {
221: if (client == null) {
222: client = new XmlRpcClient();
223: XmlRpcClientConfigImpl config = new XmlRpcClientConfigImpl();
224: if (serverUrl != null) {
225: config.setServerURL(new URL(serverUrl));
226: }
227: config.setTimeZone(TimeZone.getTimeZone(timeZone));
228: client.setConfig(config);
229: client.setTypeFactory(new MyTypeFactory(client,
230: saxTransFact(), serializerTemplates(),
231: parserTemplates()));
232: }
233: int offset = 0;
234: if (serverUrl == null) {
235: ((XmlRpcClientConfigImpl) client.getConfig())
236: .setServerURL(new URL(retrieveServerUrl(
237: formalParameters, offset++,
238: actualParameters)));
239: }
240: String operation = this .operation;
241: if (operation == null) {
242: operation = (String) retrieveServerUrl(
243: formalParameters, offset++, actualParameters);
244: }
245: String resParam = null;
246: if (offset < formalParameters.length) {
247: if (!formalParameters[offset].mode().equals(
248: FormalParameter.Mode.IN)) {
249: resParam = formalParameters[offset].id();
250: }
251: if (formalParameters[offset].mode().equals(
252: FormalParameter.Mode.OUT)) {
253: offset += 1;
254: }
255: }
256: Object[] params = new Object[formalParameters.length
257: - offset];
258: for (int i = 0; i + offset < formalParameters.length; i++) {
259: Object param = actualParameters.get(formalParameters[i
260: + offset].id());
261: params[i] = convertParameter(param);
262: }
263: Object rpcResult = client.execute(operation, params);
264: if (resParam != null) {
265: Map res = new HashMap();
266: res.put(resParam, rpcResult);
267: result.set(res);
268: }
269: } catch (MalformedURLException e) {
270: throw new CannotExecuteException(e.getMessage(), e);
271: } catch (XmlRpcException e) {
272: throw new CannotExecuteException(e.getMessage(), e);
273: } catch (TransformerConfigurationException e) {
274: throw new CannotExecuteException(e.getMessage(), e);
275: }
276: }
277:
278: private Object convertParameter(Object param)
279: throws CannotExecuteException {
280: if (param instanceof Long) {
281: if (((Long) param).longValue() > Integer.MAX_VALUE) {
282: throw new CannotExecuteException(
283: "Illegal Argument",
284: new NumberFormatException(
285: "XMLRPC can handle only handle 4 byte integers"));
286: }
287: param = new Integer(((Long) param).intValue());
288: }
289: return param;
290: }
291:
292: private String retrieveServerUrl(
293: FormalParameter[] formalParameters, int offset,
294: Map actualParameters) {
295: if (offset >= formalParameters.length
296: || (!String.class
297: .isAssignableFrom((formalParameters[offset]
298: .getClass())))
299: || (!formalParameters[offset].mode().equals(
300: FormalParameter.Mode.IN))) {
301: throw new IllegalArgumentException(
302: "Operation must be passed as argument "
303: + "if not configured as property.");
304: }
305: return (String) actualParameters.get(formalParameters[offset]
306: .id());
307: }
308:
309: private String retrieveOperation(
310: FormalParameter[] formalParameters, int offset,
311: Map actualParameters) {
312: if (offset >= formalParameters.length
313: || (!String.class
314: .isAssignableFrom((formalParameters[offset]
315: .getClass())))
316: || (!formalParameters[offset].mode().equals(
317: FormalParameter.Mode.IN))) {
318: throw new IllegalArgumentException(
319: "ServerUrl must be passed as first argument "
320: + "if not configured as property.");
321: }
322: return (String) actualParameters.get(formalParameters[offset]
323: .id());
324: }
325:
326: /* (non-Javadoc)
327: * Comment copied from interface or superclass.
328: */
329: public Object result() {
330: Object res = result.get();
331: result.set(null);
332: return res;
333: }
334:
335: /* (non-Javadoc)
336: * Comment copied from interface or superclass.
337: */
338: public void terminate(Activity activity)
339: throws ApplicationNotStoppedException, RemoteException {
340: throw new ApplicationNotStoppedException(
341: "Cannot interrupt call.");
342: }
343:
344: /* (non-Javadoc)
345: * Comment copied from interface or superclass.
346: */
347: public int requestedXMLArgumentType() {
348: return XMLArgumentTypeProvider.XML_AS_SAX;
349: }
350:
351: }
|