/*
* This file is part of the WfMOpen project.
* Copyright (C) 2001-2006 Danet GmbH (www.danet.de), BU BTS.
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* $Id: XmlRpcTool.java 2365 2007-04-22 19:44:16Z mlipp $
*
* $Log$
* Revision 1.1 2007/04/22 16:15:13 mlipp
* New generic XMLRPC tool.
*
*/
package de.danet.an.workflow.tools.xmlrpc;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.rmi.RemoteException;
import java.util.HashMap;
import java.util.Map;
import java.util.TimeZone;
import javax.xml.transform.Templates;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.sax.SAXTransformerFactory;
import javax.xml.transform.sax.TemplatesHandler;
import javax.xml.transform.stream.StreamSource;
import org.apache.xmlrpc.XmlRpcException;
import org.apache.xmlrpc.client.XmlRpcClient;
import org.apache.xmlrpc.client.XmlRpcClientConfigImpl;
import org.xml.sax.SAXException;
import de.danet.an.util.sax.SAXContentBuffer;
import de.danet.an.workflow.api.Activity;
import de.danet.an.workflow.api.FormalParameter;
import de.danet.an.workflow.api.SAXEventBuffer;
import de.danet.an.workflow.spis.aii.ApplicationNotStoppedException;
import de.danet.an.workflow.spis.aii.CannotExecuteException;
import de.danet.an.workflow.spis.aii.ResultProvider;
import de.danet.an.workflow.spis.aii.ToolAgent;
import de.danet.an.workflow.spis.aii.XMLArgumentTypeProvider;
/**
* This class provides a tool agent for executing XMLRPCs.
*
* @author mnl
*
*/
public class XmlRpcTool
implements ToolAgent, ResultProvider, XMLArgumentTypeProvider {
private static final org.apache.commons.logging.Log logger
= org.apache.commons.logging.LogFactory.getLog(XmlRpcTool.class);
private ThreadLocal result = new ThreadLocal ();
// Properties
private String timeZone = "GMT";
private String serverUrl = null;
private String operation = null;
// Non-properties
private SAXTransformerFactory saxTransFactCache = null;
private SAXEventBuffer serializerTemplates = null;
private Templates serializerTemplatesCache = null;
private SAXEventBuffer parserTemplates = null;
private Templates parserTemplatesCache = null;
private XmlRpcClient client = null;
private SAXTransformerFactory saxTransFact ()
throws TransformerConfigurationException {
synchronized (result) {
if (saxTransFactCache == null) {
saxTransFactCache = (SAXTransformerFactory)
TransformerFactory.newInstance();
}
return saxTransFactCache;
}
}
/**
* Get the serializer templates.
*/
private Templates serializerTemplates() throws CannotExecuteException {
try {
synchronized (result) {
if (serializerTemplatesCache == null) {
if (serializerTemplates != null) {
TemplatesHandler templHand
= saxTransFact().newTemplatesHandler();
serializerTemplates.emit(templHand);
// TemplatesHandler contains the xslt information
serializerTemplatesCache = templHand.getTemplates();
} else {
InputStream xslIn
= SAXContentBuffer.class.getResourceAsStream
("/de/danet/an/workflow/tools/xmlrpc/serializer.xsl");
serializerTemplatesCache = saxTransFact()
.newTemplates(new StreamSource(xslIn));
}
}
}
return serializerTemplatesCache;
} catch (TransformerConfigurationException e) {
String msg = "Error creating TransformerHandler: " + e.getMessage();
logger.error(msg, e);
throw new CannotExecuteException (msg);
} catch (SAXException e) {
String msg = "Error creating TransformerHandler: " + e.getMessage();
logger.error(msg, e);
throw new CannotExecuteException (msg);
}
}
/**
* Get the parser templates.
*/
private Templates parserTemplates() throws CannotExecuteException {
try {
synchronized (result) {
if (parserTemplatesCache == null) {
if (parserTemplates != null) {
TemplatesHandler templHand
= saxTransFact().newTemplatesHandler();
parserTemplates.emit(templHand);
// TemplatesHandler contains the xslt information
parserTemplatesCache = templHand.getTemplates();
} else {
InputStream xslIn
= SAXContentBuffer.class.getResourceAsStream
("/de/danet/an/workflow/tools/xmlrpc/parser.xsl");
parserTemplatesCache = saxTransFact()
.newTemplates(new StreamSource(xslIn));
}
}
}
return parserTemplatesCache;
} catch (TransformerConfigurationException e) {
String msg = "Error creating TransformerHandler: " + e.getMessage();
logger.error(msg, e);
throw new CannotExecuteException (msg);
} catch (SAXException e) {
String msg = "Error creating TransformerHandler: " + e.getMessage();
logger.error(msg, e);
throw new CannotExecuteException (msg);
}
}
/**
* @param timeZone The timeZone to set.
*/
public void setTimeZone(String timeZone) {
this.timeZone = timeZone;
}
/**
* @param serverUrl The serverUrl to set.
*/
public void setServerUrl(String serverUrl) {
this.serverUrl = serverUrl;
}
/**
* @param operation The operation to set.
*/
public void setOperation(String operation) {
this.operation = operation;
}
/**
* Set the serializer templates. These templates are used to
* transform arbitrary XML to XMLRPC struct arguments.
*
* @param serializerTemplates the XSL definition
*/
public void setSerializerTemplates(SAXEventBuffer xsl) {
serializerTemplates = (SAXEventBuffer)xsl;
}
/**
* Set parser templates. These templates are used to transform
* struct XMLRPC results to arbitrary XML.
*
* @param serializerTemplates the XSL definition
*/
public void setParserTemplates(SAXEventBuffer xsl) {
serializerTemplates = (SAXEventBuffer)xsl;
}
/* (non-Javadoc)
* Comment copied from interface or superclass.
*/
public void invoke(Activity activity, FormalParameter[] formalParameters,
Map actualParameters) throws RemoteException,
CannotExecuteException {
try {
if (client == null) {
client = new XmlRpcClient();
XmlRpcClientConfigImpl config = new XmlRpcClientConfigImpl();
if (serverUrl != null) {
config.setServerURL(new URL(serverUrl));
}
config.setTimeZone (TimeZone.getTimeZone(timeZone));
client.setConfig(config);
client.setTypeFactory
(new MyTypeFactory
(client, saxTransFact(),
serializerTemplates(), parserTemplates()));
}
int offset = 0;
if (serverUrl == null) {
((XmlRpcClientConfigImpl)client.getConfig()).setServerURL
(new URL(retrieveServerUrl
(formalParameters, offset++, actualParameters)));
}
String operation = this.operation;
if (operation == null) {
operation = (String)retrieveServerUrl
(formalParameters, offset++, actualParameters);
}
String resParam = null;
if (offset < formalParameters.length) {
if (!formalParameters[offset].mode()
.equals(FormalParameter.Mode.IN)) {
resParam = formalParameters[offset].id();
}
if (formalParameters[offset].mode()
.equals(FormalParameter.Mode.OUT)) {
offset += 1;
}
}
Object[] params = new Object[formalParameters.length - offset];
for (int i = 0; i + offset < formalParameters.length; i++) {
Object param = actualParameters
.get(formalParameters[i + offset].id());
params[i] = convertParameter(param);
}
Object rpcResult = client.execute(operation, params);
if (resParam != null) {
Map res = new HashMap ();
res.put(resParam, rpcResult);
result.set (res);
}
} catch (MalformedURLException e) {
throw new CannotExecuteException (e.getMessage(), e);
} catch (XmlRpcException e) {
throw new CannotExecuteException (e.getMessage(), e);
} catch (TransformerConfigurationException e) {
throw new CannotExecuteException (e.getMessage(), e);
}
}
private Object convertParameter(Object param)
throws CannotExecuteException {
if (param instanceof Long) {
if (((Long)param).longValue() > Integer.MAX_VALUE) {
throw new CannotExecuteException
("Illegal Argument",
new NumberFormatException
("XMLRPC can handle only handle 4 byte integers"));
}
param = new Integer (((Long)param).intValue());
}
return param;
}
private String retrieveServerUrl
(FormalParameter[] formalParameters, int offset, Map actualParameters) {
if (offset >= formalParameters.length
|| (!String.class.isAssignableFrom
((formalParameters[offset].getClass())))
|| (!formalParameters[offset].mode()
.equals(FormalParameter.Mode.IN))) {
throw new IllegalArgumentException
("Operation must be passed as argument "
+ "if not configured as property.");
}
return (String)actualParameters
.get(formalParameters[offset].id());
}
private String retrieveOperation
(FormalParameter[] formalParameters, int offset, Map actualParameters) {
if (offset >= formalParameters.length
|| (!String.class.isAssignableFrom
((formalParameters[offset].getClass())))
|| (!formalParameters[offset].mode()
.equals(FormalParameter.Mode.IN))) {
throw new IllegalArgumentException
("ServerUrl must be passed as first argument "
+ "if not configured as property.");
}
return (String)actualParameters
.get(formalParameters[offset].id());
}
/* (non-Javadoc)
* Comment copied from interface or superclass.
*/
public Object result() {
Object res = result.get();
result.set(null);
return res;
}
/* (non-Javadoc)
* Comment copied from interface or superclass.
*/
public void terminate(Activity activity)
throws ApplicationNotStoppedException, RemoteException {
throw new ApplicationNotStoppedException ("Cannot interrupt call.");
}
/* (non-Javadoc)
* Comment copied from interface or superclass.
*/
public int requestedXMLArgumentType() {
return XMLArgumentTypeProvider.XML_AS_SAX;
}
}