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