0001: /*
0002: * Copyright (c) 1998-2007 Caucho Technology -- all rights reserved
0003: *
0004: * This file is part of Resin(R) Open Source
0005: *
0006: * Each copy or derived work must preserve the copyright notice and this
0007: * notice unmodified.
0008: *
0009: * Resin Open Source is free software; you can redistribute it and/or modify
0010: * it under the terms of the GNU General Public License as published by
0011: * the Free Software Foundation; either version 2 of the License, or
0012: * (at your option) any later version.
0013: *
0014: * Resin Open Source is distributed in the hope that it will be useful,
0015: * but WITHOUT ANY WARRANTY; without even the implied warranty of
0016: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, or any warranty
0017: * of NON-INFRINGEMENT. See the GNU General Public License for more
0018: * details.
0019: *
0020: * You should have received a copy of the GNU General Public License
0021: * along with Resin Open Source; if not, write to the
0022: *
0023: * Free Software Foundation, Inc.
0024: * 59 Temple Place, Suite 330
0025: * Boston, MA 02111-1307 USA
0026: *
0027: * @author Emil Ong, Scott Ferguson
0028: */
0029:
0030: package com.caucho.soap.skeleton;
0031:
0032: import com.caucho.jaxb.JAXBContextImpl;
0033: import com.caucho.jaxb.JAXBUtil;
0034: import com.caucho.jaxb.property.Property;
0035: import com.caucho.jaxb.property.AttachmentProperty;
0036:
0037: import com.caucho.soap.jaxws.HandlerChainInvoker;
0038:
0039: import static com.caucho.soap.wsdl.WSDLConstants.*;
0040: import com.caucho.soap.wsdl.WSDLBinding;
0041: import com.caucho.soap.wsdl.WSDLBindingOperation;
0042: import com.caucho.soap.wsdl.WSDLDefinitions;
0043: import com.caucho.soap.wsdl.WSDLParser;
0044: import com.caucho.soap.wsdl.WSDLPortType;
0045:
0046: import com.caucho.util.Attachment;
0047: import com.caucho.util.AttachmentReader;
0048: import com.caucho.util.L10N;
0049:
0050: import javax.activation.DataHandler;
0051:
0052: import javax.jws.Oneway;
0053: import javax.jws.WebMethod;
0054: import javax.jws.WebParam;
0055: import javax.jws.WebResult;
0056: import javax.jws.WebService;
0057: import javax.jws.soap.SOAPBinding;
0058: import static javax.xml.XMLConstants.*;
0059: import javax.xml.bind.JAXBException;
0060: import javax.xml.bind.Marshaller;
0061: import javax.xml.bind.Unmarshaller;
0062: import javax.xml.namespace.QName;
0063: import javax.xml.soap.MessageFactory;
0064: import javax.xml.soap.SOAPException;
0065: import javax.xml.soap.SOAPFault;
0066: import javax.xml.soap.SOAPMessage;
0067: import javax.xml.stream.XMLInputFactory;
0068: import javax.xml.stream.XMLOutputFactory;
0069: import javax.xml.stream.XMLStreamException;
0070: import javax.xml.stream.XMLStreamReader;
0071: import javax.xml.stream.XMLStreamWriter;
0072: import javax.xml.transform.Source;
0073: import javax.xml.transform.dom.DOMResult;
0074: import javax.xml.transform.dom.DOMSource;
0075: import javax.xml.ws.Holder;
0076: import javax.xml.ws.WebServiceException;
0077: import static javax.xml.ws.handler.MessageContext.*;
0078: import javax.xml.ws.soap.SOAPFaultException;
0079:
0080: import java.io.ByteArrayInputStream;
0081: import java.io.ByteArrayOutputStream;
0082: import java.io.IOException;
0083: import java.io.InputStream;
0084: import java.io.OutputStream;
0085: import java.io.PrintWriter;
0086:
0087: import java.lang.annotation.Annotation;
0088: import java.lang.reflect.InvocationTargetException;
0089: import java.lang.reflect.Method;
0090: import java.lang.reflect.ParameterizedType;
0091: import java.lang.reflect.Type;
0092:
0093: import java.net.HttpURLConnection;
0094: import java.net.MalformedURLException;
0095: import java.net.URL;
0096: import java.net.URLConnection;
0097:
0098: import java.util.ArrayList;
0099: import java.util.HashMap;
0100: import java.util.List;
0101: import java.util.Map;
0102: import java.util.UUID;
0103: import java.util.logging.Level;
0104: import java.util.logging.Logger;
0105:
0106: /**
0107: * Invokes a SOAP request on a Java POJO method
0108: */
0109: public abstract class AbstractAction {
0110: private final static Logger log = Logger
0111: .getLogger(AbstractAction.class.getName());
0112: private static final L10N L = new L10N(AbstractAction.class);
0113:
0114: private static final HashMap<Method, String> _methodNames = new HashMap<Method, String>();
0115:
0116: protected static final String XML_SCHEMA_PREFIX = "xsd";
0117: protected static final String TARGET_NAMESPACE_PREFIX = "m";
0118: protected static final String SOAP_ENCODING_STYLE = "http://schemas.xmlsoap.org/soap/encoding/";
0119: public final static String SOAP_ENVELOPE_PREFIX = "soapenv";
0120: public final static String SOAP_ENVELOPE = "http://schemas.xmlsoap.org/soap/envelope/";
0121:
0122: protected static XMLOutputFactory _xmlOutputFactory;
0123: protected static XMLInputFactory _xmlInputFactory = XMLInputFactory
0124: .newInstance();
0125:
0126: protected static MessageFactory _messageFactory;
0127: protected static SOAPMessage _soapMessage;
0128:
0129: protected final Method _method;
0130: protected final int _arity;
0131: protected boolean _isOneway;
0132:
0133: protected String _responseName;
0134: protected String _operationName;
0135: protected String _portName;
0136: protected String _inputName;
0137: protected QName _requestName;
0138: protected QName _resultName;
0139:
0140: protected final HashMap<String, ParameterMarshal> _bodyArguments = new HashMap<String, ParameterMarshal>();
0141: protected final ParameterMarshal[] _bodyArgs;
0142:
0143: protected final HashMap<String, ParameterMarshal> _headerArguments = new HashMap<String, ParameterMarshal>();
0144: protected final ParameterMarshal[] _headerArgs;
0145:
0146: protected final HashMap<String, ParameterMarshal> _attachmentArguments = new HashMap<String, ParameterMarshal>();
0147: protected final ParameterMarshal[] _attachmentArgs;
0148:
0149: protected final ParameterMarshal _returnMarshal;
0150: protected final boolean _headerReturn;
0151:
0152: protected final HashMap<Class, ParameterMarshal> _faults = new HashMap<Class, ParameterMarshal>();
0153:
0154: protected final HashMap<QName, ParameterMarshal> _faultNames = new HashMap<QName, ParameterMarshal>();
0155:
0156: protected int _attachmentInputs;
0157: protected int _headerInputs;
0158: protected int _bodyInputs;
0159:
0160: protected int _attachmentOutputs;
0161: protected int _headerOutputs;
0162: protected int _bodyOutputs;
0163:
0164: protected final JAXBContextImpl _jaxbContext;
0165: protected final String _targetNamespace;
0166: protected final String _soapAction;
0167:
0168: protected WSDLDefinitions _wsdl;
0169: protected WSDLBindingOperation _bindingOperation;
0170:
0171: protected static XMLOutputFactory getXMLOutputFactory() {
0172: if (_xmlOutputFactory == null) {
0173: _xmlOutputFactory = XMLOutputFactory.newInstance();
0174: _xmlOutputFactory.setProperty(
0175: XMLOutputFactory.IS_REPAIRING_NAMESPACES,
0176: Boolean.TRUE);
0177: }
0178:
0179: return _xmlOutputFactory;
0180: }
0181:
0182: protected static SOAPFault createSOAPFault() throws SOAPException {
0183: if (_messageFactory == null)
0184: _messageFactory = MessageFactory.newInstance(); // XXX protocol
0185:
0186: if (_soapMessage == null)
0187: _soapMessage = _messageFactory.createMessage();
0188:
0189: _soapMessage.getSOAPBody().removeContents();
0190:
0191: return _soapMessage.getSOAPBody().addFault();
0192: }
0193:
0194: protected AbstractAction(Method method, Method eiMethod,
0195: JAXBContextImpl jaxbContext, String targetNamespace,
0196: WSDLDefinitions wsdl, Marshaller marshaller,
0197: Unmarshaller unmarshaller) throws JAXBException,
0198: WebServiceException {
0199: _method = method;
0200: _arity = _method.getParameterTypes().length;
0201: _jaxbContext = jaxbContext;
0202: _targetNamespace = targetNamespace; // XXX introspect this from the method
0203: _isOneway = (method.getAnnotation(Oneway.class) != null);
0204:
0205: // set the names for the input/output messages, portType/operation, and
0206: // binding/operation.
0207: _operationName = getWebMethodName(method, eiMethod);
0208: _portName = getPortName(method, eiMethod);
0209: _inputName = _operationName;
0210: _responseName = _operationName + "Response";
0211: _soapAction = getSOAPAction(method, eiMethod);
0212:
0213: //
0214: // Arguments
0215: //
0216:
0217: _wsdl = wsdl;
0218:
0219: if (_wsdl != null) {
0220: for (WSDLBinding binding : _wsdl.getBindings()) {
0221: WSDLPortType portType = binding.getPortType();
0222:
0223: if (portType != null
0224: && portType.getName().equals(_portName)) {
0225: for (WSDLBindingOperation operation : binding
0226: .getOperations()) {
0227: if (operation.getName().equals(_operationName)) {
0228: _bindingOperation = operation;
0229: break;
0230: }
0231: }
0232: }
0233: }
0234: }
0235:
0236: Class[] params = method.getParameterTypes();
0237: Type[] genericParams = method.getGenericParameterTypes();
0238: Annotation[][] paramAnn = method.getParameterAnnotations();
0239:
0240: Annotation[][] eiParamAnn = null;
0241:
0242: if (eiMethod != null)
0243: eiParamAnn = eiMethod.getParameterAnnotations();
0244:
0245: ArrayList<ParameterMarshal> attachmentList = new ArrayList<ParameterMarshal>();
0246: ArrayList<ParameterMarshal> headerList = new ArrayList<ParameterMarshal>();
0247: ArrayList<ParameterMarshal> bodyList = new ArrayList<ParameterMarshal>();
0248:
0249: for (int i = 0; i < params.length; i++) {
0250: boolean isHeader = false;
0251: boolean isAttachment = false;
0252:
0253: String localName = "arg" + i; // As per JAX-WS spec
0254:
0255: QName name = null;
0256: WebParam.Mode mode = WebParam.Mode.IN;
0257: WebParam webParam = null;
0258:
0259: for (Annotation ann : paramAnn[i]) {
0260: if (ann instanceof WebParam) {
0261: webParam = (WebParam) ann;
0262: break;
0263: }
0264: }
0265:
0266: if (webParam == null && eiParamAnn != null) {
0267: for (Annotation ann : eiParamAnn[i]) {
0268: if (ann instanceof WebParam) {
0269: webParam = (WebParam) ann;
0270: break;
0271: }
0272: }
0273: }
0274:
0275: if (webParam != null) {
0276: if (!"".equals(webParam.name()))
0277: localName = webParam.name();
0278:
0279: if ("".equals(webParam.targetNamespace()))
0280: name = new QName(localName);
0281: else
0282: name = new QName(webParam.targetNamespace(),
0283: localName);
0284:
0285: if (params[i].equals(Holder.class)) {
0286: mode = webParam.mode();
0287:
0288: if (_isOneway) {
0289: throw new WebServiceException(
0290: L
0291: .l(
0292: "Method {0} annotated with @Oneway, but contains output argument",
0293: method.getName()));
0294: }
0295: }
0296:
0297: isHeader = webParam.header();
0298:
0299: if (!isHeader)
0300: isAttachment = isAttachment(webParam);
0301: } else if (params[i].equals(Holder.class)) {
0302: mode = WebParam.Mode.INOUT;
0303: }
0304:
0305: if (name == null)
0306: name = new QName(localName);
0307:
0308: Type type = JAXBUtil
0309: .getActualParameterType(genericParams[i]);
0310: Property property = _jaxbContext.createProperty(type, true);
0311:
0312: ParameterMarshal pMarshal = ParameterMarshal.create(i,
0313: property, name, mode, marshaller, unmarshaller);
0314:
0315: if (isHeader) {
0316: if (pMarshal instanceof InParameterMarshal)
0317: _headerInputs++;
0318: else if (pMarshal instanceof OutParameterMarshal)
0319: _headerOutputs++;
0320: else {
0321: _headerInputs++;
0322: _headerOutputs++;
0323: }
0324:
0325: headerList.add(pMarshal);
0326: _headerArguments.put(localName, pMarshal);
0327: } else if (isAttachment) {
0328: if (!(property instanceof AttachmentProperty))
0329: throw new WebServiceException(
0330: L
0331: .l(
0332: "Argument {0} of method {1} is of type {2}: Attachment argument types must map to base64Binary",
0333: i, method.getName(),
0334: params[i]));
0335:
0336: if (pMarshal instanceof InParameterMarshal)
0337: _attachmentInputs++;
0338: else if (pMarshal instanceof OutParameterMarshal)
0339: _attachmentOutputs++;
0340: else {
0341: _attachmentInputs++;
0342: _attachmentOutputs++;
0343: }
0344:
0345: attachmentList.add(pMarshal);
0346: _attachmentArguments.put(localName, pMarshal);
0347: } else {
0348: if (pMarshal instanceof InParameterMarshal)
0349: _bodyInputs++;
0350: else if (pMarshal instanceof OutParameterMarshal)
0351: _bodyOutputs++;
0352: else {
0353: _bodyInputs++;
0354: _bodyOutputs++;
0355: }
0356:
0357: bodyList.add(pMarshal);
0358: _bodyArguments.put(localName, pMarshal);
0359: }
0360: }
0361:
0362: _attachmentArgs = new ParameterMarshal[attachmentList.size()];
0363: attachmentList.toArray(_attachmentArgs);
0364:
0365: _headerArgs = new ParameterMarshal[headerList.size()];
0366: headerList.toArray(_headerArgs);
0367:
0368: _bodyArgs = new ParameterMarshal[bodyList.size()];
0369: bodyList.toArray(_bodyArgs);
0370:
0371: //
0372: // Return type
0373: //
0374:
0375: if (!Void.TYPE.equals(method.getReturnType())) {
0376: if (_isOneway)
0377: throw new WebServiceException(
0378: L
0379: .l(
0380: "Method {0} annotated with @Oneway, but has non-void return",
0381: method.getName()));
0382:
0383: Property property = _jaxbContext.createProperty(method
0384: .getGenericReturnType());
0385:
0386: WebResult webResult = method.getAnnotation(WebResult.class);
0387:
0388: if (webResult == null && eiMethod != null)
0389: webResult = eiMethod.getAnnotation(WebResult.class);
0390:
0391: if (webResult != null) {
0392: _headerReturn = webResult.header();
0393:
0394: String localName = webResult.name();
0395:
0396: if ("".equals(localName))
0397: localName = "return";
0398:
0399: if ("".equals(webResult.targetNamespace()))
0400: _resultName = new QName(localName);
0401: else
0402: _resultName = new QName(
0403: webResult.targetNamespace(), localName);
0404: } else {
0405: _headerReturn = false;
0406: _resultName = new QName("return"); // XXX namespace?
0407: }
0408:
0409: _returnMarshal = ParameterMarshal.create(0, property,
0410: _resultName, WebParam.Mode.OUT, marshaller,
0411: unmarshaller);
0412:
0413: _bodyOutputs++;
0414:
0415: if (_headerReturn)
0416: _headerOutputs++;
0417: } else {
0418: _headerReturn = false;
0419: _returnMarshal = null;
0420: }
0421:
0422: //
0423: // Exceptions
0424: //
0425:
0426: Class[] exceptions = method.getExceptionTypes();
0427:
0428: for (Class exception : exceptions) {
0429: QName faultName = new QName(targetNamespace, exception
0430: .getSimpleName(), TARGET_NAMESPACE_PREFIX);
0431: // XXX check for generated exception classes versus raw exceptions
0432: // i.e. things like getFaultInfo()
0433: Property property = jaxbContext.createProperty(exception);
0434: ParameterMarshal marshal = ParameterMarshal.create(0,
0435: property, faultName, WebParam.Mode.OUT, marshaller,
0436: unmarshaller);
0437:
0438: _faults.put(exception, marshal);
0439: _faultNames.put(faultName, marshal);
0440: }
0441: }
0442:
0443: public static AbstractAction createAction(Method method,
0444: JAXBContextImpl jaxbContext, String targetNamespace,
0445: WSDLDefinitions wsdl, Marshaller marshaller,
0446: Unmarshaller unmarshaller) throws JAXBException,
0447: WebServiceException {
0448: // There are three valid modes in JAX-WS:
0449: //
0450: // 1. Document wrapped -- all the parameters and return values
0451: // are encapsulated in a single encoded object (i.e. the document).
0452: // This is selected by
0453: // SOAPBinding.style() == DOCUMENT
0454: // SOAPBinding.use() == LITERAL
0455: // SOAPBinding.parameterStyle() == WRAPPED
0456: //
0457: // 2. Document bare -- the method must have at most one input and
0458: // one output parameter. No wrapper objects are created.
0459: // This is selected by
0460: // SOAPBinding.style() == DOCUMENT
0461: // SOAPBinding.use() == LITERAL
0462: // SOAPBinding.parameterStyle() == BARE
0463: //
0464: // 3. RPC style -- parameters and return values are mapped to
0465: // wsdl:parts. This is selected by:
0466: // SOAPBinding.style() == RPC
0467: // SOAPBinding.use() == LITERAL
0468: // SOAPBinding.parameterStyle() == WRAPPED
0469: //
0470: // It seems that "use" is never ENCODED in JAX-WS and is not allowed
0471: // by WS-I, so we don't allow it either.
0472: //
0473:
0474: // Check for the SOAPBinding annotation...
0475:
0476: // look at the declaring class and method first
0477: Class cl = method.getDeclaringClass();
0478: Method eiMethod = null;
0479: SOAPBinding soapBinding = (SOAPBinding) cl
0480: .getAnnotation(SOAPBinding.class);
0481:
0482: if (method.isAnnotationPresent(SOAPBinding.class))
0483: soapBinding = method.getAnnotation(SOAPBinding.class);
0484:
0485: if (soapBinding == null) {
0486: // Then look at the endpoint interface, if available
0487: WebService webService = (WebService) cl
0488: .getAnnotation(WebService.class);
0489:
0490: if (webService != null) {
0491: if (!"".equals(webService.endpointInterface())) {
0492: try {
0493: ClassLoader loader = cl.getClassLoader();
0494:
0495: Class endpointInterface = loader
0496: .loadClass(webService
0497: .endpointInterface());
0498:
0499: soapBinding = (SOAPBinding) endpointInterface
0500: .getAnnotation(SOAPBinding.class);
0501:
0502: eiMethod = endpointInterface.getMethod(method
0503: .getName(), method.getParameterTypes());
0504:
0505: if (eiMethod
0506: .isAnnotationPresent(SOAPBinding.class))
0507: soapBinding = eiMethod
0508: .getAnnotation(SOAPBinding.class);
0509: } catch (ClassNotFoundException e) {
0510: throw new WebServiceException(L.l(
0511: "Endpoint interface {0} not found",
0512: webService.endpointInterface()), e);
0513: } catch (NoSuchMethodException e) {
0514: // We don't care if the method isn't defined in the interface
0515: }
0516: }
0517: }
0518: }
0519:
0520: // Document wrapped is the default for methods w/o a @SOAPBinding
0521: if (soapBinding == null)
0522: return new DocumentWrappedAction(method, eiMethod,
0523: jaxbContext, targetNamespace, wsdl, marshaller,
0524: unmarshaller);
0525:
0526: if (soapBinding.use() == SOAPBinding.Use.ENCODED)
0527: throw new UnsupportedOperationException(L
0528: .l("SOAP encoded style is not supported by JAX-WS"));
0529:
0530: if (soapBinding.style() == SOAPBinding.Style.DOCUMENT) {
0531: if (soapBinding.parameterStyle() == SOAPBinding.ParameterStyle.WRAPPED)
0532: return new DocumentWrappedAction(method, eiMethod,
0533: jaxbContext, targetNamespace, wsdl, marshaller,
0534: unmarshaller);
0535: else {
0536: return new DocumentBareAction(method, eiMethod,
0537: jaxbContext, targetNamespace, wsdl, marshaller,
0538: unmarshaller);
0539: }
0540: } else {
0541: if (soapBinding.parameterStyle() != SOAPBinding.ParameterStyle.WRAPPED)
0542: throw new UnsupportedOperationException(L
0543: .l("SOAP RPC bare style not supported"));
0544:
0545: return new RpcAction(method, eiMethod, jaxbContext,
0546: targetNamespace, wsdl, marshaller, unmarshaller);
0547: }
0548: }
0549:
0550: protected boolean isAttachment(WebParam webParam) {
0551: return webParam.name().startsWith("attach");
0552: }
0553:
0554: /**
0555: * Client-side invocation.
0556: */
0557: public Object invoke(String url, Object[] args,
0558: HandlerChainInvoker handlerChain) throws IOException,
0559: XMLStreamException, MalformedURLException, JAXBException,
0560: Throwable {
0561: XMLStreamReader in = null;
0562: URL urlObject = new URL(url);
0563: URLConnection connection = urlObject.openConnection();
0564:
0565: // XXX HTTPS
0566: if (!(connection instanceof HttpURLConnection))
0567: return null;
0568:
0569: HttpURLConnection httpConnection = (HttpURLConnection) connection;
0570:
0571: try {
0572: //
0573: // Send the request
0574: //
0575:
0576: httpConnection.setRequestMethod("POST");
0577: httpConnection.setDoInput(true);
0578: httpConnection.setDoOutput(true);
0579: // XXX: Does this change for multipart/attachments?
0580: httpConnection.setRequestProperty("Content-type",
0581: "text/xml");
0582:
0583: OutputStream httpOut = null;
0584: XMLStreamWriter out = null;
0585: DOMResult dom = null;
0586:
0587: UUID uuid = UUID.randomUUID();
0588:
0589: if (_attachmentInputs > 0) {
0590: // note that we have to add the request property (header) before
0591: // we get the output stream
0592: httpConnection.addRequestProperty("Content-Type",
0593: "multipart/related; " + "type=\"text/xml\"; "
0594: + "boundary=\"uuid:" + uuid + "\"");
0595:
0596: httpOut = httpConnection.getOutputStream();
0597:
0598: PrintWriter writer = new PrintWriter(httpOut);
0599: writer.print("--uuid:" + uuid + "\r\n");
0600: writer.print("Content-Type: text/xml\r\n");
0601: writer.print("\r\n");
0602: writer.flush();
0603: } else
0604: httpOut = httpConnection.getOutputStream();
0605:
0606: if (handlerChain != null) {
0607: dom = new DOMResult();
0608: out = getXMLOutputFactory().createXMLStreamWriter(dom);
0609: } else {
0610: out = getXMLOutputFactory().createXMLStreamWriter(
0611: httpOut);
0612: }
0613:
0614: writeRequest(out, args);
0615: out.flush();
0616:
0617: if (_attachmentInputs > 0) {
0618: httpOut.write("\r\n".getBytes());
0619: writeAttachments(httpOut, uuid, args);
0620: }
0621:
0622: if (handlerChain != null) {
0623: Source source = new DOMSource(dom.getNode());
0624:
0625: if (!handlerChain.invokeClientOutbound(source, httpOut)) {
0626: source = handlerChain.getSource();
0627: in = _xmlInputFactory.createXMLStreamReader(source);
0628:
0629: return readResponse(in, args);
0630: }
0631: }
0632:
0633: //
0634: // Parse the response
0635: //
0636:
0637: httpConnection.getResponseCode();
0638: InputStream is = httpConnection.getInputStream();
0639:
0640: if (handlerChain != null)
0641: is = handlerChain.invokeClientInbound(httpConnection);
0642:
0643: String contentType = httpConnection
0644: .getHeaderField("Content-Type");
0645:
0646: if (contentType != null
0647: && contentType.startsWith("multipart/related")) {
0648: String[] tokens = contentType.split(";");
0649:
0650: String boundary = null;
0651:
0652: for (int i = 0; i < tokens.length; i++) {
0653: int start = tokens[i].indexOf("boundary=");
0654:
0655: if (start >= 0) {
0656: boundary = tokens[i].substring(start
0657: + "boundary=".length() + 1, tokens[i]
0658: .lastIndexOf('"'));
0659: break;
0660: }
0661: }
0662:
0663: if (boundary == null)
0664: return null; // XXX throw something about malformed response
0665: }
0666:
0667: in = _xmlInputFactory.createXMLStreamReader(is);
0668:
0669: if (httpConnection.getResponseCode() != 200)
0670: return null; // XXX more meaningful error
0671:
0672: if (_isOneway)
0673: return null;
0674:
0675: return readResponse(in, args);
0676: } finally {
0677: if (httpConnection != null)
0678: httpConnection.disconnect();
0679: }
0680: }
0681:
0682: protected void writeRequest(XMLStreamWriter out, Object[] args)
0683: throws IOException, XMLStreamException, JAXBException {
0684: out.writeStartDocument("UTF-8", "1.0");
0685: out.writeStartElement(Skeleton.SOAP_ENVELOPE_PREFIX,
0686: "Envelope", Skeleton.SOAP_ENVELOPE);
0687: out.writeNamespace(Skeleton.SOAP_ENVELOPE_PREFIX,
0688: Skeleton.SOAP_ENVELOPE);
0689:
0690: out.writeStartElement(Skeleton.SOAP_ENVELOPE_PREFIX, "Header",
0691: Skeleton.SOAP_ENVELOPE);
0692:
0693: for (ParameterMarshal marshal : _headerArguments.values())
0694: marshal.serializeCall(out, args);
0695:
0696: out.writeEndElement(); // Header
0697:
0698: out.writeStartElement(Skeleton.SOAP_ENVELOPE_PREFIX, "Body",
0699: Skeleton.SOAP_ENVELOPE);
0700:
0701: writeMethodInvocation(out, args);
0702:
0703: out.writeEndElement(); // Body
0704: out.writeEndElement(); // Envelope
0705: }
0706:
0707: protected void writeAttachments(OutputStream out, UUID uuid,
0708: Object[] args) throws IOException {
0709: PrintWriter writer = new PrintWriter(out);
0710:
0711: for (int i = 0; i < _attachmentArgs.length; i++)
0712: _attachmentArgs[i].serializeCall(writer, out, uuid, args);
0713: }
0714:
0715: abstract protected void writeMethodInvocation(XMLStreamWriter out,
0716: Object[] args) throws IOException, XMLStreamException,
0717: JAXBException;
0718:
0719: abstract protected Object readResponse(XMLStreamReader in,
0720: Object[] args) throws IOException, XMLStreamException,
0721: JAXBException, Throwable;
0722:
0723: /**
0724: * Invokes the request for a call.
0725: */
0726: public int invoke(Object service, XMLStreamReader header,
0727: XMLStreamReader in, XMLStreamWriter out,
0728: List<Attachment> attachments) throws IOException,
0729: XMLStreamException, Throwable {
0730: // We're starting out at the point in the input stream where the
0731: // method name is listed (with the arguments as children) and the
0732: // point in the output stream where the results are to be written.
0733:
0734: Object[] args = readMethodInvocation(header, in);
0735: readAttachments(attachments, args);
0736:
0737: Object value = null;
0738:
0739: try {
0740: value = _method.invoke(service, args);
0741: } catch (IllegalAccessException e) {
0742: throw new Throwable(e);
0743: } catch (IllegalArgumentException e) {
0744: throw new Throwable(e);
0745: } catch (InvocationTargetException e) {
0746: writeFault(out, e.getCause());
0747: return 500;
0748: }
0749:
0750: if (!_isOneway) {
0751: if (_headerOutputs > 0) {
0752: out.writeStartElement(SOAP_ENVELOPE_PREFIX, "Header",
0753: SOAP_ENVELOPE);
0754:
0755: if (_returnMarshal != null && _headerReturn)
0756: _returnMarshal.serializeReply(out, value);
0757:
0758: for (int i = 0; i < _headerArgs.length; i++)
0759: _headerArgs[i].serializeReply(out, args);
0760:
0761: out.writeEndElement(); // Header
0762: }
0763: }
0764:
0765: // services/1318: We always need a body even if it is oneway
0766: out.writeStartElement(SOAP_ENVELOPE_PREFIX, "Body",
0767: SOAP_ENVELOPE);
0768:
0769: if (!_isOneway)
0770: writeResponse(out, value, args);
0771:
0772: out.writeEndElement(); // Body
0773:
0774: return 200;
0775: }
0776:
0777: protected void readHeaders(XMLStreamReader header, Object[] args)
0778: throws IOException, XMLStreamException, JAXBException {
0779: for (int i = 0; i < _headerArgs.length; i++)
0780: _headerArgs[i].prepareArgument(args);
0781:
0782: if (header != null) {
0783: header.nextTag();
0784:
0785: while (header.getEventType() == header.START_ELEMENT) {
0786: ParameterMarshal arg = _headerArguments.get(header
0787: .getLocalName());
0788:
0789: if (arg == null) {
0790: if (log.isLoggable(Level.FINER))
0791: log.finer(L.l("Unknown header argument: {0}",
0792: header.getName()));
0793:
0794: // skip this subtree
0795: int depth = 1;
0796:
0797: while (depth > 0) {
0798: switch (header.nextTag()) {
0799: case XMLStreamReader.START_ELEMENT:
0800: depth++;
0801: break;
0802: case XMLStreamReader.END_ELEMENT:
0803: depth--;
0804: break;
0805: }
0806: }
0807: } else {
0808: arg.deserializeCall(header, args);
0809: }
0810: }
0811: }
0812: }
0813:
0814: protected void readAttachments(List<Attachment> attachments,
0815: Object[] args) throws IOException, XMLStreamException,
0816: JAXBException {
0817: for (int i = 0; i < _attachmentArgs.length; i++) {
0818: if (_attachmentArgs[i] instanceof OutParameterMarshal)
0819: continue;
0820:
0821: _attachmentArgs[i].prepareArgument(args);
0822:
0823: if (attachments != null) {
0824: if (i < attachments.size())
0825: _attachmentArgs[i].deserializeCall(attachments
0826: .get(i), args);
0827:
0828: else
0829: log.fine(L.l("Received unexpected attachment"));
0830: }
0831: }
0832: }
0833:
0834: // reads the method invocation and returns the arguments
0835: abstract protected Object[] readMethodInvocation(
0836: XMLStreamReader header, XMLStreamReader in)
0837: throws IOException, XMLStreamException, JAXBException;
0838:
0839: abstract protected void writeResponse(XMLStreamWriter out,
0840: Object value, Object[] args) throws IOException,
0841: XMLStreamException, JAXBException;
0842:
0843: protected void writeFault(XMLStreamWriter out, Throwable fault)
0844: throws IOException, XMLStreamException, JAXBException {
0845: out.writeStartElement(SOAP_ENVELOPE_PREFIX, "Body",
0846: SOAP_ENVELOPE);
0847: out.writeStartElement(Skeleton.SOAP_ENVELOPE_PREFIX, "Fault",
0848: Skeleton.SOAP_ENVELOPE);
0849:
0850: out.writeStartElement("faultcode");
0851: out.writeCharacters(Skeleton.SOAP_ENVELOPE_PREFIX + ":Server");
0852: out.writeEndElement(); // faultcode
0853:
0854: //
0855: // Marshal this exception as a fault.
0856: //
0857: // faults must have exactly the same class as declared on the method,
0858: // otherwise we emit an internal server error.
0859: // XXX This may not be behavior required by the standard and we may
0860: // be able to improve here by casting as a superclass.
0861: ParameterMarshal faultMarshal = _faults.get(fault.getClass());
0862:
0863: if (faultMarshal == null) {
0864: out.writeStartElement("faultstring");
0865: out.writeCharacters(L.l("Internal server error"));
0866: out.writeEndElement(); // faultstring
0867: } else {
0868: out.writeStartElement("faultstring");
0869: out.writeCharacters(fault.getMessage());
0870: out.writeEndElement(); // faultstring
0871:
0872: out.writeStartElement("detail");
0873: faultMarshal.serializeReply(out, fault);
0874: out.writeEndElement(); // detail
0875: }
0876:
0877: out.writeEndElement(); // Fault
0878: out.writeEndElement(); // Body
0879: }
0880:
0881: protected Throwable readFault(XMLStreamReader in)
0882: throws IOException, XMLStreamException, JAXBException,
0883: SOAPException {
0884: Throwable fault = null;
0885: String message = null;
0886: String actor = null;
0887: SOAPFault soapFault = createSOAPFault();
0888:
0889: while (in.nextTag() == XMLStreamReader.START_ELEMENT) {
0890: if ("faultcode".equals(in.getLocalName())) {
0891: if (in.next() == XMLStreamReader.CHARACTERS) {
0892: String code = in.getText();
0893: int colon = code.indexOf(':');
0894:
0895: if (colon >= 0)
0896: code = code.substring(colon + 1);
0897:
0898: if ("Server".equalsIgnoreCase(code)) {
0899: // XXX Do anything with this?
0900: } else if ("Client".equalsIgnoreCase(code)) {
0901: // XXX Do anything with this?
0902: } else if ("VersionMismatch".equalsIgnoreCase(code)) {
0903: // XXX Do anything with this?
0904: } else if ("MustUnderstand".equalsIgnoreCase(code)) {
0905: // XXX Do anything with this?
0906: }
0907:
0908: soapFault.setFaultCode(code);
0909: }
0910:
0911: while (in.nextTag() != XMLStreamReader.END_ELEMENT) {
0912: }
0913: } else if ("faultstring".equals(in.getLocalName())) {
0914: if (in.next() == XMLStreamReader.CHARACTERS)
0915: message = in.getText();
0916:
0917: soapFault.setFaultString(message);
0918:
0919: while (in.nextTag() != XMLStreamReader.END_ELEMENT) {
0920: }
0921: } else if ("faultactor".equals(in.getLocalName())) {
0922: if (in.next() == XMLStreamReader.CHARACTERS)
0923: actor = in.getText();
0924:
0925: soapFault.setFaultActor(actor);
0926:
0927: while (in.nextTag() != XMLStreamReader.END_ELEMENT) {
0928: }
0929: } else if ("detail".equals(in.getLocalName())) {
0930: if (in.nextTag() == XMLStreamReader.START_ELEMENT) {
0931: ParameterMarshal faultMarshal = _faultNames.get(in
0932: .getName());
0933:
0934: if (faultMarshal != null)
0935: fault = (Exception) faultMarshal
0936: .deserializeReply(in, fault);
0937: }
0938: }
0939: }
0940:
0941: if (fault == null)
0942: fault = new SOAPFaultException(soapFault);
0943:
0944: return fault;
0945: }
0946:
0947: public boolean hasHeaderInput() {
0948: return false;
0949: }
0950:
0951: public int getArity() {
0952: return _arity;
0953: }
0954:
0955: public String getInputName() {
0956: return _inputName;
0957: }
0958:
0959: public static String getWebMethodName(Method method) {
0960: String methodName = _methodNames.get(method);
0961:
0962: if (methodName == null) {
0963: Method eiMethod = getEIMethod(method);
0964: methodName = getWebMethodName(method, eiMethod);
0965:
0966: _methodNames.put(method, methodName);
0967: }
0968:
0969: return methodName;
0970: }
0971:
0972: public static String getWebMethodName(Method method, Method eiMethod) {
0973: String name = method.getName();
0974:
0975: WebMethod webMethod = method.getAnnotation(WebMethod.class);
0976:
0977: if (webMethod == null && eiMethod != null)
0978: webMethod = eiMethod.getAnnotation(WebMethod.class);
0979:
0980: if (webMethod != null && !"".equals(webMethod.operationName()))
0981: name = webMethod.operationName();
0982:
0983: return name;
0984: }
0985:
0986: public static String getPortName(Method method, Method eiMethod) {
0987: Class cl = method.getDeclaringClass();
0988: WebService webService = (WebService) cl
0989: .getAnnotation(WebService.class);
0990:
0991: if (webService == null && eiMethod != null) {
0992: cl = eiMethod.getDeclaringClass();
0993: webService = (WebService) cl
0994: .getAnnotation(WebService.class);
0995: }
0996:
0997: if (webService != null && !"".equals(webService.portName()))
0998: return webService.portName();
0999:
1000: return null;
1001: }
1002:
1003: public static String getSOAPAction(Method method, Method eiMethod) {
1004: String action = "";
1005:
1006: WebMethod webMethod = method.getAnnotation(WebMethod.class);
1007:
1008: if (webMethod == null && eiMethod != null)
1009: webMethod = eiMethod.getAnnotation(WebMethod.class);
1010:
1011: if (webMethod != null)
1012: action = webMethod.action();
1013:
1014: return action;
1015: }
1016:
1017: public static Method getEIMethod(Method method) {
1018: try {
1019: Class cl = method.getDeclaringClass();
1020: WebService webService = (WebService) cl
1021: .getAnnotation(WebService.class);
1022:
1023: if (webService != null) {
1024: if (!"".equals(webService.endpointInterface())) {
1025: ClassLoader loader = cl.getClassLoader();
1026:
1027: Class endpointInterface = loader
1028: .loadClass(webService.endpointInterface());
1029:
1030: return endpointInterface.getMethod(
1031: method.getName(), method
1032: .getParameterTypes());
1033: }
1034: }
1035: } catch (ClassNotFoundException e) {
1036: } catch (NoSuchMethodException e) {
1037: }
1038:
1039: return null;
1040: }
1041:
1042: public abstract void writeWSDLMessages(XMLStreamWriter out,
1043: String soapNamespaceURI) throws XMLStreamException;
1044:
1045: public abstract void writeSchema(XMLStreamWriter out,
1046: String namespace, JAXBContextImpl context)
1047: throws XMLStreamException, WebServiceException;
1048:
1049: public abstract void writeWSDLBindingOperation(XMLStreamWriter out,
1050: String soapNamespaceURI) throws XMLStreamException;
1051:
1052: public abstract void writeWSDLOperation(XMLStreamWriter out,
1053: String soapNamespaceURI) throws XMLStreamException;
1054: }
|