View Javadoc
1   /*
2    * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved.
3    * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4    *
5    * This code is free software; you can redistribute it and/or modify it
6    * under the terms of the GNU General Public License version 2 only, as
7    * published by the Free Software Foundation.  Oracle designates this
8    * particular file as subject to the "Classpath" exception as provided
9    * by Oracle in the LICENSE file that accompanied this code.
10   *
11   * This code is distributed in the hope that it will be useful, but WITHOUT
12   * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13   * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14   * version 2 for more details (a copy is included in the LICENSE file that
15   * accompanied this code).
16   *
17   * You should have received a copy of the GNU General Public License version
18   * 2 along with this work; if not, write to the Free Software Foundation,
19   * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20   *
21   * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22   * or visit www.oracle.com if you need additional information or have any
23   * questions.
24   */
25  
26  package com.sun.xml.internal.ws.client.sei;
27  
28  import com.sun.xml.internal.ws.api.SOAPVersion;
29  import com.sun.xml.internal.ws.api.message.Attachment;
30  import com.sun.xml.internal.ws.api.message.AttachmentSet;
31  import com.sun.xml.internal.ws.api.message.Message;
32  import com.sun.xml.internal.ws.api.model.ParameterBinding;
33  import com.sun.xml.internal.ws.api.streaming.XMLStreamReaderFactory;
34  import com.sun.xml.internal.ws.message.AttachmentUnmarshallerImpl;
35  import com.sun.xml.internal.ws.model.ParameterImpl;
36  import com.sun.xml.internal.ws.model.WrapperParameter;
37  import com.sun.xml.internal.ws.resources.ServerMessages;
38  import com.sun.xml.internal.ws.spi.db.RepeatedElementBridge;
39  import com.sun.xml.internal.ws.spi.db.XMLBridge;
40  import com.sun.xml.internal.ws.spi.db.DatabindingException;
41  import com.sun.xml.internal.ws.spi.db.PropertyAccessor;
42  import com.sun.xml.internal.ws.spi.db.WrapperComposite;
43  import com.sun.xml.internal.ws.streaming.XMLStreamReaderUtil;
44  import com.sun.xml.internal.ws.encoding.StringDataContentHandler;
45  import com.sun.xml.internal.ws.encoding.DataHandlerDataSource;
46  
47  import javax.activation.DataHandler;
48  import javax.imageio.ImageIO;
49  import javax.xml.bind.JAXBException;
50  import javax.xml.namespace.QName;
51  import javax.xml.soap.SOAPException;
52  import javax.xml.soap.SOAPFault;
53  import javax.xml.stream.XMLStreamException;
54  import javax.xml.stream.XMLStreamReader;
55  import javax.xml.stream.XMLStreamConstants;
56  import javax.xml.transform.Source;
57  import javax.xml.ws.Holder;
58  import javax.xml.ws.WebServiceException;
59  import javax.xml.ws.soap.SOAPFaultException;
60  import java.awt.Image;
61  import java.io.IOException;
62  import java.io.InputStream;
63  import java.io.UnsupportedEncodingException;
64  import java.lang.reflect.Type;
65  import java.util.ArrayList;
66  import java.util.Collection;
67  import java.util.HashMap;
68  import java.util.Iterator;
69  import java.util.List;
70  import java.util.Map;
71  
72  /**
73   * Reads a response {@link Message}, disassembles it, and moves obtained Java values
74   * to the expected places.
75   *
76   * @author Kohsuke Kawaguchi
77   * @author Jitendra Kotamraju
78   */
79  public abstract class ResponseBuilder {
80      /**
81       * Reads a response {@link Message}, disassembles it, and moves obtained Java values
82       * to the expected places.
83       *
84       * @param reply
85       *      The reply {@link Message} to be de-composed.
86       * @param args
87       *      The Java arguments given to the SEI method invocation.
88       *      Some parts of the reply message may be set to {@link Holder}s in the arguments.
89       * @return
90       *      If a part of the reply message is returned as a return value from
91       *      the SEI method, this method returns that value. Otherwise null.
92       * @throws JAXBException
93       *      if there's an error during unmarshalling the reply message.
94       * @throws XMLStreamException
95       *      if there's an error during unmarshalling the reply message.
96       */
97      public abstract Object readResponse(Message reply, Object[] args) throws JAXBException, XMLStreamException;
98  
99      static final class WrappedPartBuilder {
100         private final XMLBridge bridge;
101         private final ValueSetter setter;
102         public WrappedPartBuilder(XMLBridge bridge, ValueSetter setter) {
103             this.bridge = bridge;
104             this.setter = setter;
105         }
106         final Object readResponse(Object[] args, XMLStreamReader r, AttachmentSet att) throws JAXBException {
107             Object obj;
108             AttachmentUnmarshallerImpl au = (att != null)?new AttachmentUnmarshallerImpl(att):null;
109             if (bridge instanceof RepeatedElementBridge) {
110                 RepeatedElementBridge rbridge = (RepeatedElementBridge)bridge;
111                 ArrayList list = new ArrayList();
112                 QName name = r.getName();
113                 while (r.getEventType()==XMLStreamReader.START_ELEMENT && name.equals(r.getName())) {
114                     list.add(rbridge.unmarshal(r, au));
115                     XMLStreamReaderUtil.toNextTag(r, name);
116                 }
117                 obj = rbridge.collectionHandler().convert(list);
118             } else {
119                 obj = bridge.unmarshal(r, au);
120             }
121             return setter.put(obj,args);
122         }
123     }
124     /**
125      * {@link ResponseBuilder.PartBuilder} keyed by the element name (inside the wrapper element.)
126      */
127     protected Map<QName,WrappedPartBuilder> wrappedParts = null;
128     protected QName wrapperName;
129 
130     protected Object readWrappedResponse(Message msg, Object[] args) throws JAXBException, XMLStreamException {
131         Object retVal = null;
132 
133         if (!msg.hasPayload()) {
134             throw new WebServiceException("No payload. Expecting payload with "+wrapperName+" element");
135         }
136         XMLStreamReader reader = msg.readPayload();
137         XMLStreamReaderUtil.verifyTag(reader,wrapperName);
138         reader.nextTag();
139 
140         while(reader.getEventType()==XMLStreamReader.START_ELEMENT) {
141             // TODO: QName has a performance issue
142             WrappedPartBuilder part = wrappedParts.get(reader.getName());
143             if(part==null) {
144                 // no corresponding part found. ignore
145                 XMLStreamReaderUtil.skipElement(reader);
146                 reader.nextTag();
147             } else {
148                 Object o = part.readResponse(args,reader, msg.getAttachments());
149                 // there's only at most one ResponseBuilder that returns a value.
150                 if(o!=null) {
151                     assert retVal==null;
152                     retVal = o;
153                 }
154             }
155             // skip any whitespace
156             if (reader.getEventType() != XMLStreamConstants.START_ELEMENT &&
157                     reader.getEventType() != XMLStreamConstants.END_ELEMENT) {
158                 XMLStreamReaderUtil.nextElementContent(reader);
159             }
160         }
161 
162         // we are done with the body
163         reader.close();
164         XMLStreamReaderFactory.recycle(reader);
165 
166         return retVal;
167     }
168 
169     static final class None extends ResponseBuilder {
170         private None(){
171         }
172         @Override
173         public Object readResponse(Message msg, Object[] args) {
174             msg.consume();
175             return null;
176         }
177     }
178 
179     /**
180      * The singleton instance that produces null return value.
181      * Used for operations that doesn't have any output.
182      */
183     public final static ResponseBuilder NONE = new None();
184 
185     /**
186      * Returns the 'uninitialized' value for the given type.
187      *
188      * <p>
189      * For primitive types, it's '0', and for reference types, it's null.
190      */
191     @SuppressWarnings("element-type-mismatch")
192     public static Object getVMUninitializedValue(Type type) {
193         // if this map returns null, that means the 'type' is a reference type,
194         // in which case 'null' is the correct null value, so this code is correct.
195         return primitiveUninitializedValues.get(type);
196     }
197 
198     private static final Map<Class,Object> primitiveUninitializedValues = new HashMap<Class, Object>();
199 
200     static {
201         Map<Class, Object> m = primitiveUninitializedValues;
202         m.put(int.class,(int)0);
203         m.put(char.class,(char)0);
204         m.put(byte.class,(byte)0);
205         m.put(short.class,(short)0);
206         m.put(long.class,(long)0);
207         m.put(float.class,(float)0);
208         m.put(double.class,(double)0);
209     }
210 
211     /**
212      * {@link ResponseBuilder} that sets the VM uninitialized value to the type.
213      */
214     public static final class NullSetter extends ResponseBuilder {
215         private final ValueSetter setter;
216         private final Object nullValue;
217 
218         public NullSetter(ValueSetter setter, Object nullValue){
219             assert setter!=null;
220             this.nullValue = nullValue;
221             this.setter = setter;
222         }
223         @Override
224         public Object readResponse(Message msg, Object[] args) {
225             return setter.put(nullValue, args);
226         }
227     }
228 
229     /**
230      * {@link ResponseBuilder} that is a composition of multiple
231      * {@link ResponseBuilder}s.
232      *
233      * <p>
234      * Sometimes we need to look at multiple parts of the reply message
235      * (say, two header params, one body param, and three attachments, etc.)
236      * and that's when this object is used to combine multiple {@link ResponseBuilder}s
237      * (that each responsible for handling one part).
238      *
239      * <p>
240      * The model guarantees that only at most one {@link ResponseBuilder} will
241      * return a value as a return value (and everything else has to go to
242      * {@link Holder}s.)
243      */
244     public static final class Composite extends ResponseBuilder {
245         private final ResponseBuilder[] builders;
246 
247         public Composite(ResponseBuilder... builders) {
248             this.builders = builders;
249         }
250 
251         public Composite(Collection<? extends ResponseBuilder> builders) {
252             this(builders.toArray(new ResponseBuilder[builders.size()]));
253         }
254 
255         @Override
256         public Object readResponse(Message msg, Object[] args) throws JAXBException, XMLStreamException {
257             Object retVal = null;
258             for (ResponseBuilder builder : builders) {
259                 Object r = builder.readResponse(msg,args);
260                 // there's only at most one ResponseBuilder that returns a value.
261                 if(r!=null) {
262                     assert retVal==null;
263                     retVal = r;
264                 }
265             }
266             return retVal;
267         }
268     }
269 
270     /**
271      * Reads an Attachment into a Java parameter.
272      */
273     public static abstract class AttachmentBuilder extends ResponseBuilder {
274         protected final ValueSetter setter;
275         protected final ParameterImpl param;
276         private final String pname;
277         private final String pname1;
278 
279         AttachmentBuilder(ParameterImpl param, ValueSetter setter) {
280             this.setter = setter;
281             this.param = param;
282             this.pname = param.getPartName();
283             this.pname1 = "<"+pname;
284         }
285 
286         /**
287          * Creates an AttachmentBuilder based on the parameter type
288          *
289          * @param param
290          *      runtime Parameter that abstracts the annotated java parameter
291          * @param setter
292          *      specifies how the obtained value is set into the argument. Takes
293          *      care of Holder arguments.
294          */
295         public static ResponseBuilder createAttachmentBuilder(ParameterImpl param, ValueSetter setter) {
296             Class type = (Class)param.getTypeInfo().type;
297             if (DataHandler.class.isAssignableFrom(type)) {
298                 return new DataHandlerBuilder(param, setter);
299             } else if (byte[].class==type) {
300                 return new ByteArrayBuilder(param, setter);
301             } else if(Source.class.isAssignableFrom(type)) {
302                 return new SourceBuilder(param, setter);
303             } else if(Image.class.isAssignableFrom(type)) {
304                 return new ImageBuilder(param, setter);
305             } else if(InputStream.class==type) {
306                 return new InputStreamBuilder(param, setter);
307             } else if(isXMLMimeType(param.getBinding().getMimeType())) {
308                 return new JAXBBuilder(param, setter);
309             } else if(String.class.isAssignableFrom(type)) {
310                 return new StringBuilder(param, setter);
311             } else {
312                 throw new UnsupportedOperationException("Unexpected Attachment type ="+type);
313             }
314         }
315 
316         @Override
317         public Object readResponse(Message msg, Object[] args) throws JAXBException, XMLStreamException {
318             // TODO not to loop
319             for (Attachment att : msg.getAttachments()) {
320                 String part = getWSDLPartName(att);
321                 if (part == null) {
322                     continue;
323                 }
324                 if(part.equals(pname) || part.equals(pname1)){
325                     return mapAttachment(att, args);
326                 }
327             }
328             return null;
329         }
330 
331         abstract Object mapAttachment(Attachment att, Object[] args) throws JAXBException;
332     }
333 
334     private static final class DataHandlerBuilder extends AttachmentBuilder {
335         DataHandlerBuilder(ParameterImpl param, ValueSetter setter) {
336             super(param, setter);
337         }
338 
339         @Override
340         Object mapAttachment(Attachment att, Object[] args) {
341             return setter.put(att.asDataHandler(), args);
342         }
343     }
344 
345     private static final class StringBuilder extends AttachmentBuilder {
346         StringBuilder(ParameterImpl param, ValueSetter setter) {
347             super(param, setter);
348         }
349 
350         @Override
351         Object mapAttachment(Attachment att, Object[] args) {
352             att.getContentType();
353             StringDataContentHandler sdh = new StringDataContentHandler();
354             try {
355                 String str = (String)sdh.getContent(new DataHandlerDataSource(att.asDataHandler()));
356                 return setter.put(str, args);
357             } catch(Exception e) {
358                 throw new WebServiceException(e);
359             }
360 
361         }
362     }
363 
364     private static final class ByteArrayBuilder extends AttachmentBuilder {
365         ByteArrayBuilder(ParameterImpl param, ValueSetter setter) {
366             super(param, setter);
367         }
368 
369         @Override
370         Object mapAttachment(Attachment att, Object[] args) {
371             return setter.put(att.asByteArray(), args);
372         }
373     }
374 
375     private static final class SourceBuilder extends AttachmentBuilder {
376         SourceBuilder(ParameterImpl param, ValueSetter setter) {
377             super(param, setter);
378         }
379 
380         @Override
381         Object mapAttachment(Attachment att, Object[] args) {
382             return setter.put(att.asSource(), args);
383         }
384     }
385 
386     private static final class ImageBuilder extends AttachmentBuilder {
387         ImageBuilder(ParameterImpl param, ValueSetter setter) {
388             super(param, setter);
389         }
390 
391         @Override
392         Object mapAttachment(Attachment att, Object[] args) {
393             Image image;
394             InputStream is = null;
395             try {
396                 is = att.asInputStream();
397                 image = ImageIO.read(is);
398             } catch(IOException ioe) {
399                 throw new WebServiceException(ioe);
400             } finally {
401                 if (is != null) {
402                     try {
403                         is.close();
404                     } catch(IOException ioe) {
405                         throw new WebServiceException(ioe);
406                     }
407                 }
408             }
409             return setter.put(image, args);
410         }
411     }
412 
413     private static final class InputStreamBuilder extends AttachmentBuilder {
414         InputStreamBuilder(ParameterImpl param, ValueSetter setter) {
415             super(param, setter);
416         }
417 
418         @Override
419         Object mapAttachment(Attachment att, Object[] args) {
420             return setter.put(att.asInputStream(), args);
421         }
422     }
423 
424     private static final class JAXBBuilder extends AttachmentBuilder {
425         JAXBBuilder(ParameterImpl param, ValueSetter setter) {
426             super(param, setter);
427         }
428 
429         @Override
430         Object mapAttachment(Attachment att, Object[] args) throws JAXBException {
431             Object obj = param.getXMLBridge().unmarshal(att.asInputStream());
432             return setter.put(obj, args);
433         }
434     }
435 
436     /**
437      * Gets the WSDL part name of this attachment.
438      *
439      * <p>
440      * According to WSI AP 1.0
441      * <PRE>
442      * 3.8 Value-space of Content-Id Header
443      *   Definition: content-id part encoding
444      *   The "content-id part encoding" consists of the concatenation of:
445      * The value of the name attribute of the wsdl:part element referenced by the mime:content, in which characters disallowed in content-id headers (non-ASCII characters as represented by code points above 0x7F) are escaped as follows:
446      *     o Each disallowed character is converted to UTF-8 as one or more bytes.
447      *     o Any bytes corresponding to a disallowed character are escaped with the URI escaping mechanism (that is, converted to %HH, where HH is the hexadecimal notation of the byte value).
448      *     o The original character is replaced by the resulting character sequence.
449      * The character '=' (0x3D).
450      * A globally unique value such as a UUID.
451      * The character '@' (0x40).
452      * A valid domain name under the authority of the entity constructing the message.
453      * </PRE>
454      *
455      * So a wsdl:part fooPart will be encoded as:
456      *      <fooPart=somereallybignumberlikeauuid@example.com>
457      *
458      * @return null
459      *      if the parsing fails.
460      */
461     @SuppressWarnings("FinalStaticMethod")
462     public static final String getWSDLPartName(com.sun.xml.internal.ws.api.message.Attachment att){
463         String cId = att.getContentId();
464 
465         int index = cId.lastIndexOf('@', cId.length());
466         if(index == -1){
467             return null;
468         }
469         String localPart = cId.substring(0, index);
470         index = localPart.lastIndexOf('=', localPart.length());
471         if(index == -1){
472             return null;
473         }
474         try {
475             return java.net.URLDecoder.decode(localPart.substring(0, index), "UTF-8");
476         } catch (UnsupportedEncodingException e) {
477             throw new WebServiceException(e);
478         }
479     }
480 
481 
482     /**
483      * Reads a header into a JAXB object.
484      */
485     public static final class Header extends ResponseBuilder {
486         private final XMLBridge<?> bridge;
487         private final ValueSetter setter;
488         private final QName headerName;
489         private final SOAPVersion soapVersion;
490 
491         /**
492          * @param soapVersion
493          *      SOAP1.1 or 1.2
494          * @param name
495          *      The name of the header element.
496          * @param bridge
497          *      specifies how to unmarshal a header into a JAXB object.
498          * @param setter
499          *      specifies how the obtained value is returned to the client.
500          */
501         public Header(SOAPVersion soapVersion, QName name, XMLBridge<?> bridge, ValueSetter setter) {
502             this.soapVersion = soapVersion;
503             this.headerName = name;
504             this.bridge = bridge;
505             this.setter = setter;
506         }
507 
508         public Header(SOAPVersion soapVersion, ParameterImpl param, ValueSetter setter) {
509             this(soapVersion,
510                 param.getTypeInfo().tagName,
511                 param.getXMLBridge(),
512                 setter);
513             assert param.getOutBinding()== ParameterBinding.HEADER;
514         }
515 
516         private SOAPFaultException createDuplicateHeaderException() {
517             try {
518                 SOAPFault fault = soapVersion.getSOAPFactory().createFault();
519                 fault.setFaultCode(soapVersion.faultCodeServer);
520                 fault.setFaultString(ServerMessages.DUPLICATE_PORT_KNOWN_HEADER(headerName));
521                 return new SOAPFaultException(fault);
522             } catch(SOAPException e) {
523                 throw new WebServiceException(e);
524             }
525         }
526 
527         @Override
528         public Object readResponse(Message msg, Object[] args) throws JAXBException {
529             com.sun.xml.internal.ws.api.message.Header header = null;
530             Iterator<com.sun.xml.internal.ws.api.message.Header> it =
531                 msg.getHeaders().getHeaders(headerName,true);
532             if (it.hasNext()) {
533                 header = it.next();
534                 if (it.hasNext()) {
535                     throw createDuplicateHeaderException();
536                 }
537             }
538 
539             if(header!=null)
540                 return setter.put( header.readAsJAXB(bridge), args );
541             else
542                 // header not found.
543                 return null;
544         }
545     }
546 
547     /**
548      * Reads the whole payload into a single JAXB bean.
549      */
550     public static final class Body extends ResponseBuilder {
551         private final XMLBridge<?> bridge;
552         private final ValueSetter setter;
553 
554         /**
555          * @param bridge
556          *      specifies how to unmarshal the payload into a JAXB object.
557          * @param setter
558          *      specifies how the obtained value is returned to the client.
559          */
560         public Body(XMLBridge<?> bridge, ValueSetter setter) {
561             this.bridge = bridge;
562             this.setter = setter;
563         }
564 
565         @Override
566         public Object readResponse(Message msg, Object[] args) throws JAXBException {
567             return setter.put( msg.readPayloadAsJAXB(bridge), args );
568         }
569     }
570 
571     /**
572      * Treats a payload as multiple parts wrapped into one element,
573      * and processes all such wrapped parts.
574      */
575     public static final class DocLit extends ResponseBuilder {
576         /**
577          * {@link PartBuilder} keyed by the element name (inside the wrapper element.)
578          */
579         private final PartBuilder[] parts;
580 
581         private final XMLBridge wrapper;
582 
583         private boolean dynamicWrapper;
584 
585         public DocLit(WrapperParameter wp, ValueSetterFactory setterFactory) {
586             wrapperName = wp.getName();
587             wrapper = wp.getXMLBridge();
588             Class wrapperType = (Class) wrapper.getTypeInfo().type;
589             dynamicWrapper = WrapperComposite.class.equals(wrapperType);
590 
591             List<PartBuilder> tempParts = new ArrayList<PartBuilder>();
592 
593             List<ParameterImpl> children = wp.getWrapperChildren();
594             for (ParameterImpl p : children) {
595                 if(p.isIN())
596                     continue;
597                 QName name = p.getName();
598                 if (dynamicWrapper) {
599                     if (wrappedParts == null) wrappedParts = new HashMap<QName,WrappedPartBuilder>();
600                     XMLBridge xmlBridge = p.getInlinedRepeatedElementBridge();
601                     if (xmlBridge == null) xmlBridge = p.getXMLBridge();
602                     wrappedParts.put( p.getName(), new WrappedPartBuilder(xmlBridge, setterFactory.get(p)));
603                 } else {
604                     try {
605                         tempParts.add(new PartBuilder(
606                                     wp.getOwner().getBindingContext().getElementPropertyAccessor(
607                                         wrapperType,
608                                         name.getNamespaceURI(),
609                                         p.getName().getLocalPart()),
610                                     setterFactory.get(p)
611                                 ));
612                         // wrapper parameter itself always bind to body, and
613                         // so do all its children
614                         assert p.getBinding()== ParameterBinding.BODY;
615                     } catch (JAXBException e) {
616                         throw new WebServiceException(  // TODO: i18n
617                             wrapperType+" do not have a property of the name "+name,e);
618                     }
619                 }
620             }
621             this.parts = tempParts.toArray(new PartBuilder[tempParts.size()]);
622         }
623 
624         @Override
625         public Object readResponse(Message msg, Object[] args) throws JAXBException, XMLStreamException {
626             if (dynamicWrapper) return readWrappedResponse(msg, args);
627             Object retVal = null;
628 
629             if (parts.length>0) {
630                 if (!msg.hasPayload()) {
631                     throw new WebServiceException("No payload. Expecting payload with "+wrapperName+" element");
632                 }
633                 XMLStreamReader reader = msg.readPayload();
634                 XMLStreamReaderUtil.verifyTag(reader,wrapperName);
635                 Object wrapperBean = wrapper.unmarshal(reader, (msg.getAttachments() != null) ?
636                     new AttachmentUnmarshallerImpl(msg.getAttachments()): null);
637 
638                 try {
639                     for (PartBuilder part : parts) {
640                         Object o = part.readResponse(args,wrapperBean);
641                         // there's only at most one ResponseBuilder that returns a value.
642                         // TODO: reorder parts so that the return value comes at the end.
643                         if(o!=null) {
644                             assert retVal==null;
645                             retVal = o;
646                         }
647                     }
648                 } catch (DatabindingException e) {
649                     // this can happen when the set method throw a checked exception or something like that
650                     throw new WebServiceException(e);    // TODO:i18n
651                 }
652 
653                 // we are done with the body
654                 reader.close();
655                 XMLStreamReaderFactory.recycle(reader);
656             } else {
657                 msg.consume();
658             }
659 
660             return retVal;
661         }
662 
663         /**
664          * Unmarshals each wrapped part into a JAXB object and moves it
665          * to the expected place.
666          */
667         static final class PartBuilder {
668             private final PropertyAccessor accessor;
669             private final ValueSetter setter;
670 
671             /**
672              * @param accessor
673              *      specifies which portion of the wrapper bean to obtain the value from.
674              * @param setter
675              *      specifies how the obtained value is returned to the client.
676              */
677             public PartBuilder(PropertyAccessor accessor, ValueSetter setter) {
678                 this.accessor = accessor;
679                 this.setter = setter;
680                 assert accessor!=null && setter!=null;
681             }
682 
683             final Object readResponse( Object[] args, Object wrapperBean ) {
684                 Object obj = accessor.get(wrapperBean);
685                 return setter.put(obj,args);
686             }
687 
688 
689         }
690     }
691 
692     /**
693      * Treats a payload as multiple parts wrapped into one element,
694      * and processes all such wrapped parts.
695      */
696     public static final class RpcLit extends ResponseBuilder {
697         public RpcLit(WrapperParameter wp, ValueSetterFactory setterFactory) {
698             assert wp.getTypeInfo().type== WrapperComposite.class;
699             wrapperName = wp.getName();
700             wrappedParts = new HashMap<QName,WrappedPartBuilder>();
701             List<ParameterImpl> children = wp.getWrapperChildren();
702             for (ParameterImpl p : children) {
703                 wrappedParts.put( p.getName(), new WrappedPartBuilder(
704                     p.getXMLBridge(), setterFactory.get(p)
705                 ));
706                 // wrapper parameter itself always bind to body, and
707                 // so do all its children
708                 assert p.getBinding()== ParameterBinding.BODY;
709             }
710         }
711 
712         @Override
713         public Object readResponse(Message msg, Object[] args) throws JAXBException, XMLStreamException {
714             return readWrappedResponse(msg, args);
715         }
716     }
717 
718     private static boolean isXMLMimeType(String mimeType){
719         return mimeType.equals("text/xml") || mimeType.equals("application/xml");
720     }
721 }