|
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 } |