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.client.api.repository;
046:
047: import org.apache.commons.logging.Log;
048: import org.apache.commons.logging.LogFactory;
049: import org.obe.XMLException;
050: import org.obe.util.SchemaUtils;
051: import org.obe.util.W3CNames;
052: import org.obe.xpdl.model.data.*;
053: import org.xml.sax.EntityResolver;
054: import org.xml.sax.InputSource;
055: import org.xml.sax.SAXException;
056:
057: import javax.wsdl.*;
058: import javax.wsdl.extensions.ExtensibilityElement;
059: import javax.wsdl.extensions.soap.SOAPAddress;
060: import javax.wsdl.extensions.soap.SOAPBinding;
061: import javax.wsdl.extensions.soap.SOAPOperation;
062: import javax.wsdl.factory.WSDLFactory;
063: import javax.wsdl.xml.WSDLReader;
064: import javax.xml.namespace.QName;
065: import java.io.IOException;
066: import java.io.Reader;
067: import java.util.*;
068:
069: /**
070: * Describes a tool implemented by a Web Service.
071: *
072: * @author Adrian Price
073: */
074: public final class WebServiceMetaData extends ToolAgentMetaData {
075: private static final long serialVersionUID = -285543509428840735L;
076: private static final Log _logger = LogFactory
077: .getLog(WebServiceMetaData.class);
078: private static final String WSDL_OP_OVERLOADED = "xref=\"[service.]operation[(input, output)]\" must be specified if "
079: + "operation is overloaded or offered by multiple services";
080: private static final String IMPL_CLASS = "org.obe.runtime.tool.WebService";
081: private static final String[] IMPL_CTOR_SIG = { WebServiceMetaData.class
082: .getName() };
083:
084: private String _wsdlLocation;
085: private boolean _authenticate;
086: private String _userName;
087: private String _password;
088: private String _portName;
089: private String _portLocation;
090: private String _portTypeName;
091: private String _operationStyle;
092: private String _targetNamespace;
093: private String _serviceName;
094: private String _operationName;
095: private String _soapAction;
096: private Message _inputMessage = new Message();
097: private Message _outputMessage = new Message();
098:
099: // private QName _serviceQName;
100: // private QName _portQName;
101: // private QName _operationQName;
102:
103: /**
104: * No-args constructor JavaBeans compliance.
105: */
106: public WebServiceMetaData() {
107: }
108:
109: /**
110: * Constructor for dynamically defining a Web Service.
111: *
112: * @param id
113: * @param threadsafe
114: * @param wsdlLocation
115: * @param authenticate
116: * @param userName
117: * @param password
118: * @param targetNamespace
119: * @param serviceName
120: * @param operationName
121: * @param inputMessage
122: * @param outputMessage
123: */
124: public WebServiceMetaData(String id, boolean threadsafe,
125: String wsdlLocation, boolean authenticate, String userName,
126: String password, String targetNamespace,
127: String serviceName, String operationName,
128: String inputMessage, String outputMessage) {
129:
130: super (id, null, null, null, null, threadsafe);
131: _wsdlLocation = wsdlLocation;
132: _authenticate = authenticate;
133: _userName = userName;
134: _password = password;
135: _targetNamespace = targetNamespace;
136: _serviceName = serviceName;
137: _operationName = operationName;
138: _inputMessage.name = inputMessage;
139: _outputMessage.name = outputMessage;
140: }
141:
142: public String getWsdlLocation() {
143: return _wsdlLocation != null ? _wsdlLocation : _type == null
144: || !allowInheritance ? null
145: : ((WebServiceMetaData) _type).getWsdlLocation();
146: }
147:
148: public void setWsdlLocation(String wsdlLocation) {
149: _wsdlLocation = wsdlLocation;
150: }
151:
152: public boolean isAuthenticate() {
153: return _authenticate;
154: }
155:
156: public boolean getAuthenticate() {
157: return _authenticate;
158: }
159:
160: public void setAuthenticate(boolean authenticate) {
161: _authenticate = authenticate;
162: }
163:
164: public String getUserName() {
165: return _userName != null ? _userName : _type == null
166: || !allowInheritance ? null
167: : ((WebServiceMetaData) _type).getUserName();
168: }
169:
170: public void setUserName(String userName) {
171: _userName = userName;
172: }
173:
174: public String getPassword() {
175: return _password != null ? _password : _type == null
176: || !allowInheritance ? null
177: : ((WebServiceMetaData) _type).getPassword();
178: }
179:
180: public void setPassword(String password) {
181: _password = password;
182: }
183:
184: public String getTargetNamespace() {
185: return _targetNamespace != null ? _targetNamespace
186: : _type == null || !allowInheritance ? null
187: : ((WebServiceMetaData) _type)
188: .getTargetNamespace();
189: }
190:
191: public void setTargetNamespace(String targetNamespace) {
192: _targetNamespace = targetNamespace;
193: }
194:
195: public String getPortLocation() {
196: return _portLocation != null ? _portLocation : _type == null
197: || !allowInheritance ? null
198: : ((WebServiceMetaData) _type).getPortLocation();
199: }
200:
201: public void setPortLocation(String portLocation) {
202: _portLocation = portLocation;
203: }
204:
205: public String getPortName() {
206: return _portName != null ? _portName : _type == null
207: || !allowInheritance ? null
208: : ((WebServiceMetaData) _type).getPortName();
209: }
210:
211: public void setPortName(String portName) {
212: _portName = portName;
213: }
214:
215: public String getPortTypeName() {
216: return _portTypeName != null ? _portTypeName : _type == null
217: || !allowInheritance ? null
218: : ((WebServiceMetaData) _type).getPortTypeName();
219: }
220:
221: public void setPortTypeName(String portTypeName) {
222: _portTypeName = portTypeName;
223: }
224:
225: public String getOperationStyle() {
226: return _operationStyle != null ? _operationStyle
227: : _type == null || !allowInheritance ? null
228: : ((WebServiceMetaData) _type)
229: .getOperationStyle();
230: }
231:
232: public void setOperationStyle(String operationStyle) {
233: _operationStyle = operationStyle;
234: }
235:
236: public String getServiceName() {
237: return _serviceName != null ? _serviceName : _type == null
238: || !allowInheritance ? null
239: : ((WebServiceMetaData) _type).getServiceName();
240: }
241:
242: public void setServiceName(String serviceName) {
243: _serviceName = serviceName;
244: }
245:
246: public String getOperationName() {
247: return _operationName != null ? _operationName : _type == null
248: || !allowInheritance ? null
249: : ((WebServiceMetaData) _type).getOperationName();
250: }
251:
252: public void setOperationName(String operationName) {
253: _operationName = operationName;
254: }
255:
256: public String getSoapAction() {
257: return _soapAction != null ? _soapAction : _type == null
258: || !allowInheritance ? null
259: : ((WebServiceMetaData) _type).getSoapAction();
260: }
261:
262: public void setSoapAction(String soapAction) {
263: _soapAction = soapAction;
264: }
265:
266: public Message getInputMessage() {
267: return _inputMessage != null ? _inputMessage : _type == null
268: || !allowInheritance ? null
269: : ((WebServiceMetaData) _type).getInputMessage();
270: }
271:
272: public void setInputMessage(Message inputMessage) {
273: _inputMessage = inputMessage;
274: }
275:
276: public Message getOutputMessage() {
277: return _outputMessage != null ? _outputMessage : _type == null
278: || !allowInheritance ? null
279: : ((WebServiceMetaData) _type).getOutputMessage();
280: }
281:
282: public void setOutputMessage(Message outputMessage) {
283: _outputMessage = outputMessage;
284: }
285:
286: public Object createInstance(EntityResolver entityResolver)
287: throws RepositoryException {
288:
289: // If we haven't yet introspected the WSDL, we must do this before
290: // instantiating a tool agent.
291: if (_formalParameters == null) {
292: introspect(entityResolver);
293: }
294:
295: return createInstance(new Object[] { this });
296: }
297:
298: protected String getImplClass() {
299: return IMPL_CLASS;
300: }
301:
302: protected String[] getImplCtorSig() {
303: return IMPL_CTOR_SIG;
304: }
305:
306: public ToolAgentMetaData introspect(ExternalReference extRef,
307: EntityResolver entityResolver) throws RepositoryException {
308:
309: WebServiceMetaData metaData = null;
310: String location = extRef.getLocation();
311: if (location.endsWith(".wsdl") || location.endsWith("?wsdl")) {
312: // Parse xref, which is of form: [service.]operation[(input, output)].
313: // In order to construct a valid QName for the service, the
314: // ExternalReference namespace attribute must be set correctly.
315: String namespace = extRef.getNamespace();
316: String service = null;
317: String xref = extRef.getXref();
318: String operation = xref;
319: int opNameIndex = xref.indexOf('.');
320: if (opNameIndex != -1) {
321: service = xref.substring(0, opNameIndex);
322: operation = xref.substring(opNameIndex + 1);
323: }
324: String inputMessage = null;
325: String outputMessage = null;
326: boolean inOutSpecified = operation.indexOf('(') != -1;
327: if (inOutSpecified) {
328: StringTokenizer strtok = new StringTokenizer(operation,
329: "(, )");
330: if (strtok.countTokens() != 3) {
331: throw new IllegalArgumentException(
332: WSDL_OP_OVERLOADED);
333: }
334: operation = strtok.nextToken();
335: inputMessage = strtok.nextToken();
336: outputMessage = strtok.nextToken();
337: }
338:
339: // Create a new meta-data object and customize it to this WSDL,
340: // inheriting this template object's security settings.
341: metaData = new WebServiceMetaData(extRef.toString(),
342: _threadsafe, location, _authenticate, _userName,
343: _password, namespace, service, operation,
344: inputMessage, outputMessage);
345: metaData.introspect(entityResolver);
346: }
347: return metaData;
348: }
349:
350: private synchronized void introspect(EntityResolver entityResolver)
351: throws RepositoryException {
352:
353: if (_logger.isDebugEnabled()) {
354: _logger.debug("introspect: id=" + getId());
355: }
356:
357: InputSource in = null;
358: try {
359: // Load and parse the WSDL.
360: in = entityResolver.resolveEntity(null, _wsdlLocation);
361: WSDLFactory factory = WSDLFactory.newInstance();
362: WSDLReader reader = factory.newWSDLReader();
363: Definition definition = reader.readWSDL(_wsdlLocation, in);
364:
365: // Search the WSDL for a port that specifies a SOAP address, and
366: // which is bound to a port type that supports the required
367: // operation. If more than one service offers the operation, the
368: // operation name must be fully qualified in the xref thus:
369: // [service.]operation[(inputMessage, outputMessage)]
370: _portLocation = null;
371: _portTypeName = null;
372: QName svcQName = _serviceName == null ? null : new QName(
373: _targetNamespace, _serviceName);
374: boolean ioSpecified = _inputMessage.name != null
375: && _outputMessage.name != null;
376: Operation operation = null;
377: boolean operationFound = false;
378: Map services = definition.getServices();
379: for (Iterator iter = services.entrySet().iterator(); iter
380: .hasNext();) {
381: Map.Entry entry = (Map.Entry) iter.next();
382: Service svc = (Service) entry.getValue();
383:
384: // If we know the service name, only proceed if it matches.
385: if (svcQName != null
386: && !svcQName.equals(svc.getQName())) {
387: continue;
388: }
389:
390: // _serviceQName = svc.getQName();
391:
392: // Look for a port that has a SOAP address.
393: Map ports = svc.getPorts();
394: for (Iterator iter2 = ports.values().iterator(); iter2
395: .hasNext();) {
396: Port port = (Port) iter2.next();
397: List portEE = port.getExtensibilityElements();
398: String portLoc = null;
399: for (int k = 0; k < portEE.size(); k++) {
400: ExtensibilityElement ee = (ExtensibilityElement) portEE
401: .get(k);
402: if (ee instanceof SOAPAddress) {
403: portLoc = ((SOAPAddress) ee)
404: .getLocationURI();
405: break;
406: }
407: }
408: if (portLoc != null) {
409: Binding binding = port.getBinding();
410: List bindingEE = binding
411: .getExtensibilityElements();
412: for (int k = 0; k < bindingEE.size(); k++) {
413: ExtensibilityElement ee = (ExtensibilityElement) bindingEE
414: .get(k);
415: if (ee instanceof SOAPBinding) {
416: _operationStyle = ((SOAPBinding) ee)
417: .getStyle();
418: break;
419: }
420: }
421: PortType pType = binding.getPortType();
422: List operations = pType.getOperations();
423: for (int k = 0; k < operations.size(); k++) {
424: Operation op = (Operation) operations
425: .get(k);
426: if (op.getName().equals(_operationName)) {
427: if (!ioSpecified
428: || op
429: .getInput()
430: .getName()
431: .equals(
432: _inputMessage.name)
433: && op
434: .getOutput()
435: .getName()
436: .equals(
437: _outputMessage.name)) {
438:
439: // _portQName = port.getName();
440: // _operationQName = op.getName();
441:
442: // If we were passed an unqualified WSDL
443: // operation name (i.e., one that doesn't
444: // specify the input and output messages),
445: // this is ambiguous if the operation is
446: // overloaded. OR if we were passed one that
447: // doesn't specify the service name, this is
448: // ambiguous if the operation is provided
449: // by more than one service.
450: if (operationFound) {
451: throw new IllegalArgumentException(
452: WSDL_OP_OVERLOADED);
453: }
454: operationFound = true;
455:
456: // Save service invocation information.
457: if (_serviceName == null) {
458: _serviceName = svc.getQName()
459: .getLocalPart();
460: }
461: operation = op;
462: _portName = port.getName();
463: _portLocation = portLoc;
464: _portTypeName = pType.getQName()
465: .getLocalPart();
466:
467: // See if there's an associated SOAP action.
468: BindingOperation bOp = port
469: .getBinding()
470: .getBindingOperation(
471: _operationName,
472: _inputMessage.name,
473: _outputMessage.name);
474: List bOpEE = bOp
475: .getExtensibilityElements();
476: for (int m = 0; m < bOpEE.size(); m++) {
477: ExtensibilityElement ee = (ExtensibilityElement) bOpEE
478: .get(m);
479: if (ee instanceof SOAPOperation) {
480: _soapAction = ((SOAPOperation) ee)
481: .getSoapActionURI();
482: break;
483: }
484: }
485:
486: if (ioSpecified) {
487: break;
488: }
489: }
490: }
491: }
492: }
493: }
494: }
495:
496: if (!operationFound) {
497: throw new IllegalArgumentException(
498: "Undefined SOAP operation: " + _operationName
499: + " in namespace: " + _targetNamespace);
500: }
501:
502: // We found a matching service.operation - introspect its messages.
503: List formalParameters = new ArrayList();
504: introspectSOAPMessage(operation.getInput().getMessage(),
505: _inputMessage, ParameterMode.IN, formalParameters);
506:
507: Output output = operation.getOutput();
508: if (output != null) {
509: introspectSOAPMessage(output.getMessage(),
510: _outputMessage, ParameterMode.OUT,
511: formalParameters);
512: }
513: _formalParameters = (FormalParameter[]) formalParameters
514: .toArray(new FormalParameter[formalParameters
515: .size()]);
516:
517: if (_logger.isDebugEnabled()) {
518: _logger.debug("introspect: formalParms="
519: + formalParameters);
520: }
521: } catch (IOException e) {
522: throw new RepositoryException(e);
523: } catch (SAXException e) {
524: throw new RepositoryException(e);
525: } catch (XMLException e) {
526: throw new RepositoryException(e);
527: } catch (WSDLException e) {
528: throw new RepositoryException(e);
529: } finally {
530: try {
531: SchemaUtils.close(in);
532: } catch (IOException e) {
533: // Don't throw an exception, there may already be one pending.
534: }
535: }
536: }
537:
538: private void introspectSOAPMessage(javax.wsdl.Message wsdlMessage,
539: Message message, ParameterMode parameterMode,
540: List formalParameters) throws XMLException {
541:
542: List wsdlParts = wsdlMessage.getOrderedParts(null);
543: List parts = new ArrayList();
544: for (int i = 0, n = wsdlParts.size(); i < n; i++) {
545: javax.wsdl.Part wsdlPart = (javax.wsdl.Part) wsdlParts
546: .get(i);
547:
548: DataType dataType;
549:
550: // Is this one of the element types defined in the WSDL?
551: QName qname = wsdlPart.getElementName();
552: String partName = wsdlPart.getName();
553: if (qname == null) {
554: // No: is the type defined by XML Schema?
555: qname = wsdlPart.getTypeName();
556: if (qname.getNamespaceURI().equals(W3CNames.XSD_NS_URI)) {
557: // Map XML Schema type to XPDL type.
558: Class javaClass = SchemaUtils.classForSchemaType(
559: (Reader) null, qname);
560: dataType = DataTypes.dataTypeForClass(javaClass);
561: } else {
562: // TODO: How to ensure the ext-ref location is correct?
563: // No - it's defined in some other namespace.
564: dataType = new DataType(new ExternalReference(
565: _wsdlLocation, qname.getLocalPart(), qname
566: .getNamespaceURI()));
567: }
568: } else {
569: // Yes - so use an external reference.
570: dataType = new DataType(new ExternalReference(
571: _wsdlLocation, partName, qname
572: .getNamespaceURI()));
573: }
574: parts.add(new Part(partName, qname.getNamespaceURI(), qname
575: .getLocalPart()));
576:
577: formalParameters.add(new FormalParameter(partName, null,
578: parameterMode, dataType, null));
579: }
580: message.name = wsdlMessage.getQName().getLocalPart();
581: message.parts = (Part[]) parts.toArray(new Part[parts.size()]);
582: }
583: }
|