/* * Copyright (c) 1998-2011 Caucho Technology -- all rights reserved * * This file is part of Resin(R) Open Source * * Each copy or derived work must preserve the copyright notice and this * notice unmodified. * * Resin Open Source 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. * * Resin Open Source 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, or any warranty * of NON-INFRINGEMENT. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License * along with Resin Open Source; if not, write to the * * Free Software Foundation, Inc. * 59 Temple Place, Suite 330 * Boston, MA 02111-1307 USA * * @author Emil Ong */ package com.caucho.soap.skeleton; import com.caucho.jaxb.property.Property; import com.caucho.jaxb.property.WrapperProperty; import com.caucho.jaxb.JAXBContextImpl; import static com.caucho.soap.wsdl.WSDLConstants.*; import com.caucho.soap.wsdl.WSDLDefinitions; import com.caucho.util.L10N; import com.caucho.xml.stream.StaxUtil; import javax.jws.WebParam; import javax.jws.WebResult; import static javax.xml.XMLConstants.*; import javax.xml.bind.Marshaller; import javax.xml.bind.Unmarshaller; import javax.xml.bind.JAXBException; import javax.xml.namespace.QName; import javax.xml.stream.XMLStreamException; import javax.xml.stream.XMLStreamReader; import javax.xml.stream.XMLStreamWriter; import javax.xml.ws.Holder; import javax.xml.ws.WebServiceException; import java.io.IOException; import java.lang.annotation.Annotation; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Type; import java.util.ArrayList; import java.util.Collection; import java.util.Map; import java.util.logging.Logger; /** * Invokes a SOAP request on a Java POJO method. * * This class handles the document-literal bare (i.e. non-wrapped) style * which JAX-WS maps to methods of at most one input and one output * argument. Non-void return values count as an output argument and * INOUT arguments count as both one input and one output. */ public class DocumentBareAction extends AbstractAction { private final static Logger log = Logger.getLogger(DocumentBareAction.class.getName()); private static final L10N L = new L10N(DocumentBareAction.class); private static final QName ITEM_NAME = new QName("item"); private int _inputArgument = -1; private String _partName; private final String _messageName; public DocumentBareAction(Method method, Method eiMethod, JAXBContextImpl jaxbContext, String targetNamespace, WSDLDefinitions wsdl, Marshaller marshaller, Unmarshaller unmarshaller) throws JAXBException, WebServiceException { super(method, eiMethod, jaxbContext, targetNamespace, wsdl, marshaller, unmarshaller); _messageName = _operationName; if (_bodyInputs > 1) { throw new WebServiceException(L.l("Document bare methods may not have more than one input argument: {0}.{1} has {2} input args", method.getDeclaringClass().getName(), method.getName(), _bodyInputs)); } if (_bodyOutputs > 1) throw new WebServiceException(L.l("Document bare methods may not have more than one output argument: {0}.{1} has {2} output args (including the return value)", method.getDeclaringClass().getName(), method.getName(), _bodyOutputs)); // // Fix the argument/response names // // XXX header args/no args if (_bodyInputs == 1) { for (int i = 0; i < _bodyArgs.length; i++) { if (! (_bodyArgs[i] instanceof OutParameterMarshal)) { _inputArgument = i; break; } } // XXX check that @WebParam is not set on the argument (and for some // reason explicitly set the name to "arg0") if ("arg0".equals(_bodyArgs[_inputArgument].getName().getLocalPart())) { QName argName = new QName(_targetNamespace, _operationName); _bodyArgs[_inputArgument].setName(argName); } else _inputName = _bodyArgs[_inputArgument].getName().getLocalPart(); // Document bare does something strange with arrays and collections: // They are wrapped and their individual element names are <item>, // except for byte[]. Class[] parameterTypes = method.getParameterTypes(); Class inputType = parameterTypes[_inputArgument]; if ((inputType.isArray() && ! byte.class.equals(inputType.getComponentType())) || Collection.class.isAssignableFrom(inputType)) { WrapperProperty wrapper = new WrapperProperty(_bodyArgs[_inputArgument]._property, _bodyArgs[_inputArgument].getName(), ITEM_NAME); _bodyArgs[_inputArgument].setName(ITEM_NAME); _bodyArgs[_inputArgument]._property = wrapper; } } else if (_bodyInputs == 0) _inputName = ""; // services/131[89] if (_returnMarshal != null) { WebResult webResult = _method.getAnnotation(WebResult.class); if (webResult == null && eiMethod != null) webResult = eiMethod.getAnnotation(WebResult.class); if (webResult == null || "".equals(webResult.name())) _returnMarshal.setName(new QName(_targetNamespace, _responseName)); // services/144[01] else if (webResult.targetNamespace() == null || "".equals(webResult.targetNamespace())) _returnMarshal.setName(new QName(_targetNamespace, webResult.name())); else _returnMarshal.setName(new QName(webResult.targetNamespace(), webResult.name())); Class returnType = method.getReturnType(); if ((returnType.isArray() && ! byte.class.equals(returnType.getComponentType())) || Collection.class.isAssignableFrom(returnType)) { WrapperProperty wrapper = new WrapperProperty(_returnMarshal._property, _returnMarshal.getName(), ITEM_NAME); _returnMarshal.setName(ITEM_NAME); _returnMarshal._property = wrapper; } } } protected void writeMethodInvocation(XMLStreamWriter out, Object []args) throws IOException, XMLStreamException, JAXBException { // services/1308 if (_bodyInputs > 0) { for (int i = 0; i < _bodyArgs.length; i++) _bodyArgs[i].serializeCall(out, args); } } protected Object[] readMethodInvocation(XMLStreamReader header, XMLStreamReader in) throws IOException, XMLStreamException, JAXBException { Object[] args = new Object[_arity]; readHeaders(header, args); if (_bodyInputs > 0) { for (int i = 0; i < _bodyArgs.length; i++) _bodyArgs[i].deserializeCall(in, args); } return args; } protected void writeResponse(XMLStreamWriter out, Object value, Object[] args) throws IOException, XMLStreamException, JAXBException { if (_returnMarshal != null && ! _headerReturn) _returnMarshal.serializeReply(out, value); for (int i = 0; i < _bodyArgs.length; i++) _bodyArgs[i].serializeReply(out, args); } protected Object readResponse(XMLStreamReader in, Object []args) throws IOException, XMLStreamException, JAXBException, Throwable { Object ret = null; in.nextTag(); in.require(XMLStreamReader.START_ELEMENT, null, "Envelope"); in.nextTag(); in.require(XMLStreamReader.START_ELEMENT, null, null); if ("Header".equals(in.getLocalName())) { while (in.nextTag() == XMLStreamReader.START_ELEMENT) { String tagName = in.getLocalName(); ParameterMarshal marshal = _headerArguments.get(tagName); if (marshal != null) marshal.deserializeReply(in, args); else { int depth = 1; while (depth > 0) { switch (in.nextTag()) { case XMLStreamReader.START_ELEMENT: depth++; break; case XMLStreamReader.END_ELEMENT: depth--; break; default: throw new IOException("expected </Header>"); } } } } in.require(XMLStreamReader.END_ELEMENT, null, "Header"); in.nextTag(); } in.require(XMLStreamReader.START_ELEMENT, null, "Body"); in.nextTag(); if (in.getEventType() == XMLStreamReader.START_ELEMENT && "Fault".equals(in.getLocalName())) { Throwable fault = readFault(in); if (fault == null) throw new WebServiceException(); // XXX throw fault; } if (_returnMarshal != null) ret = _returnMarshal.deserializeReply(in, ret); for (int i = 0; i < _bodyArgs.length; i++) _bodyArgs[i].deserializeReply(in, args); in.require(XMLStreamReader.END_ELEMENT, null, "Body"); in.nextTag(); in.require(XMLStreamReader.END_ELEMENT, null, "Envelope"); return ret; } public void writeWSDLBindingOperation(XMLStreamWriter out, String soapNamespaceURI) throws XMLStreamException { out.writeStartElement(WSDL_NAMESPACE, "operation"); out.writeAttribute("name", _messageName); // XXX out.writeAttribute("parameterOrder", ""); out.writeEmptyElement(soapNamespaceURI, "operation"); out.writeAttribute("soapAction", _soapAction); out.writeStartElement(WSDL_NAMESPACE, "input"); // XXX out.writeEmptyElement(soapNamespaceURI, "body"); out.writeAttribute("use", "literal"); out.writeEndElement(); // input if (! _isOneway) { out.writeStartElement(WSDL_NAMESPACE, "output"); // XXX out.writeEmptyElement(soapNamespaceURI, "body"); out.writeAttribute("use", "literal"); out.writeEndElement(); // output } out.writeEndElement(); // operation } public void writeWSDLMessages(XMLStreamWriter out, String soapNamespaceURI) throws XMLStreamException { out.writeStartElement(WSDL_NAMESPACE, "message"); out.writeAttribute("name", _messageName); if (_bodyInputs > 0) { out.writeEmptyElement(WSDL_NAMESPACE, "part"); // XXX take name from @WebParam out.writeAttribute("name", _operationName); out.writeAttribute("element", TARGET_NAMESPACE_PREFIX + ':' + _operationName); } out.writeEndElement(); // message if (! _isOneway) { out.writeStartElement(WSDL_NAMESPACE, "message"); out.writeAttribute("name", _responseName); if (_returnMarshal != null) { out.writeEmptyElement(WSDL_NAMESPACE, "part"); out.writeAttribute("name", _responseName); out.writeAttribute("element", TARGET_NAMESPACE_PREFIX + ':' + _responseName); } out.writeEndElement(); // message } } public void writeWSDLOperation(XMLStreamWriter out, String soapNamespaceURI) throws XMLStreamException { out.writeStartElement(WSDL_NAMESPACE, "operation"); out.writeAttribute("name", _operationName); // XXX out.writeAttribute("parameterOrder", ""); out.writeEmptyElement(WSDL_NAMESPACE, "input"); out.writeAttribute("message", TARGET_NAMESPACE_PREFIX + ':' + _messageName); if (! _isOneway) { out.writeEmptyElement(WSDL_NAMESPACE, "output"); out.writeAttribute("message", TARGET_NAMESPACE_PREFIX + ':' + _responseName); } out.writeEndElement(); // operation } public void writeSchema(XMLStreamWriter out, String namespace, JAXBContextImpl context) throws XMLStreamException, WebServiceException { if (_bodyInputs > 0) { QName elementName = new QName(namespace, _operationName); if (context.hasRootElement(elementName)) throw new WebServiceException(L.l("Duplicate element {0}", elementName)); out.writeEmptyElement(XML_SCHEMA_PREFIX, "element", W3C_XML_SCHEMA_NS_URI); out.writeAttribute("name", _operationName); Property property = _bodyArgs[_inputArgument].getProperty(); String type = StaxUtil.qnameToString(out, property.getSchemaType()); out.writeAttribute("type", type); } if (_returnMarshal != null) { QName elementName = new QName(namespace, _responseName); if (context.hasRootElement(elementName)) throw new WebServiceException(L.l("Duplicate element {0}", elementName)); out.writeEmptyElement(XML_SCHEMA_PREFIX, "element", W3C_XML_SCHEMA_NS_URI); out.writeAttribute("name", _responseName); QName schemaType = _returnMarshal.getProperty().getSchemaType(); String type = StaxUtil.qnameToString(out, schemaType); out.writeAttribute("type", type); } } public String toString() { return "DocumentBareAction[" + _method.getName() + "]"; } }