001: /*--
002:
003: Copyright (C) 2002-2005 Adrian Price.
004: All rights reserved.
005:
006: Redistribution and use in source and binary forms, with or without
007: modification, are permitted provided that the following conditions
008: are met:
009:
010: 1. Redistributions of source code must retain the above copyright
011: notice, this list of conditions, and the following disclaimer.
012:
013: 2. Redistributions in binary form must reproduce the above copyright
014: notice, this list of conditions, and the disclaimer that follows
015: these conditions in the documentation and/or other materials
016: provided with the distribution.
017:
018: 3. The names "OBE" and "Open Business Engine" must not be used to
019: endorse or promote products derived from this software without prior
020: written permission. For written permission, please contact
021: adrianprice@sourceforge.net.
022:
023: 4. Products derived from this software may not be called "OBE" or
024: "Open Business Engine", nor may "OBE" or "Open Business Engine"
025: appear in their name, without prior written permission from
026: Adrian Price (adrianprice@users.sourceforge.net).
027:
028: THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
029: WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
030: OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
031: DISCLAIMED. IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT,
032: INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
033: (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
034: SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
035: HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
036: STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
037: IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
038: POSSIBILITY OF SUCH DAMAGE.
039:
040: For more information on OBE, please see
041: <http://obe.sourceforge.net/>.
042:
043: */
044:
045: package org.obe.runtime.tool;
046:
047: import org.apache.axis.constants.Style;
048: import org.apache.axis.message.SOAPBodyElement;
049: import org.apache.commons.logging.Log;
050: import org.apache.commons.logging.LogFactory;
051: import org.obe.client.api.repository.Message;
052: import org.obe.client.api.repository.Part;
053: import org.obe.client.api.repository.WebServiceMetaData;
054: import org.obe.client.api.tool.Parameter;
055: import org.obe.client.api.tool.ToolInvocation;
056: import org.obe.xpdl.model.data.ParameterMode;
057: import org.w3c.dom.Document;
058:
059: import javax.xml.namespace.QName;
060: import javax.xml.rpc.*;
061: import java.lang.reflect.InvocationTargetException;
062: import java.net.MalformedURLException;
063: import java.net.URL;
064: import java.rmi.RemoteException;
065: import java.util.*;
066:
067: /**
068: * A tool agent that invokes a Web Service.
069: *
070: * @author Adrian Price
071: */
072: public class WebService extends AbstractToolAgent {
073: private static final long serialVersionUID = 6138938392921161335L;
074: private static final Log _logger = LogFactory
075: .getLog(WebService.class);
076: private static final javax.xml.rpc.ParameterMode[] _soapParmModes = {
077: javax.xml.rpc.ParameterMode.IN,
078: javax.xml.rpc.ParameterMode.OUT,
079: javax.xml.rpc.ParameterMode.INOUT };
080:
081: private final WebServiceMetaData _metadata;
082:
083: public WebService(WebServiceMetaData metadata) {
084: _metadata = metadata;
085: }
086:
087: protected int _invokeApplication(ToolInvocation ti)
088: throws InvocationTargetException {
089:
090: try {
091: // Prepare the SOAP service call.
092: if (_logger.isDebugEnabled())
093: _logger.debug("Preparing SOAP Service Call");
094:
095: Service service = ServiceFactory.newInstance()
096: .createService(
097: new URL(_metadata.getWsdlLocation()),
098: new QName(_metadata.getTargetNamespace(),
099: _metadata.getServiceName()));
100: Call call = service.createCall(new QName(_metadata
101: .getTargetNamespace(), _metadata.getPortName()),
102: new QName(_metadata.getTargetNamespace(), _metadata
103: .getOperationName()));
104:
105: // Extract the input parameters.
106: List args = new ArrayList(ti.parameters.length);
107: for (int i = 0; i < ti.parameters.length; i++) {
108: Parameter parm = ti.parameters[i];
109: if (parm.getMode() != ParameterMode.OUT) {
110: Object value = parm.getValue();
111: if (value instanceof Document) {
112: value = new SOAPBodyElement(((Document) value)
113: .getDocumentElement());
114: }
115: args.add(value);
116: }
117: }
118:
119: if (_logger.isDebugEnabled())
120: _logger.debug("Invoking SOAP call");
121:
122: // Invoke the Web Service.
123: _status = ACTIVE;
124: Object result = call.invoke(args.toArray());
125: if (_logger.isDebugEnabled())
126: _logger.debug("SOAP call returned " + result);
127:
128: Map outputs;
129:
130: // Ugh! Must use axis getter for now.
131: // See bug report http://issues.apache.org/jira/browse/AXIS-2209.
132: // if (call.getProperty(Call.OPERATION_STYLE_PROPERTY).equals("document")) {
133: if (((org.apache.axis.client.Call) call)
134: .getOperationStyle().equals(Style.DOCUMENT)) {
135:
136: // We only get a result if the web service has output messages.
137: if (result != null) {
138: Collection results = (Collection) result;
139: outputs = new HashMap();
140: for (Iterator iter = results.iterator(); iter
141: .hasNext();) {
142: SOAPBodyElement element = (SOAPBodyElement) iter
143: .next();
144: outputs.put(element.getName(), element
145: .getObjectValue());
146: }
147: } else {
148: outputs = Collections.EMPTY_MAP;
149: }
150: } else {
151: // Extract the return values.
152: outputs = call.getOutputParams();
153: }
154:
155: for (int i = 0; i < ti.parameters.length; i++) {
156: Parameter parm = ti.parameters[i];
157: if (parm.getMode() != ParameterMode.IN) {
158: parm.setValue(outputs.get(parm.getFormalParm()
159: .getId()));
160: if (_logger.isDebugEnabled()) {
161: _logger.debug("Set output parameter '"
162: + parm.getFormalParm().getId()
163: + "' to: " + parm.getValue());
164: }
165: }
166: }
167:
168: return 0;
169: } catch (RemoteException e) {
170: throw new InvocationTargetException(e);
171: } catch (ServiceException e) {
172: throw new InvocationTargetException(e.getCause());
173: } catch (MalformedURLException e) {
174: throw new InvocationTargetException(e);
175: }
176: }
177:
178: protected int _invokeApplicationDirect(Parameter[] parameters)
179: throws InvocationTargetException {
180:
181: if (_logger.isDebugEnabled())
182: _logger.debug("Preparing SOAP Service Call");
183:
184: // Prepare the SOAP service call.
185: Service svc;
186: Call call;
187: try {
188: svc = ServiceFactory.newInstance().createService(
189: new QName(_metadata.getTargetNamespace(), _metadata
190: .getServiceName()));
191: call = svc.createCall();
192: } catch (ServiceException e) {
193: throw new InvocationTargetException(e);
194: }
195: call.setTargetEndpointAddress(_metadata.getPortLocation());
196:
197: // TODO: Is it correct to use the targetNamespace for the operation name?
198: QName opnQName = new QName(_metadata.getTargetNamespace(),
199: _metadata.getOperationName());
200: call.setOperationName(opnQName);
201: // TODO: Is it correct to use the targetNamespace for the port type name?
202: call.setPortTypeName(new QName(_metadata.getTargetNamespace(),
203: _metadata.getPortTypeName()));
204: call.setProperty(Call.OPERATION_STYLE_PROPERTY, _metadata
205: .getOperationStyle());
206: if (_metadata.getSoapAction() != null) {
207: call.setProperty(Call.SOAPACTION_URI_PROPERTY, _metadata
208: .getSoapAction());
209: call
210: .setProperty(Call.SOAPACTION_USE_PROPERTY,
211: Boolean.TRUE);
212: }
213: boolean parmsAndRetnSpecReq = call
214: .isParameterAndReturnSpecRequired(opnQName);
215:
216: if (_metadata.getAuthenticate()) {
217: call.setProperty(Call.USERNAME_PROPERTY, _metadata
218: .getUserName());
219: call.setProperty(Call.PASSWORD_PROPERTY, _metadata
220: .getPassword());
221: }
222:
223: if (_logger.isDebugEnabled())
224: _logger.debug("Created SOAP Service Call");
225:
226: // TODO: NOT THREADSAFE!!! Create Messages, don't use metaData version.
227:
228: // Define and set the parameters.
229: int inPartCount = 0;
230: int outPartCount = 0;
231: List argList = new ArrayList();
232: boolean resultTypeSet = false;
233: for (int i = 0; i < parameters.length; i++) {
234: Parameter parm = parameters[i];
235: String formalParmId = parm.getFormalParm().getId();
236: boolean inParm = parm.getMode() != ParameterMode.OUT;
237: Message message = inParm ? _metadata.getInputMessage()
238: : _metadata.getOutputMessage();
239: QName partTypeName = null;
240: Part[] parts = message.parts;
241: for (int k = 0; k < parts.length; k++) {
242: Part part = parts[k];
243: if (part.name.equals(formalParmId)) {
244: partTypeName = new QName(part.typeNamespaceURI,
245: part.typeName);
246: break;
247: }
248: }
249: if (partTypeName == null) {
250: throw new IllegalArgumentException(parm.getMode()
251: + " parameter '" + formalParmId
252: + "' is not part of the SOAP input message ");
253: }
254:
255: // Map XPDL parameter mode to SOAP parameter mode.
256: javax.xml.rpc.ParameterMode mode = _soapParmModes[parm
257: .getMode().value()];
258:
259: try {
260: if (inParm) {
261: inPartCount++;
262: argList.add(parm.getValue());
263: call.addParameter(formalParmId, partTypeName, parm
264: .getValue() == null ? null : parm
265: .getValue().getClass(), mode);
266: } else {
267: outPartCount++;
268:
269: // TODO: Investigate whether to do our own type conversion.
270: call.addParameter(formalParmId, partTypeName, parm
271: .getFormalClass(), mode);
272:
273: // If the call requires a return type, specify the first output
274: // parameter we encounter.
275: if (!resultTypeSet) {
276: // TODO: Investigate whether to do our own type conversion.
277: call.setReturnType(partTypeName, parm
278: .getFormalClass());
279: resultTypeSet = true;
280:
281: if (_logger.isDebugEnabled())
282: _logger.debug("Set return type to "
283: + partTypeName);
284: }
285: }
286: } catch (JAXRPCException e) {
287: // If parameters & return specificatinos are not required,
288: // addParameter() can throw an exception which we can ignore.
289: if (parmsAndRetnSpecReq)
290: throw e;
291: }
292:
293: if (_logger.isDebugEnabled())
294: _logger.debug("Added parameter " + parm);
295: }
296:
297: // TODO: Understand whether/how to set a void return type.
298: if (parmsAndRetnSpecReq
299: && _metadata.getOutputMessage().parts.length == 0) {
300: call.setReturnType(null);
301:
302: if (_logger.isDebugEnabled())
303: _logger.debug("Set void return type");
304: }
305:
306: // Check that all input parameters have been set.
307: if (inPartCount != _metadata.getInputMessage().parts.length) {
308: throw new IllegalArgumentException(
309: "Incorrect input parameter count: expected <"
310: + _metadata.getInputMessage().parts.length
311: + "> actual <" + inPartCount + '>');
312: }
313: // If output message parts have been ignored, log the fact.
314: if (outPartCount != _metadata.getOutputMessage().parts.length) {
315: _logger.info("Output parameters ignored: expected <"
316: + _metadata.getOutputMessage().parts.length
317: + "> actual <" + outPartCount + '>');
318: }
319:
320: if (_logger.isDebugEnabled())
321: _logger.debug("Invoking SOAP call");
322:
323: // Invoke the Web Service.
324: _status = ACTIVE;
325: Object result;
326: try {
327: Object[] args = argList.toArray();
328: result = call.invoke(args);
329: } catch (RemoteException e) {
330: throw new InvocationTargetException(e);
331: }
332:
333: if (_logger.isDebugEnabled())
334: _logger.debug("SOAP call returned " + result);
335:
336: // Create the return values array.
337: Map outputs = call.getOutputParams();
338: for (int i = 0; i < parameters.length; i++) {
339: Parameter parm = parameters[i];
340: if (parm.getMode() != ParameterMode.IN) {
341: parm
342: .setValue(outputs.get(parm.getFormalParm()
343: .getId()));
344: if (_logger.isDebugEnabled()) {
345: _logger.debug("Set output parameter '"
346: + parm.getFormalParm().getId() + "' to: "
347: + parm.getValue());
348: }
349: }
350: }
351: return 0;
352: }
353: }
|