001: /*
002: * This file is part of the WfMOpen project.
003: * Copyright (C) 2001-2004 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: GenericSoapClient.java,v 1.8 2007/01/30 11:56:15 drmlipp Exp $
021: *
022: * $Log: GenericSoapClient.java,v $
023: * Revision 1.8 2007/01/30 11:56:15 drmlipp
024: * Merged Wf-XML branch.
025: *
026: * Revision 1.7.4.1 2006/12/20 14:37:59 schnelle
027: * Implemented Factory GetDefinition.
028: *
029: * Revision 1.7 2006/10/17 08:43:42 drmlipp
030: * Added support for JBoss connection exception detection.
031: *
032: * Revision 1.6 2006/09/29 12:32:09 drmlipp
033: * Consistently using WfMOpen as projct name now.
034: *
035: * Revision 1.5 2005/09/05 14:03:44 drmlipp
036: * Adapted to ToolAgent API update.
037: *
038: * Revision 1.4 2005/08/29 14:22:32 drmlipp
039: * Added ConnectException handling for generic soap client.
040: *
041: * Revision 1.3 2005/08/04 07:02:37 drmlipp
042: * Rmoved not needed code.
043: *
044: * Revision 1.2 2005/08/03 15:35:35 drmlipp
045: * Back-ported generic SOAP tool from wfmopen-2 branch.
046: *
047: */
048: package de.danet.an.workflow.tools.soap;
049:
050: import java.io.Serializable;
051: import java.net.MalformedURLException;
052: import java.net.URL;
053: import java.rmi.ConnectException;
054: import java.rmi.RemoteException;
055: import java.util.HashMap;
056: import java.util.Iterator;
057: import java.util.List;
058: import java.util.Map;
059:
060: import javax.xml.soap.MessageFactory;
061: import javax.xml.soap.SOAPBody;
062: import javax.xml.soap.SOAPConnection;
063: import javax.xml.soap.SOAPConnectionFactory;
064: import javax.xml.soap.SOAPElement;
065: import javax.xml.soap.SOAPEnvelope;
066: import javax.xml.soap.SOAPException;
067: import javax.xml.soap.SOAPHeader;
068: import javax.xml.soap.SOAPMessage;
069: import javax.xml.soap.SOAPPart;
070: import javax.xml.transform.Transformer;
071: import javax.xml.transform.TransformerException;
072: import javax.xml.transform.TransformerFactory;
073: import javax.xml.transform.dom.DOMSource;
074: import javax.xml.transform.sax.SAXResult;
075:
076: import org.jaxen.JaxenException;
077: import org.jaxen.XPath;
078: import org.jaxen.dom.DOMXPath;
079: import org.w3c.dom.Document;
080: import org.w3c.dom.DocumentFragment;
081: import org.w3c.dom.Element;
082: import org.w3c.dom.Node;
083: import org.w3c.dom.NodeList;
084: import org.xml.sax.SAXException;
085:
086: import de.danet.an.util.sax.HandlerStack;
087: import de.danet.an.util.soap.SOAPBuilder;
088: import de.danet.an.util.web.W3CDomUtil;
089: import de.danet.an.workflow.api.Activity;
090: import de.danet.an.workflow.api.FormalParameter;
091: import de.danet.an.workflow.api.SAXEventBuffer;
092: import de.danet.an.workflow.spis.aii.ApplicationNotStoppedException;
093: import de.danet.an.workflow.spis.aii.CannotExecuteException;
094: import de.danet.an.workflow.spis.aii.ResultProvider;
095: import de.danet.an.workflow.spis.aii.ToolAgent;
096: import de.danet.an.workflow.spis.aii.XMLArgumentTypeProvider;
097: import de.danet.an.workflow.util.SAXEventBufferImpl;
098: import de.danet.an.workflow.util.XPDLUtil;
099:
100: /**
101: * This class provides a generic SOAP client.
102: *
103: * @author <a href="mailto:lipp@danet.de">Michael Lipp</a>
104: * @version $Revision: 1.8 $
105: */
106:
107: public class GenericSoapClient implements ToolAgent,
108: XMLArgumentTypeProvider, ResultProvider, Serializable {
109:
110: private static final org.apache.commons.logging.Log logger = org.apache.commons.logging.LogFactory
111: .getLog(GenericSoapClient.class);
112:
113: // hold the mapping of return parameter name and eventually the XPath
114: // expression
115: private Map returnParamInfo = new HashMap();
116:
117: private String endpoint;
118:
119: /** The result container. */
120: private ThreadLocal result = new ThreadLocal();
121:
122: /**
123: * Creates an instance of <code>GenericSoapClient</code>
124: * with all attributes initialized to default values.
125: */
126: public GenericSoapClient() {
127: }
128:
129: /**
130: * Get the value of endpoint.
131: * @return value of endpoint.
132: * @see #setEndpoint
133: */
134: public String getEndpoint() {
135: return endpoint;
136: }
137:
138: /**
139: * Set the value of endpoint.
140: * @param newEndpoint value to assign to endpoint.
141: * @see #getEndpoint
142: */
143: public void setEndpoint(String newEndpoint) {
144: this .endpoint = newEndpoint;
145: }
146:
147: /**
148: * Set the xml definition of output mappings. It is used to convert the
149: * WSIF invocation response.
150: *
151: * @param outputMappings the given xml as W3C Element.
152: */
153: public void setMappings(Element outputMappings) {
154: try {
155: // retrieve namespaces
156: NodeList namespacesNodeList = outputMappings
157: .getElementsByTagNameNS(XPDLUtil.XPDL_EXTN_NS,
158: "Namespaces");
159: NodeList nsNodeList = null;
160: if (namespacesNodeList.getLength() > 0) {
161: Element namespacesNode = (Element) namespacesNodeList
162: .item(0);
163: nsNodeList = namespacesNode.getElementsByTagNameNS(
164: XPDLUtil.XPDL_EXTN_NS, "Namespace");
165: }
166: NodeList paramNodeList = outputMappings
167: .getElementsByTagNameNS(XPDLUtil.XPDL_EXTN_NS,
168: "Parameter");
169: for (int i = 0; i < paramNodeList.getLength(); i++) {
170: Element param = (Element) paramNodeList.item(i);
171: String name = param.getAttribute("Name");
172: XPath xpath = new DOMXPath(param.getAttribute("Select"));
173: if (nsNodeList != null) {
174: for (int j = 0; j < nsNodeList.getLength(); j++) {
175: Element ns = (Element) nsNodeList.item(j);
176: String prefix = ns.getAttribute("Prefix");
177: String uri = ns.getAttribute("Uri");
178: xpath.addNamespace(prefix, uri);
179: }
180: }
181: returnParamInfo.put(name, xpath);
182: }
183: } catch (Exception e) {
184: // if any error ocurred, outputMappings is still null.
185: logger
186: .error("error in setting XML for output mappings!",
187: e);
188: }
189: }
190:
191: // Implementation of de.danet.an.workflow.spis.aii.ToolAgent
192:
193: /* Comment copied from interface. */
194: public void invoke(Activity activity, FormalParameter[] formPars,
195: Map map) throws RemoteException, CannotExecuteException {
196: try {
197: SAXEventBuffer headerContent = (SAXEventBuffer) map
198: .get("header");
199: SAXEventBuffer bodyContent = (SAXEventBuffer) map
200: .get("body");
201:
202: SOAPConnectionFactory factory = SOAPConnectionFactory
203: .newInstance();
204: SOAPConnection connection = factory.createConnection();
205:
206: MessageFactory messageFactory = MessageFactory
207: .newInstance();
208: SOAPMessage message = messageFactory.createMessage();
209: SOAPEnvelope env = message.getSOAPPart().getEnvelope();
210:
211: if (headerContent != null) {
212: SOAPHeader hdr = env.getHeader();
213: appendNodes(env, hdr, headerContent);
214: }
215:
216: SOAPBody body = env.getBody();
217: appendNodes(env, body, bodyContent);
218:
219: SOAPMessage response = null;
220: try {
221: response = connection.call(message, new URL(endpoint));
222: } catch (SOAPException e) {
223: if (e.getMessage().indexOf("ConnectException") >= 0) {
224: // Axis way of reporting this
225: if (logger.isDebugEnabled()) {
226: logger.debug(
227: "Cannot invoke (signalled ConnectException): "
228: + e.getMessage(), e);
229: }
230: throw new CannotExecuteException(
231: "Assuming connection failure: "
232: + e.getMessage(),
233: new ConnectException(e.getMessage(), e));
234: } else if (e.getCause() != null
235: && e.getCause().getCause() != null
236: && (e.getCause().getCause() instanceof java.net.ConnectException)) {
237: // JBossWS way of reporting this
238: throw new CannotExecuteException(
239: "Assuming connection failure: "
240: + e.getMessage(),
241: new ConnectException(e.getMessage(), e));
242: }
243: throw e;
244: } finally {
245: connection.close();
246: }
247: SOAPPart respPart = response.getSOAPPart();
248:
249: Map resData = new HashMap();
250: for (int i = 0; i < formPars.length; i++) {
251: if (formPars[i].mode() == FormalParameter.Mode.IN) {
252: continue;
253: }
254: XPath path = (XPath) returnParamInfo.get(formPars[i]
255: .id());
256: if (path == null) {
257: resData.put(formPars[i].id(), convertToSax(respPart
258: .getDocumentElement()));
259: } else {
260: if (formPars[i].type().equals(String.class)) {
261: resData.put(formPars[i].id(), path
262: .stringValueOf(respPart));
263: } else if ((formPars[i].type() instanceof Class)
264: && Number.class
265: .isAssignableFrom((Class) formPars[i]
266: .type())) {
267: Number n = path.numberValueOf(respPart);
268: if (formPars[i].type().equals(Long.class)
269: && !(n instanceof Long)) {
270: n = new Long(n.longValue());
271: }
272: resData.put(formPars[i].id(), n);
273: } else {
274: resData.put(formPars[i].id(), convertToSax(path
275: .selectNodes(respPart)));
276: }
277: }
278: }
279: result.set(resData);
280: } catch (CannotExecuteException e) {
281: throw e;
282: } catch (MalformedURLException e) {
283: throw new CannotExecuteException(e.getMessage(), e);
284: } catch (SOAPException e) {
285: throw new CannotExecuteException(e.getMessage(), e);
286: } catch (SAXException e) {
287: throw new CannotExecuteException(e.getMessage(), e);
288: } catch (JaxenException e) {
289: throw new CannotExecuteException(e.getMessage(), e);
290: } catch (TransformerException e) {
291: throw new CannotExecuteException(e.getMessage(), e);
292: }
293: }
294:
295: private SAXEventBuffer convertToSax(Node value)
296: throws TransformerException {
297: TransformerFactory tf = TransformerFactory.newInstance();
298: Transformer t = tf.newTransformer();
299: SAXEventBufferImpl res = new SAXEventBufferImpl();
300: t.transform(new DOMSource(value), new SAXResult(res));
301: return res;
302: }
303:
304: private SAXEventBuffer convertToSax(List nodes)
305: throws TransformerException {
306: Document doc = W3CDomUtil.createNewDocument();
307: DocumentFragment frag = doc.createDocumentFragment();
308: for (Iterator i = nodes.iterator(); i.hasNext();) {
309: Element e = (Element) i.next();
310: frag.appendChild(doc.importNode(e, true));
311: }
312: return convertToSax(frag);
313: }
314:
315: private void appendNodes(SOAPEnvelope env, SOAPElement root,
316: SAXEventBuffer data) throws SAXException {
317: HandlerStack hs = new HandlerStack(new SOAPBuilder(root));
318: hs.setContextData("envelope", env);
319: data.emit(hs.contentHandler());
320: }
321:
322: /**
323: * Return the requested type for XML arguments.
324: * @return one of <code>XML_AS_W3C_DOM</code>,
325: * <code>XML_AS_JDOM</code> or <code>XML_AS_SAX</code>
326: */
327: public int requestedXMLArgumentType() {
328: return XMLArgumentTypeProvider.XML_AS_SAX;
329: }
330:
331: /**
332: * Return the result evaluated during {@link ToolAgent#invoke
333: * <code>invoke</code>}. The method will only be called once after
334: * each invoke, i.e. the attribute holding the result be be
335: * cleared in this method.
336: *
337: * @return the result data or <code>null</code> if the invocation
338: * does not return any data.
339: */
340: public Object result() {
341: Object res = result.get();
342: result.set(null);
343: return res;
344: }
345:
346: /* Comment copied from interface. */
347: public void terminate(Activity activity)
348: throws ApplicationNotStoppedException {
349: throw new ApplicationNotStoppedException(
350: "Terminate not implemented for GenericSoapClient.");
351: }
352: }
|