001: /*
002: * Copyright (c) 1998-2007 Caucho Technology -- all rights reserved
003: *
004: * This file is part of Resin(R) Open Source
005: *
006: * Each copy or derived work must preserve the copyright notice and this
007: * notice unmodified.
008: *
009: * Resin Open Source is free software; you can redistribute it and/or modify
010: * it under the terms of the GNU General Public License as published by
011: * the Free Software Foundation; either version 2 of the License, or
012: * (at your option) any later version.
013: *
014: * Resin Open Source is distributed in the hope that it will be useful,
015: * but WITHOUT ANY WARRANTY; without even the implied warranty of
016: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, or any warranty
017: * of NON-INFRINGEMENT. See the GNU General Public License for more
018: * details.
019: *
020: * You should have received a copy of the GNU General Public License
021: * along with Resin Open Source; if not, write to the
022: *
023: * Free Software Foundation, Inc.
024: * 59 Temple Place, Suite 330
025: * Boston, MA 02111-1307 USA
026: *
027: * @author Emil Ong
028: */
029:
030: package com.caucho.soap.skeleton;
031:
032: import com.caucho.jaxb.property.Property;
033: import com.caucho.jaxb.property.WrapperProperty;
034: import com.caucho.jaxb.JAXBContextImpl;
035: import static com.caucho.soap.wsdl.WSDLConstants.*;
036: import com.caucho.soap.wsdl.WSDLDefinitions;
037: import com.caucho.util.L10N;
038: import com.caucho.xml.stream.StaxUtil;
039:
040: import javax.jws.WebParam;
041: import javax.jws.WebResult;
042: import static javax.xml.XMLConstants.*;
043: import javax.xml.bind.Marshaller;
044: import javax.xml.bind.Unmarshaller;
045: import javax.xml.bind.JAXBException;
046: import javax.xml.namespace.QName;
047: import javax.xml.stream.XMLStreamException;
048: import javax.xml.stream.XMLStreamReader;
049: import javax.xml.stream.XMLStreamWriter;
050: import javax.xml.ws.Holder;
051: import javax.xml.ws.WebServiceException;
052: import java.io.IOException;
053: import java.lang.annotation.Annotation;
054: import java.lang.reflect.InvocationTargetException;
055: import java.lang.reflect.Method;
056: import java.lang.reflect.Type;
057: import java.util.ArrayList;
058: import java.util.Collection;
059: import java.util.Map;
060: import java.util.logging.Logger;
061:
062: /**
063: * Invokes a SOAP request on a Java POJO method.
064: *
065: * This class handles the document-literal bare (i.e. non-wrapped) style
066: * which JAX-WS maps to methods of at most one input and one output
067: * argument. Non-void return values count as an output argument and
068: * INOUT arguments count as both one input and one output.
069: */
070: public class DocumentBareAction extends AbstractAction {
071: private final static Logger log = Logger
072: .getLogger(DocumentBareAction.class.getName());
073: private static final L10N L = new L10N(DocumentBareAction.class);
074: private static final QName ITEM_NAME = new QName("item");
075:
076: private int _inputArgument = -1;
077: private String _partName;
078: private final String _messageName;
079:
080: public DocumentBareAction(Method method, Method eiMethod,
081: JAXBContextImpl jaxbContext, String targetNamespace,
082: WSDLDefinitions wsdl, Marshaller marshaller,
083: Unmarshaller unmarshaller) throws JAXBException,
084: WebServiceException {
085: super (method, eiMethod, jaxbContext, targetNamespace, wsdl,
086: marshaller, unmarshaller);
087:
088: _messageName = _operationName;
089:
090: if (_bodyInputs > 1) {
091: throw new WebServiceException(
092: L
093: .l(
094: "Document bare methods may not have more than one input argument: {0}.{1} has {2} input args",
095: method.getDeclaringClass()
096: .getName(), method
097: .getName(), _bodyInputs));
098: }
099:
100: if (_bodyOutputs > 1)
101: throw new WebServiceException(
102: L
103: .l(
104: "Document bare methods may not have more than one output argument: {0}.{1} has {2} output args (including the return value)",
105: method.getDeclaringClass()
106: .getName(), method
107: .getName(), _bodyOutputs));
108:
109: //
110: // Fix the argument/response names
111: //
112:
113: // XXX header args/no args
114:
115: if (_bodyInputs == 1) {
116: for (int i = 0; i < _bodyArgs.length; i++) {
117: if (!(_bodyArgs[i] instanceof OutParameterMarshal)) {
118: _inputArgument = i;
119: break;
120: }
121: }
122:
123: // XXX check that @WebParam is not set on the argument (and for some
124: // reason explicitly set the name to "arg0")
125: if ("arg0".equals(_bodyArgs[_inputArgument].getName()
126: .getLocalPart())) {
127:
128: QName argName = new QName(_targetNamespace,
129: _operationName);
130: _bodyArgs[_inputArgument].setName(argName);
131: } else
132: _inputName = _bodyArgs[_inputArgument].getName()
133: .getLocalPart();
134:
135: // Document bare does something strange with arrays and collections:
136: // They are wrapped and their individual element names are <item>,
137: // except for byte[].
138: Class[] parameterTypes = method.getParameterTypes();
139: Class inputType = parameterTypes[_inputArgument];
140:
141: if ((inputType.isArray() && !byte.class.equals(inputType
142: .getComponentType()))
143: || Collection.class.isAssignableFrom(inputType)) {
144: WrapperProperty wrapper = new WrapperProperty(
145: _bodyArgs[_inputArgument]._property,
146: _bodyArgs[_inputArgument].getName(), ITEM_NAME);
147:
148: _bodyArgs[_inputArgument].setName(ITEM_NAME);
149: _bodyArgs[_inputArgument]._property = wrapper;
150: }
151: } else if (_bodyInputs == 0)
152: _inputName = ""; // services/131[89]
153:
154: if (_returnMarshal != null) {
155: WebResult webResult = _method
156: .getAnnotation(WebResult.class);
157:
158: if (webResult == null && eiMethod != null)
159: webResult = eiMethod.getAnnotation(WebResult.class);
160:
161: if (webResult == null || "".equals(webResult.name()))
162: _returnMarshal.setName(new QName(_targetNamespace,
163: _responseName));
164:
165: // services/144[01]
166:
167: else if (webResult.targetNamespace() == null
168: || "".equals(webResult.targetNamespace()))
169: _returnMarshal.setName(new QName(_targetNamespace,
170: webResult.name()));
171:
172: else
173: _returnMarshal.setName(new QName(webResult
174: .targetNamespace(), webResult.name()));
175:
176: Class returnType = method.getReturnType();
177:
178: if ((returnType.isArray() && !byte.class.equals(returnType
179: .getComponentType()))
180: || Collection.class.isAssignableFrom(returnType)) {
181: WrapperProperty wrapper = new WrapperProperty(
182: _returnMarshal._property, _returnMarshal
183: .getName(), ITEM_NAME);
184:
185: _returnMarshal.setName(ITEM_NAME);
186: _returnMarshal._property = wrapper;
187: }
188: }
189: }
190:
191: protected void writeMethodInvocation(XMLStreamWriter out,
192: Object[] args) throws IOException, XMLStreamException,
193: JAXBException {
194: // services/1308
195: if (_bodyInputs > 0) {
196: for (int i = 0; i < _bodyArgs.length; i++)
197: _bodyArgs[i].serializeCall(out, args);
198: }
199: }
200:
201: protected Object[] readMethodInvocation(XMLStreamReader header,
202: XMLStreamReader in) throws IOException, XMLStreamException,
203: JAXBException {
204: Object[] args = new Object[_arity];
205:
206: readHeaders(header, args);
207:
208: if (_bodyInputs > 0) {
209: for (int i = 0; i < _bodyArgs.length; i++)
210: _bodyArgs[i].deserializeCall(in, args);
211: }
212:
213: return args;
214: }
215:
216: protected void writeResponse(XMLStreamWriter out, Object value,
217: Object[] args) throws IOException, XMLStreamException,
218: JAXBException {
219: if (_returnMarshal != null && !_headerReturn)
220: _returnMarshal.serializeReply(out, value);
221:
222: for (int i = 0; i < _bodyArgs.length; i++)
223: _bodyArgs[i].serializeReply(out, args);
224: }
225:
226: protected Object readResponse(XMLStreamReader in, Object[] args)
227: throws IOException, XMLStreamException, JAXBException,
228: Throwable {
229: Object ret = null;
230:
231: in.nextTag();
232: in.require(XMLStreamReader.START_ELEMENT, null, "Envelope");
233:
234: in.nextTag();
235: in.require(XMLStreamReader.START_ELEMENT, null, null);
236:
237: if ("Header".equals(in.getLocalName())) {
238: while (in.nextTag() == XMLStreamReader.START_ELEMENT) {
239: String tagName = in.getLocalName();
240:
241: ParameterMarshal marshal = _headerArguments
242: .get(tagName);
243:
244: if (marshal != null)
245: marshal.deserializeReply(in, args);
246: else {
247: int depth = 1;
248:
249: while (depth > 0) {
250: switch (in.nextTag()) {
251: case XMLStreamReader.START_ELEMENT:
252: depth++;
253: break;
254: case XMLStreamReader.END_ELEMENT:
255: depth--;
256: break;
257: default:
258: throw new IOException("expected </Header>");
259: }
260: }
261: }
262: }
263:
264: in.require(XMLStreamReader.END_ELEMENT, null, "Header");
265:
266: in.nextTag();
267: }
268:
269: in.require(XMLStreamReader.START_ELEMENT, null, "Body");
270:
271: in.nextTag();
272:
273: if (in.getEventType() == XMLStreamReader.START_ELEMENT
274: && "Fault".equals(in.getLocalName())) {
275: Throwable fault = readFault(in);
276:
277: if (fault == null)
278: throw new WebServiceException(); // XXX
279:
280: throw fault;
281: }
282:
283: if (_returnMarshal != null)
284: ret = _returnMarshal.deserializeReply(in, ret);
285:
286: for (int i = 0; i < _bodyArgs.length; i++)
287: _bodyArgs[i].deserializeReply(in, args);
288:
289: in.require(XMLStreamReader.END_ELEMENT, null, "Body");
290:
291: in.nextTag();
292: in.require(XMLStreamReader.END_ELEMENT, null, "Envelope");
293:
294: return ret;
295: }
296:
297: public void writeWSDLBindingOperation(XMLStreamWriter out,
298: String soapNamespaceURI) throws XMLStreamException {
299: out.writeStartElement(WSDL_NAMESPACE, "operation");
300: out.writeAttribute("name", _messageName);
301: // XXX out.writeAttribute("parameterOrder", "");
302:
303: out.writeEmptyElement(soapNamespaceURI, "operation");
304: out.writeAttribute("soapAction", _soapAction);
305:
306: out.writeStartElement(WSDL_NAMESPACE, "input");
307: // XXX
308: out.writeEmptyElement(soapNamespaceURI, "body");
309: out.writeAttribute("use", "literal");
310:
311: out.writeEndElement(); // input
312:
313: if (!_isOneway) {
314: out.writeStartElement(WSDL_NAMESPACE, "output");
315: // XXX
316: out.writeEmptyElement(soapNamespaceURI, "body");
317: out.writeAttribute("use", "literal");
318:
319: out.writeEndElement(); // output
320: }
321:
322: out.writeEndElement(); // operation
323: }
324:
325: public void writeWSDLMessages(XMLStreamWriter out,
326: String soapNamespaceURI) throws XMLStreamException {
327: out.writeStartElement(WSDL_NAMESPACE, "message");
328: out.writeAttribute("name", _messageName);
329:
330: if (_bodyInputs > 0) {
331: out.writeEmptyElement(WSDL_NAMESPACE, "part");
332:
333: // XXX take name from @WebParam
334:
335: out.writeAttribute("name", _operationName);
336: out.writeAttribute("element", TARGET_NAMESPACE_PREFIX + ':'
337: + _operationName);
338: }
339:
340: out.writeEndElement(); // message
341:
342: if (!_isOneway) {
343: out.writeStartElement(WSDL_NAMESPACE, "message");
344: out.writeAttribute("name", _responseName);
345:
346: if (_returnMarshal != null) {
347: out.writeEmptyElement(WSDL_NAMESPACE, "part");
348: out.writeAttribute("name", _responseName);
349: out.writeAttribute("element", TARGET_NAMESPACE_PREFIX
350: + ':' + _responseName);
351: }
352:
353: out.writeEndElement(); // message
354: }
355: }
356:
357: public void writeWSDLOperation(XMLStreamWriter out,
358: String soapNamespaceURI) throws XMLStreamException {
359: out.writeStartElement(WSDL_NAMESPACE, "operation");
360: out.writeAttribute("name", _operationName);
361: // XXX out.writeAttribute("parameterOrder", "");
362:
363: out.writeEmptyElement(WSDL_NAMESPACE, "input");
364: out.writeAttribute("message", TARGET_NAMESPACE_PREFIX + ':'
365: + _messageName);
366:
367: if (!_isOneway) {
368: out.writeEmptyElement(WSDL_NAMESPACE, "output");
369: out.writeAttribute("message", TARGET_NAMESPACE_PREFIX + ':'
370: + _responseName);
371: }
372:
373: out.writeEndElement(); // operation
374: }
375:
376: public void writeSchema(XMLStreamWriter out, String namespace,
377: JAXBContextImpl context) throws XMLStreamException,
378: WebServiceException {
379: if (_bodyInputs > 0) {
380: QName elementName = new QName(namespace, _operationName);
381:
382: if (context.hasRootElement(elementName))
383: throw new WebServiceException(L.l(
384: "Duplicate element {0}", elementName));
385:
386: out.writeEmptyElement(XML_SCHEMA_PREFIX, "element",
387: W3C_XML_SCHEMA_NS_URI);
388: out.writeAttribute("name", _operationName);
389:
390: Property property = _bodyArgs[_inputArgument].getProperty();
391: String type = StaxUtil.qnameToString(out, property
392: .getSchemaType());
393:
394: out.writeAttribute("type", type);
395: }
396:
397: if (_returnMarshal != null) {
398: QName elementName = new QName(namespace, _responseName);
399:
400: if (context.hasRootElement(elementName))
401: throw new WebServiceException(L.l(
402: "Duplicate element {0}", elementName));
403:
404: out.writeEmptyElement(XML_SCHEMA_PREFIX, "element",
405: W3C_XML_SCHEMA_NS_URI);
406: out.writeAttribute("name", _responseName);
407:
408: QName schemaType = _returnMarshal.getProperty()
409: .getSchemaType();
410: String type = StaxUtil.qnameToString(out, schemaType);
411:
412: out.writeAttribute("type", type);
413: }
414: }
415:
416: public String toString() {
417: return "DocumentBareAction[" + _method.getName() + "]";
418: }
419: }
|