001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one
003: * or more contributor license agreements. See the NOTICE file
004: * distributed with this work for additional information
005: * regarding copyright ownership. The ASF licenses this file
006: * to you under the Apache License, Version 2.0 (the
007: * "License"); you may not use this file except in compliance
008: * with the License. You may obtain a copy of the License at
009: *
010: * http://www.apache.org/licenses/LICENSE-2.0
011: *
012: * Unless required by applicable law or agreed to in writing,
013: * software distributed under the License is distributed on an
014: * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015: * KIND, either express or implied. See the License for the
016: * specific language governing permissions and limitations
017: * under the License.
018: */
019: package org.apache.axis2.jaxws.marshaller.impl.alt;
020:
021: import org.apache.axis2.jaxws.ExceptionFactory;
022: import org.apache.axis2.jaxws.description.EndpointDescription;
023: import org.apache.axis2.jaxws.description.EndpointInterfaceDescription;
024: import org.apache.axis2.jaxws.description.OperationDescription;
025: import org.apache.axis2.jaxws.description.ParameterDescription;
026: import org.apache.axis2.jaxws.i18n.Messages;
027: import org.apache.axis2.jaxws.marshaller.MethodMarshaller;
028: import org.apache.axis2.jaxws.message.Message;
029: import org.apache.axis2.jaxws.message.Protocol;
030: import org.apache.axis2.jaxws.message.factory.MessageFactory;
031: import org.apache.axis2.jaxws.registry.FactoryRegistry;
032: import org.apache.axis2.jaxws.runtime.description.marshal.MarshalServiceRuntimeDescription;
033: import org.apache.commons.logging.Log;
034: import org.apache.commons.logging.LogFactory;
035:
036: import javax.jws.soap.SOAPBinding.Style;
037: import javax.xml.namespace.QName;
038: import javax.xml.ws.WebServiceException;
039: import java.util.List;
040: import java.util.TreeSet;
041:
042: public class RPCLitMethodMarshaller implements MethodMarshaller {
043:
044: private static Log log = LogFactory
045: .getLog(RPCLitMethodMarshaller.class);
046:
047: public RPCLitMethodMarshaller() {
048: super ();
049: }
050:
051: public Message marshalRequest(Object[] signatureArguments,
052: OperationDescription operationDesc)
053: throws WebServiceException {
054:
055: EndpointInterfaceDescription ed = operationDesc
056: .getEndpointInterfaceDescription();
057: EndpointDescription endpointDesc = ed.getEndpointDescription();
058: Protocol protocol = Protocol.getProtocolForBinding(endpointDesc
059: .getClientBindingID());
060:
061: // Note all exceptions are caught and rethrown with a WebServiceException
062: try {
063:
064: // Sample RPC message
065: // ..
066: // <soapenv:body>
067: // <m:op xmlns:m="urn://api">
068: // <param xsi:type="data:foo" >...</param>
069: // </m:op>
070: // </soapenv:body>
071: //
072: // Important points.
073: // 1) RPC has an operation element under the body. This is the name of the
074: // wsdl operation.
075: // 2) The data blocks are located underneath the operation element. (In doc/lit
076: // the data elements are underneath the body.
077: // 3) The name of the data blocks (param) are defined by the wsdl:part not the
078: // schema. Note that the param is unqualified...per WS-I BP.
079: // 4) The type of the data block (data:foo) is defined by schema (thus there is
080: // JAXB type rendering. Since we are using JAXB to marshal the data,
081: // we always generate an xsi:type attribute. This is an implemenation detail
082: // and is not defined by any spec.
083:
084: // Get the operation information
085: ParameterDescription[] pds = operationDesc
086: .getParameterDescriptions();
087: MarshalServiceRuntimeDescription marshalDesc = MethodMarshallerUtils
088: .getMarshalDesc(endpointDesc);
089: TreeSet<String> packages = marshalDesc.getPackages();
090:
091: // TODO This needs more work. We need to check inside holders of input params. We also
092: // may want to exclude header params from this check
093: //Validate input parameters for operation and make sure no input parameters are null.
094: //As per JAXWS Specification section 3.6.2.3 if a null value is passes as an argument
095: //to a method then an implementation MUST throw WebServiceException.
096: if (pds.length > 0) {
097: if (signatureArguments == null) {
098: throw ExceptionFactory
099: .makeWebServiceException(Messages
100: .getMessage(
101: "NullParamErr1",
102: "Input",
103: operationDesc
104: .getJavaMethodName(),
105: "rpc/lit"));
106: }
107: if (signatureArguments != null) {
108: for (Object argument : signatureArguments) {
109: if (argument == null) {
110: throw ExceptionFactory
111: .makeWebServiceException(Messages
112: .getMessage(
113: "NullParamErr1",
114: "Input",
115: operationDesc
116: .getJavaMethodName(),
117: "rpc/lit"));
118: }
119: }
120: }
121: }
122:
123: // Create the message
124: MessageFactory mf = (MessageFactory) FactoryRegistry
125: .getFactory(MessageFactory.class);
126: Message m = mf.create(protocol);
127:
128: // Indicate the style and operation element name. This triggers the message to
129: // put the data blocks underneath the operation element
130: m.setStyle(Style.RPC);
131: m.setOperationElement(getRPCOperationQName(operationDesc,
132: true));
133:
134: // The input object represent the signature arguments.
135: // Signature arguments are both holders and non-holders
136: // Convert the signature into a list of JAXB objects for marshalling
137: List<PDElement> pdeList = MethodMarshallerUtils
138: .getPDElements(marshalDesc, pds,
139: signatureArguments, true, // input
140: false, true); // use partName since this is rpc/lit
141:
142: // We want to use "by Java Type" marshalling for
143: // all body elements and all non-JAXB objects
144: for (PDElement pde : pdeList) {
145: ParameterDescription pd = pde.getParam();
146: Class type = pd.getParameterActualType();
147: if (!pd.isHeader()
148: || MethodMarshallerUtils.isNotJAXBRootElement(
149: type, marshalDesc)) {
150: pde.setByJavaTypeClass(type);
151: }
152: }
153:
154: // Put values onto the message
155: MethodMarshallerUtils.toMessage(pdeList, m, packages);
156:
157: return m;
158: } catch (Exception e) {
159: throw ExceptionFactory.makeWebServiceException(e);
160: }
161: }
162:
163: public Object[] demarshalRequest(Message message,
164: OperationDescription operationDesc)
165: throws WebServiceException {
166:
167: EndpointInterfaceDescription ed = operationDesc
168: .getEndpointInterfaceDescription();
169: EndpointDescription endpointDesc = ed.getEndpointDescription();
170:
171: // Note all exceptions are caught and rethrown with a WebServiceException
172: try {
173: // Sample RPC message
174: // ..
175: // <soapenv:body>
176: // <m:op xmlns:m="urn://api">
177: // <param xsi:type="data:foo" >...</param>
178: // </m:op>
179: // </soapenv:body>
180: //
181: // Important points.
182: // 1) RPC has an operation element under the body. This is the name of the
183: // wsdl operation.
184: // 2) The data blocks are located underneath the operation element. (In doc/lit
185: // the data elements are underneath the body.
186: // 3) The name of the data blocks (param) are defined by the wsdl:part not the
187: // schema. Note that it is unqualified per WSI-BP
188: // 4) The type of the data block (data:foo) is defined by schema (thus there is
189: // JAXB type rendering.
190: // 5) We always send an xsi:type, but other vendor's may not.
191: // Get the operation information
192: ParameterDescription[] pds = operationDesc
193: .getParameterDescriptions();
194: MarshalServiceRuntimeDescription marshalDesc = MethodMarshallerUtils
195: .getMarshalDesc(endpointDesc);
196: TreeSet<String> packages = marshalDesc.getPackages();
197:
198: // Indicate that the style is RPC. This is important so that the message understands
199: // that the data blocks are underneath the operation element
200: message.setStyle(Style.RPC);
201:
202: // We want to use "by Java Type" unmarshalling for
203: // all body elements and all non-JAXB objects
204: Class[] javaTypes = new Class[pds.length];
205: for (int i = 0; i < pds.length; i++) {
206: ParameterDescription pd = pds[i];
207: Class type = pd.getParameterActualType();
208: if (!pd.isHeader()
209: || MethodMarshallerUtils.isNotJAXBRootElement(
210: type, marshalDesc)) {
211: javaTypes[i] = type;
212: }
213: }
214:
215: // Unmarshal the ParamValues from the Message
216: List<PDElement> pvList = MethodMarshallerUtils
217: .getPDElements(pds, message, packages, true, // input
218: false, javaTypes); // unmarshal by type
219:
220: // Build the signature arguments
221: Object[] sigArguments = MethodMarshallerUtils
222: .createRequestSignatureArgs(pds, pvList);
223:
224: // TODO This needs more work. We need to check inside holders of input params. We also
225: // may want to exclude header params from this check
226: //Validate input parameters for operation and make sure no input parameters are null.
227: //As per JAXWS Specification section 3.6.2.3 if a null value is passes as an argument
228: //to a method then an implementation MUST throw WebServiceException.
229: if (sigArguments != null) {
230: for (Object argument : sigArguments) {
231: if (argument == null) {
232: throw ExceptionFactory
233: .makeWebServiceException(Messages
234: .getMessage(
235: "NullParamErr1",
236: "Input",
237: operationDesc
238: .getJavaMethodName(),
239: "rpc/lit"));
240:
241: }
242: }
243: }
244: return sigArguments;
245: } catch (Exception e) {
246: throw ExceptionFactory.makeWebServiceException(e);
247: }
248: }
249:
250: public Message marshalResponse(Object returnObject,
251: Object[] signatureArgs, OperationDescription operationDesc,
252: Protocol protocol) throws WebServiceException {
253:
254: EndpointInterfaceDescription ed = operationDesc
255: .getEndpointInterfaceDescription();
256: EndpointDescription endpointDesc = ed.getEndpointDescription();
257: // We want to respond with the same protocol as the request,
258: // It the protocol is null, then use the Protocol defined by the binding
259: if (protocol == null) {
260: protocol = Protocol.getProtocolForBinding(endpointDesc
261: .getBindingType());
262: }
263:
264: // Note all exceptions are caught and rethrown with a WebServiceException
265: try {
266: // Sample RPC message
267: // ..
268: // <soapenv:body>
269: // <m:opResponse xmlns:m="urn://api">
270: // <param xsi:type="data:foo" >...</param>
271: // </m:op>
272: // </soapenv:body>
273: //
274: // Important points.
275: // 1) RPC has an operation element under the body. This is the name of the
276: // wsdl operation.
277: // 2) The data blocks are located underneath the operation element. (In doc/lit
278: // the data elements are underneath the body.
279: // 3) The name of the data blocks (param) are defined by the wsdl:part not the
280: // schema. Note that it is unqualified.
281: // 4) The type of the data block (data:foo) is defined by schema (thus there is
282: // JAXB type rendering. Since we are using JAXB to marshal the data,
283: // we always generate an xsi:type attribute. This is an implemenation detail
284: // and is not defined by any spec.
285:
286: // Get the operation information
287: ParameterDescription[] pds = operationDesc
288: .getParameterDescriptions();
289: MarshalServiceRuntimeDescription marshalDesc = MethodMarshallerUtils
290: .getMarshalDesc(endpointDesc);
291: TreeSet<String> packages = marshalDesc.getPackages();
292:
293: // Create the message
294: MessageFactory mf = (MessageFactory) FactoryRegistry
295: .getFactory(MessageFactory.class);
296: Message m = mf.create(protocol);
297:
298: // Indicate the style and operation element name. This triggers the message to
299: // put the data blocks underneath the operation element
300: m.setStyle(Style.RPC);
301:
302: QName rpcOpQName = getRPCOperationQName(operationDesc,
303: false);
304: String localPart = rpcOpQName.getLocalPart() + "Response";
305: QName responseOp = new QName(rpcOpQName.getNamespaceURI(),
306: localPart, rpcOpQName.getPrefix());
307: m.setOperationElement(responseOp);
308:
309: // Put the return object onto the message
310: Class returnType = operationDesc.getResultActualType();
311: String returnNS = null;
312: String returnLocalPart = null;
313: if (operationDesc.isResultHeader()) {
314: returnNS = operationDesc.getResultTargetNamespace();
315: returnLocalPart = operationDesc.getResultName();
316: } else {
317: returnNS = ""; // According to WSI BP the body part is unqualified
318: returnLocalPart = operationDesc.getResultPartName();
319: }
320:
321: if (returnType != void.class) {
322:
323: // TODO should we allow null if the return is a header?
324: //Validate input parameters for operation and make sure no input parameters are null.
325: //As per JAXWS Specification section 3.6.2.3 if a null value is passes as an argument
326: //to a method then an implementation MUST throw WebServiceException.
327: if (returnObject == null) {
328: throw ExceptionFactory
329: .makeWebServiceException(Messages
330: .getMessage(
331: "NullParamErr1",
332: "Return",
333: operationDesc
334: .getJavaMethodName(),
335: "rpc/lit"));
336:
337: }
338: Element returnElement = null;
339: QName returnQName = new QName(returnNS, returnLocalPart);
340: if (marshalDesc.getAnnotationDesc(returnType)
341: .hasXmlRootElement()) {
342: returnElement = new Element(returnObject,
343: returnQName);
344: } else {
345: returnElement = new Element(returnObject,
346: returnQName, returnType);
347: }
348:
349: // Use marshalling by java type if necessary
350: Class byJavaType = null;
351: if (!operationDesc.isResultHeader()
352: || MethodMarshallerUtils.isNotJAXBRootElement(
353: returnType, marshalDesc)) {
354: byJavaType = returnType;
355: }
356: MethodMarshallerUtils.toMessage(returnElement,
357: returnType, operationDesc.isListType(),
358: marshalDesc, m, byJavaType, operationDesc
359: .isResultHeader());
360: }
361:
362: // Convert the holder objects into a list of JAXB objects for marshalling
363: List<PDElement> pdeList = MethodMarshallerUtils
364: .getPDElements(marshalDesc, pds, signatureArgs,
365: false, // output
366: false, true); // use partName since this is rpc/lit
367:
368: // We want to use "by Java Type" marshalling for
369: // all body elements and all non-JAXB objects
370: for (PDElement pde : pdeList) {
371: ParameterDescription pd = pde.getParam();
372: Class type = pd.getParameterActualType();
373: if (!pd.isHeader()
374: || MethodMarshallerUtils.isNotJAXBRootElement(
375: type, marshalDesc)) {
376: pde.setByJavaTypeClass(type);
377: }
378: }
379: // TODO Should we check for null output body values? Should we check for null output header values ?
380: // Put values onto the message
381: MethodMarshallerUtils.toMessage(pdeList, m, packages);
382:
383: return m;
384: } catch (Exception e) {
385: throw ExceptionFactory.makeWebServiceException(e);
386: }
387: }
388:
389: public Object demarshalResponse(Message message,
390: Object[] signatureArgs, OperationDescription operationDesc)
391: throws WebServiceException {
392:
393: EndpointInterfaceDescription ed = operationDesc
394: .getEndpointInterfaceDescription();
395: EndpointDescription endpointDesc = ed.getEndpointDescription();
396:
397: // Note all exceptions are caught and rethrown with a WebServiceException
398: try {
399: // Sample RPC message
400: // ..
401: // <soapenv:body>
402: // <m:opResponse xmlns:m="urn://api">
403: // <param xsi:type="data:foo" >...</param>
404: // </m:op>
405: // </soapenv:body>
406: //
407: // Important points.
408: // 1) RPC has an operation element under the body. This is the name of the
409: // wsdl operation.
410: // 2) The data blocks are located underneath the operation element. (In doc/lit
411: // the data elements are underneath the body.
412: // 3) The name of the data blocks (param) are defined by the wsdl:part not the
413: // schema. Note that it is unqualified per WSI-BP
414: // 4) The type of the data block (data:foo) is defined by schema (thus there is
415: // JAXB type rendering.
416: // 5) We always send an xsi:type, but other vendor's may not.
417: // Get the operation information
418: ParameterDescription[] pds = operationDesc
419: .getParameterDescriptions();
420: MarshalServiceRuntimeDescription marshalDesc = MethodMarshallerUtils
421: .getMarshalDesc(endpointDesc);
422: TreeSet<String> packages = marshalDesc.getPackages();
423:
424: // Indicate that the style is RPC. This is important so that the message understands
425: // that the data blocks are underneath the operation element
426: message.setStyle(Style.RPC);
427:
428: // Get the return value.
429: Class returnType = operationDesc.getResultActualType();
430: Object returnValue = null;
431: boolean hasReturnInBody = false;
432: if (returnType != void.class) {
433: // If the webresult is in the header, we need the name of the header so that we can find it.
434: Element returnElement = null;
435: // Use "byJavaType" unmarshalling if necessary
436: Class byJavaType = null;
437: if (!operationDesc.isResultHeader()
438: || MethodMarshallerUtils.isNotJAXBRootElement(
439: returnType, marshalDesc)) {
440: byJavaType = returnType;
441: }
442: if (operationDesc.isResultHeader()) {
443: returnElement = MethodMarshallerUtils
444: .getReturnElement(
445: packages,
446: message,
447: byJavaType,
448: operationDesc.isListType(),
449: true,
450: operationDesc
451: .getResultTargetNamespace(),
452: operationDesc.getResultPartName(),
453: MethodMarshallerUtils
454: .numOutputBodyParams(pds) > 0);
455:
456: } else {
457: returnElement = MethodMarshallerUtils
458: .getReturnElement(
459: packages,
460: message,
461: byJavaType,
462: operationDesc.isListType(),
463: false,
464: null,
465: null,
466: MethodMarshallerUtils
467: .numOutputBodyParams(pds) > 0);
468: hasReturnInBody = true;
469: }
470: returnValue = returnElement.getTypeValue();
471: // TODO should we allow null if the return is a header?
472: //Validate input parameters for operation and make sure no input parameters are null.
473: //As per JAXWS Specification section 3.6.2.3 if a null value is passes as an argument
474: //to a method then an implementation MUST throw WebServiceException.
475: if (returnValue == null) {
476: throw ExceptionFactory
477: .makeWebServiceException(Messages
478: .getMessage(
479: "NullParamErr1",
480: "Return",
481: operationDesc
482: .getJavaMethodName(),
483: "rpc/lit"));
484: }
485: }
486:
487: // We want to use "by Java Type" unmarshalling for
488: // all body elements and all non-JAXB objects
489: Class[] javaTypes = new Class[pds.length];
490: for (int i = 0; i < pds.length; i++) {
491: ParameterDescription pd = pds[i];
492: Class type = pd.getParameterActualType();
493: if (!pd.isHeader()
494: || MethodMarshallerUtils.isNotJAXBRootElement(
495: type, marshalDesc)) {
496: javaTypes[i] = type;
497: }
498: }
499:
500: // Unmarshall the ParamValues from the Message
501: List<PDElement> pvList = MethodMarshallerUtils
502: .getPDElements(pds, message, packages, false, // output
503: hasReturnInBody, javaTypes); // unmarshal by type
504:
505: // TODO Should we check for null output body values? Should we check for null output header values ?
506:
507: // Populate the response Holders
508: MethodMarshallerUtils.updateResponseSignatureArgs(pds,
509: pvList, signatureArgs);
510:
511: return returnValue;
512: } catch (Exception e) {
513: throw ExceptionFactory.makeWebServiceException(e);
514: }
515: }
516:
517: public Message marshalFaultResponse(Throwable throwable,
518: OperationDescription operationDesc, Protocol protocol)
519: throws WebServiceException {
520:
521: EndpointInterfaceDescription ed = operationDesc
522: .getEndpointInterfaceDescription();
523: EndpointDescription endpointDesc = ed.getEndpointDescription();
524: MarshalServiceRuntimeDescription marshalDesc = MethodMarshallerUtils
525: .getMarshalDesc(endpointDesc);
526: TreeSet<String> packages = marshalDesc.getPackages();
527:
528: // We want to respond with the same protocol as the request,
529: // It the protocol is null, then use the Protocol defined by the binding
530: if (protocol == null) {
531: protocol = Protocol.getProtocolForBinding(endpointDesc
532: .getBindingType());
533: }
534:
535: // Note all exceptions are caught and rethrown with a WebServiceException
536: try {
537: // Create the message
538: MessageFactory mf = (MessageFactory) FactoryRegistry
539: .getFactory(MessageFactory.class);
540: Message m = mf.create(protocol);
541:
542: // Put the fault onto the message
543: MethodMarshallerUtils.marshalFaultResponse(throwable,
544: marshalDesc, operationDesc, m);
545: return m;
546: } catch (Exception e) {
547: throw ExceptionFactory.makeWebServiceException(e);
548: }
549: }
550:
551: public Throwable demarshalFaultResponse(Message message,
552: OperationDescription operationDesc)
553: throws WebServiceException {
554:
555: EndpointInterfaceDescription ed = operationDesc
556: .getEndpointInterfaceDescription();
557: EndpointDescription endpointDesc = ed.getEndpointDescription();
558: MarshalServiceRuntimeDescription marshalDesc = MethodMarshallerUtils
559: .getMarshalDesc(endpointDesc);
560:
561: // Note all exceptions are caught and rethrown with a WebServiceException
562: try {
563: Throwable t = MethodMarshallerUtils.demarshalFaultResponse(
564: operationDesc, marshalDesc, message);
565: return t;
566: } catch (Exception e) {
567: throw ExceptionFactory.makeWebServiceException(e);
568: }
569: }
570:
571: /**
572: * @param opDesc
573: * @return qualified qname to use in the rpc message to represent the operation (per WSI BP)
574: */
575: private static QName getRPCOperationQName(
576: OperationDescription opDesc, boolean isRequest) {
577: QName qName = opDesc.getName();
578:
579: String localPart = qName.getLocalPart();
580: String uri = null;
581:
582: if (isRequest) {
583: uri = opDesc.getBindingInputNamespace();
584: } else {
585: uri = opDesc.getBindingOutputNamespace();
586: }
587:
588: String prefix = "rpcOp"; // Prefer using an actual prefix
589:
590: qName = new QName(uri, localPart, prefix);
591: return qName;
592: }
593:
594: }
|