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
26package com.sun.xml.internal.ws.client.sei;
27
28import com.sun.xml.internal.ws.api.SOAPVersion;
29import com.sun.xml.internal.ws.api.message.Attachment;
30import com.sun.xml.internal.ws.api.message.AttachmentSet;
31import com.sun.xml.internal.ws.api.message.Message;
32import com.sun.xml.internal.ws.api.model.ParameterBinding;
33import com.sun.xml.internal.ws.api.streaming.XMLStreamReaderFactory;
34import com.sun.xml.internal.ws.message.AttachmentUnmarshallerImpl;
35import com.sun.xml.internal.ws.model.ParameterImpl;
36import com.sun.xml.internal.ws.model.WrapperParameter;
37import com.sun.xml.internal.ws.resources.ServerMessages;
38import com.sun.xml.internal.ws.spi.db.RepeatedElementBridge;
39import com.sun.xml.internal.ws.spi.db.XMLBridge;
40import com.sun.xml.internal.ws.spi.db.DatabindingException;
41import com.sun.xml.internal.ws.spi.db.PropertyAccessor;
42import com.sun.xml.internal.ws.spi.db.WrapperComposite;
43import com.sun.xml.internal.ws.streaming.XMLStreamReaderUtil;
44import com.sun.xml.internal.ws.encoding.StringDataContentHandler;
45import com.sun.xml.internal.ws.encoding.DataHandlerDataSource;
46
47import javax.activation.DataHandler;
48import javax.imageio.ImageIO;
49import javax.xml.bind.JAXBException;
50import javax.xml.namespace.QName;
51import javax.xml.soap.SOAPException;
52import javax.xml.soap.SOAPFault;
53import javax.xml.stream.XMLStreamException;
54import javax.xml.stream.XMLStreamReader;
55import javax.xml.stream.XMLStreamConstants;
56import javax.xml.transform.Source;
57import javax.xml.ws.Holder;
58import javax.xml.ws.WebServiceException;
59import javax.xml.ws.soap.SOAPFaultException;
60import java.awt.Image;
61import java.io.IOException;
62import java.io.InputStream;
63import java.io.UnsupportedEncodingException;
64import java.lang.reflect.Type;
65import java.util.ArrayList;
66import java.util.Collection;
67import java.util.HashMap;
68import java.util.Iterator;
69import java.util.List;
70import 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 */
79public 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}
722