ohair@286: /* ohair@286: * Copyright (c) 1997, 2010, Oracle and/or its affiliates. All rights reserved. ohair@286: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ohair@286: * ohair@286: * This code is free software; you can redistribute it and/or modify it ohair@286: * under the terms of the GNU General Public License version 2 only, as ohair@286: * published by the Free Software Foundation. Oracle designates this ohair@286: * particular file as subject to the "Classpath" exception as provided ohair@286: * by Oracle in the LICENSE file that accompanied this code. ohair@286: * ohair@286: * This code is distributed in the hope that it will be useful, but WITHOUT ohair@286: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ohair@286: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ohair@286: * version 2 for more details (a copy is included in the LICENSE file that ohair@286: * accompanied this code). ohair@286: * ohair@286: * You should have received a copy of the GNU General Public License version ohair@286: * 2 along with this work; if not, write to the Free Software Foundation, ohair@286: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ohair@286: * ohair@286: * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ohair@286: * or visit www.oracle.com if you need additional information or have any ohair@286: * questions. ohair@286: */ ohair@286: ohair@286: package com.sun.xml.internal.ws.server.sei; ohair@286: ohair@286: import com.sun.xml.internal.ws.api.SOAPVersion; ohair@286: import com.sun.xml.internal.ws.api.message.Attachment; ohair@286: import com.sun.xml.internal.ws.api.message.AttachmentSet; ohair@286: import com.sun.xml.internal.ws.api.message.Message; ohair@286: import com.sun.xml.internal.ws.api.model.ParameterBinding; ohair@286: import com.sun.xml.internal.ws.api.streaming.XMLStreamReaderFactory; ohair@286: import com.sun.xml.internal.ws.message.AttachmentUnmarshallerImpl; ohair@286: import com.sun.xml.internal.ws.model.ParameterImpl; ohair@286: import com.sun.xml.internal.ws.model.WrapperParameter; ohair@286: import com.sun.xml.internal.ws.resources.ServerMessages; ohair@286: import com.sun.xml.internal.ws.spi.db.XMLBridge; ohair@286: import com.sun.xml.internal.ws.spi.db.DatabindingException; ohair@286: import com.sun.xml.internal.ws.spi.db.PropertyAccessor; ohair@286: import com.sun.xml.internal.ws.spi.db.WrapperComposite; ohair@286: import com.sun.xml.internal.ws.streaming.XMLStreamReaderUtil; ohair@286: import com.sun.xml.internal.ws.encoding.StringDataContentHandler; ohair@286: import com.sun.xml.internal.ws.encoding.DataHandlerDataSource; ohair@286: ohair@286: import javax.activation.DataHandler; ohair@286: import javax.imageio.ImageIO; ohair@286: import javax.jws.WebParam.Mode; ohair@286: import javax.xml.bind.JAXBException; ohair@286: import javax.xml.namespace.QName; ohair@286: import javax.xml.soap.SOAPException; ohair@286: import javax.xml.soap.SOAPFault; ohair@286: import javax.xml.stream.XMLStreamException; ohair@286: import javax.xml.stream.XMLStreamReader; ohair@286: import javax.xml.stream.XMLStreamConstants; ohair@286: import javax.xml.transform.Source; ohair@286: import javax.xml.ws.Holder; ohair@286: import javax.xml.ws.WebServiceException; ohair@286: import javax.xml.ws.soap.SOAPFaultException; ohair@286: import java.awt.Image; ohair@286: import java.io.IOException; ohair@286: import java.io.InputStream; ohair@286: import java.io.UnsupportedEncodingException; ohair@286: import java.lang.reflect.Type; ohair@286: import java.util.ArrayList; ohair@286: import java.util.Collection; ohair@286: import java.util.HashMap; ohair@286: import java.util.Iterator; ohair@286: import java.util.List; ohair@286: import java.util.Map; ohair@286: ohair@286: /** ohair@286: * Reads a request {@link Message}, disassembles it, and moves obtained Java values ohair@286: * to the expected places. ohair@286: * ohair@286: * @author Jitendra Kotamraju ohair@286: */ ohair@286: public abstract class EndpointArgumentsBuilder { ohair@286: /** ohair@286: * Reads a request {@link Message}, disassembles it, and moves obtained ohair@286: * Java values to the expected places. ohair@286: * ohair@286: * @param request ohair@286: * The request {@link Message} to be de-composed. ohair@286: * @param args ohair@286: * The Java arguments given to the SEI method invocation. ohair@286: * Some parts of the reply message may be set to {@link Holder}s in the arguments. ohair@286: * @throws JAXBException ohair@286: * if there's an error during unmarshalling the request message. ohair@286: * @throws XMLStreamException ohair@286: * if there's an error during unmarshalling the request message. ohair@286: */ ohair@286: public abstract void readRequest(Message request, Object[] args) ohair@286: throws JAXBException, XMLStreamException; ohair@286: ohair@286: static final class None extends EndpointArgumentsBuilder { ohair@286: private None(){ ohair@286: } ohair@286: public void readRequest(Message msg, Object[] args) { ohair@286: msg.consume(); ohair@286: } ohair@286: } ohair@286: ohair@286: /** ohair@286: * The singleton instance that produces null return value. ohair@286: * Used for operations that doesn't have any output. ohair@286: */ ohair@286: public static EndpointArgumentsBuilder NONE = new None(); ohair@286: ohair@286: /** ohair@286: * Returns the 'uninitialized' value for the given type. ohair@286: * ohair@286: *

ohair@286: * For primitive types, it's '0', and for reference types, it's null. ohair@286: */ ohair@286: public static Object getVMUninitializedValue(Type type) { ohair@286: // if this map returns null, that means the 'type' is a reference type, ohair@286: // in which case 'null' is the correct null value, so this code is correct. ohair@286: return primitiveUninitializedValues.get(type); ohair@286: } ohair@286: ohair@286: private static final Map primitiveUninitializedValues = new HashMap(); ohair@286: ohair@286: static { ohair@286: Map m = primitiveUninitializedValues; ohair@286: m.put(int.class,(int)0); ohair@286: m.put(char.class,(char)0); ohair@286: m.put(byte.class,(byte)0); ohair@286: m.put(short.class,(short)0); ohair@286: m.put(long.class,(long)0); ohair@286: m.put(float.class,(float)0); ohair@286: m.put(double.class,(double)0); ohair@286: } ohair@286: ohair@286: /** ohair@286: * {@link EndpointArgumentsBuilder} that sets the VM uninitialized value to the type. ohair@286: */ ohair@286: public static final class NullSetter extends EndpointArgumentsBuilder { ohair@286: private final EndpointValueSetter setter; ohair@286: private final Object nullValue; ohair@286: ohair@286: public NullSetter(EndpointValueSetter setter, Object nullValue){ ohair@286: assert setter!=null; ohair@286: this.nullValue = nullValue; ohair@286: this.setter = setter; ohair@286: } ohair@286: public void readRequest(Message msg, Object[] args) { ohair@286: setter.put(nullValue, args); ohair@286: } ohair@286: } ohair@286: ohair@286: /** ohair@286: * {@link EndpointArgumentsBuilder} that is a composition of multiple ohair@286: * {@link EndpointArgumentsBuilder}s. ohair@286: * ohair@286: *

ohair@286: * Sometimes we need to look at multiple parts of the reply message ohair@286: * (say, two header params, one body param, and three attachments, etc.) ohair@286: * and that's when this object is used to combine multiple {@link EndpointArgumentsBuilder}s ohair@286: * (that each responsible for handling one part). ohair@286: * ohair@286: *

ohair@286: * The model guarantees that only at most one {@link EndpointArgumentsBuilder} will ohair@286: * return a value as a return value (and everything else has to go to ohair@286: * {@link Holder}s.) ohair@286: */ ohair@286: public static final class Composite extends EndpointArgumentsBuilder { ohair@286: private final EndpointArgumentsBuilder[] builders; ohair@286: ohair@286: public Composite(EndpointArgumentsBuilder... builders) { ohair@286: this.builders = builders; ohair@286: } ohair@286: ohair@286: public Composite(Collection builders) { ohair@286: this(builders.toArray(new EndpointArgumentsBuilder[builders.size()])); ohair@286: } ohair@286: ohair@286: public void readRequest(Message msg, Object[] args) throws JAXBException, XMLStreamException { ohair@286: for (EndpointArgumentsBuilder builder : builders) { ohair@286: builder.readRequest(msg,args); ohair@286: } ohair@286: } ohair@286: } ohair@286: ohair@286: ohair@286: /** ohair@286: * Reads an Attachment into a Java parameter. ohair@286: */ ohair@286: public static abstract class AttachmentBuilder extends EndpointArgumentsBuilder { ohair@286: protected final EndpointValueSetter setter; ohair@286: protected final ParameterImpl param; ohair@286: protected final String pname; ohair@286: protected final String pname1; ohair@286: ohair@286: AttachmentBuilder(ParameterImpl param, EndpointValueSetter setter) { ohair@286: this.setter = setter; ohair@286: this.param = param; ohair@286: this.pname = param.getPartName(); ohair@286: this.pname1 = "<"+pname; ohair@286: } ohair@286: ohair@286: /** ohair@286: * Creates an AttachmentBuilder based on the parameter type ohair@286: * ohair@286: * @param param ohair@286: * runtime Parameter that abstracts the annotated java parameter ohair@286: * @param setter ohair@286: * specifies how the obtained value is set into the argument. Takes ohair@286: * care of Holder arguments. ohair@286: */ ohair@286: public static EndpointArgumentsBuilder createAttachmentBuilder(ParameterImpl param, EndpointValueSetter setter) { ohair@286: Class type = (Class)param.getTypeInfo().type; ohair@286: if (DataHandler.class.isAssignableFrom(type)) { ohair@286: return new DataHandlerBuilder(param, setter); ohair@286: } else if (byte[].class==type) { ohair@286: return new ByteArrayBuilder(param, setter); ohair@286: } else if(Source.class.isAssignableFrom(type)) { ohair@286: return new SourceBuilder(param, setter); ohair@286: } else if(Image.class.isAssignableFrom(type)) { ohair@286: return new ImageBuilder(param, setter); ohair@286: } else if(InputStream.class==type) { ohair@286: return new InputStreamBuilder(param, setter); ohair@286: } else if(isXMLMimeType(param.getBinding().getMimeType())) { ohair@286: return new JAXBBuilder(param, setter); ohair@286: } else if(String.class.isAssignableFrom(type)) { ohair@286: return new StringBuilder(param, setter); ohair@286: } else { ohair@286: throw new UnsupportedOperationException("Unknown Type="+type+" Attachment is not mapped."); ohair@286: } ohair@286: } ohair@286: ohair@286: public void readRequest(Message msg, Object[] args) throws JAXBException, XMLStreamException { ohair@286: boolean foundAttachment = false; ohair@286: // TODO not to loop ohair@286: for (Attachment att : msg.getAttachments()) { ohair@286: String part = getWSDLPartName(att); ohair@286: if (part == null) { ohair@286: continue; ohair@286: } ohair@286: if(part.equals(pname) || part.equals(pname1)){ ohair@286: foundAttachment = true; ohair@286: mapAttachment(att, args); ohair@286: break; ohair@286: } ohair@286: } ohair@286: if (!foundAttachment) { ohair@286: throw new WebServiceException("Missing Attachment for "+pname); ohair@286: } ohair@286: } ohair@286: ohair@286: abstract void mapAttachment(Attachment att, Object[] args) throws JAXBException; ohair@286: } ohair@286: ohair@286: private static final class DataHandlerBuilder extends AttachmentBuilder { ohair@286: DataHandlerBuilder(ParameterImpl param, EndpointValueSetter setter) { ohair@286: super(param, setter); ohair@286: } ohair@286: ohair@286: void mapAttachment(Attachment att, Object[] args) { ohair@286: setter.put(att.asDataHandler(), args); ohair@286: } ohair@286: } ohair@286: ohair@286: private static final class ByteArrayBuilder extends AttachmentBuilder { ohair@286: ByteArrayBuilder(ParameterImpl param, EndpointValueSetter setter) { ohair@286: super(param, setter); ohair@286: } ohair@286: ohair@286: void mapAttachment(Attachment att, Object[] args) { ohair@286: setter.put(att.asByteArray(), args); ohair@286: } ohair@286: } ohair@286: ohair@286: private static final class SourceBuilder extends AttachmentBuilder { ohair@286: SourceBuilder(ParameterImpl param, EndpointValueSetter setter) { ohair@286: super(param, setter); ohair@286: } ohair@286: ohair@286: void mapAttachment(Attachment att, Object[] args) { ohair@286: setter.put(att.asSource(), args); ohair@286: } ohair@286: } ohair@286: ohair@286: private static final class ImageBuilder extends AttachmentBuilder { ohair@286: ImageBuilder(ParameterImpl param, EndpointValueSetter setter) { ohair@286: super(param, setter); ohair@286: } ohair@286: ohair@286: void mapAttachment(Attachment att, Object[] args) { ohair@286: Image image; ohair@286: InputStream is = null; ohair@286: try { ohair@286: is = att.asInputStream(); ohair@286: image = ImageIO.read(is); ohair@286: } catch(IOException ioe) { ohair@286: throw new WebServiceException(ioe); ohair@286: } finally { ohair@286: if (is != null) { ohair@286: try { ohair@286: is.close(); ohair@286: } catch(IOException ioe) { ohair@286: throw new WebServiceException(ioe); ohair@286: } ohair@286: } ohair@286: } ohair@286: setter.put(image, args); ohair@286: } ohair@286: } ohair@286: ohair@286: private static final class InputStreamBuilder extends AttachmentBuilder { ohair@286: InputStreamBuilder(ParameterImpl param, EndpointValueSetter setter) { ohair@286: super(param, setter); ohair@286: } ohair@286: ohair@286: void mapAttachment(Attachment att, Object[] args) { ohair@286: setter.put(att.asInputStream(), args); ohair@286: } ohair@286: } ohair@286: ohair@286: private static final class JAXBBuilder extends AttachmentBuilder { ohair@286: JAXBBuilder(ParameterImpl param, EndpointValueSetter setter) { ohair@286: super(param, setter); ohair@286: } ohair@286: ohair@286: void mapAttachment(Attachment att, Object[] args) throws JAXBException { ohair@286: Object obj = param.getXMLBridge().unmarshal(att.asInputStream()); ohair@286: setter.put(obj, args); ohair@286: } ohair@286: } ohair@286: ohair@286: private static final class StringBuilder extends AttachmentBuilder { ohair@286: StringBuilder(ParameterImpl param, EndpointValueSetter setter) { ohair@286: super(param, setter); ohair@286: } ohair@286: ohair@286: void mapAttachment(Attachment att, Object[] args) { ohair@286: att.getContentType(); ohair@286: StringDataContentHandler sdh = new StringDataContentHandler(); ohair@286: try { ohair@286: String str = (String)sdh.getContent(new DataHandlerDataSource(att.asDataHandler())); ohair@286: setter.put(str, args); ohair@286: } catch(Exception e) { ohair@286: throw new WebServiceException(e); ohair@286: } ohair@286: } ohair@286: } ohair@286: ohair@286: /** ohair@286: * Gets the WSDL part name of this attachment. ohair@286: * ohair@286: *

ohair@286: * According to WSI AP 1.0 ohair@286: *

ohair@286:      * 3.8 Value-space of Content-Id Header
ohair@286:      *   Definition: content-id part encoding
ohair@286:      *   The "content-id part encoding" consists of the concatenation of:
ohair@286:      * 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:
ohair@286:      *     o Each disallowed character is converted to UTF-8 as one or more bytes.
ohair@286:      *     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).
ohair@286:      *     o The original character is replaced by the resulting character sequence.
ohair@286:      * The character '=' (0x3D).
ohair@286:      * A globally unique value such as a UUID.
ohair@286:      * The character '@' (0x40).
ohair@286:      * A valid domain name under the authority of the entity constructing the message.
ohair@286:      * 
ohair@286: * ohair@286: * So a wsdl:part fooPart will be encoded as: ohair@286: * ohair@286: * ohair@286: * @return null ohair@286: * if the parsing fails. ohair@286: */ ohair@286: public static final String getWSDLPartName(com.sun.xml.internal.ws.api.message.Attachment att){ ohair@286: String cId = att.getContentId(); ohair@286: ohair@286: int index = cId.lastIndexOf('@', cId.length()); ohair@286: if(index == -1){ ohair@286: return null; ohair@286: } ohair@286: String localPart = cId.substring(0, index); ohair@286: index = localPart.lastIndexOf('=', localPart.length()); ohair@286: if(index == -1){ ohair@286: return null; ohair@286: } ohair@286: try { ohair@286: return java.net.URLDecoder.decode(localPart.substring(0, index), "UTF-8"); ohair@286: } catch (UnsupportedEncodingException e) { ohair@286: throw new WebServiceException(e); ohair@286: } ohair@286: } ohair@286: ohair@286: ohair@286: ohair@286: ohair@286: /** ohair@286: * Reads a header into a JAXB object. ohair@286: */ ohair@286: public static final class Header extends EndpointArgumentsBuilder { ohair@286: private final XMLBridge bridge; ohair@286: private final EndpointValueSetter setter; ohair@286: private final QName headerName; ohair@286: private final SOAPVersion soapVersion; ohair@286: ohair@286: /** ohair@286: * @param name ohair@286: * The name of the header element. ohair@286: * @param bridge ohair@286: * specifies how to unmarshal a header into a JAXB object. ohair@286: * @param setter ohair@286: * specifies how the obtained value is returned to the client. ohair@286: */ ohair@286: public Header(SOAPVersion soapVersion, QName name, XMLBridge bridge, EndpointValueSetter setter) { ohair@286: this.soapVersion = soapVersion; ohair@286: this.headerName = name; ohair@286: this.bridge = bridge; ohair@286: this.setter = setter; ohair@286: } ohair@286: ohair@286: public Header(SOAPVersion soapVersion, ParameterImpl param, EndpointValueSetter setter) { ohair@286: this( ohair@286: soapVersion, ohair@286: param.getTypeInfo().tagName, ohair@286: param.getXMLBridge(), ohair@286: setter); ohair@286: assert param.getOutBinding()== ParameterBinding.HEADER; ohair@286: } ohair@286: ohair@286: private SOAPFaultException createDuplicateHeaderException() { ohair@286: try { ohair@286: SOAPFault fault = soapVersion.getSOAPFactory().createFault(); ohair@286: fault.setFaultCode(soapVersion.faultCodeClient); ohair@286: fault.setFaultString(ServerMessages.DUPLICATE_PORT_KNOWN_HEADER(headerName)); ohair@286: return new SOAPFaultException(fault); ohair@286: } catch(SOAPException e) { ohair@286: throw new WebServiceException(e); ohair@286: } ohair@286: } ohair@286: ohair@286: public void readRequest(Message msg, Object[] args) throws JAXBException { ohair@286: com.sun.xml.internal.ws.api.message.Header header = null; ohair@286: Iterator it = ohair@286: msg.getHeaders().getHeaders(headerName,true); ohair@286: if (it.hasNext()) { ohair@286: header = it.next(); ohair@286: if (it.hasNext()) { ohair@286: throw createDuplicateHeaderException(); ohair@286: } ohair@286: } ohair@286: ohair@286: if(header!=null) { ohair@286: setter.put( header.readAsJAXB(bridge), args ); ohair@286: } else { ohair@286: // header not found. ohair@286: } ohair@286: } ohair@286: } ohair@286: ohair@286: /** ohair@286: * Reads the whole payload into a single JAXB bean. ohair@286: */ ohair@286: public static final class Body extends EndpointArgumentsBuilder { ohair@286: private final XMLBridge bridge; ohair@286: private final EndpointValueSetter setter; ohair@286: ohair@286: /** ohair@286: * @param bridge ohair@286: * specifies how to unmarshal the payload into a JAXB object. ohair@286: * @param setter ohair@286: * specifies how the obtained value is returned to the client. ohair@286: */ ohair@286: public Body(XMLBridge bridge, EndpointValueSetter setter) { ohair@286: this.bridge = bridge; ohair@286: this.setter = setter; ohair@286: } ohair@286: ohair@286: public void readRequest(Message msg, Object[] args) throws JAXBException { ohair@286: setter.put( msg.readPayloadAsJAXB(bridge), args ); ohair@286: } ohair@286: } ohair@286: ohair@286: /** ohair@286: * Treats a payload as multiple parts wrapped into one element, ohair@286: * and processes all such wrapped parts. ohair@286: */ ohair@286: public static final class DocLit extends EndpointArgumentsBuilder { ohair@286: /** ohair@286: * {@link PartBuilder} keyed by the element name (inside the wrapper element.) ohair@286: */ ohair@286: private final PartBuilder[] parts; ohair@286: ohair@286: private final XMLBridge wrapper; ohair@286: private final QName wrapperName; ohair@286: ohair@286: public DocLit(WrapperParameter wp, Mode skipMode) { ohair@286: wrapperName = wp.getName(); ohair@286: wrapper = wp.getXMLBridge(); ohair@286: Class wrapperType = (Class) wrapper.getTypeInfo().type; ohair@286: ohair@286: List parts = new ArrayList(); ohair@286: ohair@286: List children = wp.getWrapperChildren(); ohair@286: for (ParameterImpl p : children) { ohair@286: if (p.getMode() == skipMode) { ohair@286: continue; ohair@286: } ohair@286: /* ohair@286: if(p.isIN()) ohair@286: continue; ohair@286: */ ohair@286: QName name = p.getName(); ohair@286: try { ohair@286: parts.add( new PartBuilder( ohair@286: wp.getOwner().getBindingContext().getElementPropertyAccessor( ohair@286: wrapperType, ohair@286: name.getNamespaceURI(), ohair@286: p.getName().getLocalPart()), ohair@286: EndpointValueSetter.get(p) ohair@286: )); ohair@286: // wrapper parameter itself always bind to body, and ohair@286: // so do all its children ohair@286: assert p.getBinding()== ParameterBinding.BODY; ohair@286: } catch (JAXBException e) { ohair@286: throw new WebServiceException( // TODO: i18n ohair@286: wrapperType+" do not have a property of the name "+name,e); ohair@286: } ohair@286: } ohair@286: ohair@286: this.parts = parts.toArray(new PartBuilder[parts.size()]); ohair@286: } ohair@286: ohair@286: public void readRequest(Message msg, Object[] args) throws JAXBException, XMLStreamException { ohair@286: ohair@286: if (parts.length>0) { ohair@286: if (!msg.hasPayload()) { ohair@286: throw new WebServiceException("No payload. Expecting payload with "+wrapperName+" element"); ohair@286: } ohair@286: XMLStreamReader reader = msg.readPayload(); ohair@286: XMLStreamReaderUtil.verifyTag(reader, wrapperName); ohair@286: Object wrapperBean = wrapper.unmarshal(reader, (msg.getAttachments() != null) ? ohair@286: new AttachmentUnmarshallerImpl(msg.getAttachments()): null); ohair@286: ohair@286: try { ohair@286: for (PartBuilder part : parts) { ohair@286: part.readRequest(args,wrapperBean); ohair@286: } ohair@286: } catch (DatabindingException e) { ohair@286: // this can happen when the set method throw a checked exception or something like that ohair@286: throw new WebServiceException(e); // TODO:i18n ohair@286: } ohair@286: ohair@286: // we are done with the body ohair@286: reader.close(); ohair@286: XMLStreamReaderFactory.recycle(reader); ohair@286: } else { ohair@286: msg.consume(); ohair@286: } ohair@286: } ohair@286: ohair@286: /** ohair@286: * Unmarshals each wrapped part into a JAXB object and moves it ohair@286: * to the expected place. ohair@286: */ ohair@286: static final class PartBuilder { ohair@286: private final PropertyAccessor accessor; ohair@286: private final EndpointValueSetter setter; ohair@286: ohair@286: /** ohair@286: * @param accessor ohair@286: * specifies which portion of the wrapper bean to obtain the value from. ohair@286: * @param setter ohair@286: * specifies how the obtained value is returned to the client. ohair@286: */ ohair@286: public PartBuilder(PropertyAccessor accessor, EndpointValueSetter setter) { ohair@286: this.accessor = accessor; ohair@286: this.setter = setter; ohair@286: assert accessor!=null && setter!=null; ohair@286: } ohair@286: ohair@286: final void readRequest( Object[] args, Object wrapperBean ) { ohair@286: Object obj = accessor.get(wrapperBean); ohair@286: setter.put(obj,args); ohair@286: } ohair@286: ohair@286: ohair@286: } ohair@286: } ohair@286: ohair@286: /** ohair@286: * Treats a payload as multiple parts wrapped into one element, ohair@286: * and processes all such wrapped parts. ohair@286: */ ohair@286: public static final class RpcLit extends EndpointArgumentsBuilder { ohair@286: /** ohair@286: * {@link PartBuilder} keyed by the element name (inside the wrapper element.) ohair@286: */ ohair@286: private final Map parts = new HashMap(); ohair@286: ohair@286: private QName wrapperName; ohair@286: ohair@286: public RpcLit(WrapperParameter wp) { ohair@286: assert wp.getTypeInfo().type== WrapperComposite.class; ohair@286: ohair@286: wrapperName = wp.getName(); ohair@286: List children = wp.getWrapperChildren(); ohair@286: for (ParameterImpl p : children) { ohair@286: parts.put( p.getName(), new PartBuilder( ohair@286: p.getXMLBridge(), EndpointValueSetter.get(p) ohair@286: )); ohair@286: // wrapper parameter itself always bind to body, and ohair@286: // so do all its children ohair@286: assert p.getBinding()== ParameterBinding.BODY; ohair@286: } ohair@286: } ohair@286: ohair@286: public void readRequest(Message msg, Object[] args) throws JAXBException, XMLStreamException { ohair@286: if (!msg.hasPayload()) { ohair@286: throw new WebServiceException("No payload. Expecting payload with "+wrapperName+" element"); ohair@286: } ohair@286: XMLStreamReader reader = msg.readPayload(); ohair@286: XMLStreamReaderUtil.verifyTag(reader,wrapperName); ohair@286: reader.nextTag(); ohair@286: ohair@286: while(reader.getEventType()==XMLStreamReader.START_ELEMENT) { ohair@286: // TODO: QName has a performance issue ohair@286: QName name = reader.getName(); ohair@286: PartBuilder part = parts.get(name); ohair@286: if(part==null) { ohair@286: // no corresponding part found. ignore ohair@286: XMLStreamReaderUtil.skipElement(reader); ohair@286: reader.nextTag(); ohair@286: } else { ohair@286: part.readRequest(args,reader, msg.getAttachments()); ohair@286: } ohair@286: // skip any whitespace ohair@286: if (reader.getEventType() != XMLStreamConstants.START_ELEMENT && ohair@286: reader.getEventType() != XMLStreamConstants.END_ELEMENT) { ohair@286: XMLStreamReaderUtil.nextElementContent(reader); ohair@286: } ohair@286: if(reader.getEventType() == XMLStreamConstants.END_ELEMENT && name.equals(reader.getName())) { ohair@286: XMLStreamReaderUtil.nextElementContent(reader); ohair@286: } ohair@286: } ohair@286: ohair@286: // we are done with the body ohair@286: reader.close(); ohair@286: XMLStreamReaderFactory.recycle(reader); ohair@286: } ohair@286: ohair@286: /** ohair@286: * Unmarshals each wrapped part into a JAXB object and moves it ohair@286: * to the expected place. ohair@286: */ ohair@286: static final class PartBuilder { ohair@286: private final XMLBridge bridge; ohair@286: private final EndpointValueSetter setter; ohair@286: ohair@286: /** ohair@286: * @param bridge ohair@286: * specifies how the part is unmarshalled. ohair@286: * @param setter ohair@286: * specifies how the obtained value is returned to the endpoint. ohair@286: */ ohair@286: public PartBuilder(XMLBridge bridge, EndpointValueSetter setter) { ohair@286: this.bridge = bridge; ohair@286: this.setter = setter; ohair@286: } ohair@286: ohair@286: final void readRequest( Object[] args, XMLStreamReader r, AttachmentSet att) throws JAXBException { ohair@286: Object obj = bridge.unmarshal(r, (att != null)?new AttachmentUnmarshallerImpl(att):null); ohair@286: setter.put(obj,args); ohair@286: } ohair@286: } ohair@286: } ohair@286: ohair@286: private static boolean isXMLMimeType(String mimeType){ ohair@286: return mimeType.equals("text/xml") || mimeType.equals("application/xml"); ohair@286: } ohair@286: }