src/share/jaxws_classes/com/sun/xml/internal/ws/server/sei/EndpointArgumentsBuilder.java

changeset 286
f50545b5e2f1
child 368
0989ad8c0860
equal deleted inserted replaced
284:88b85470e72c 286:f50545b5e2f1
1 /*
2 * Copyright (c) 1997, 2010, 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.server.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.XMLBridge;
39 import com.sun.xml.internal.ws.spi.db.DatabindingException;
40 import com.sun.xml.internal.ws.spi.db.PropertyAccessor;
41 import com.sun.xml.internal.ws.spi.db.WrapperComposite;
42 import com.sun.xml.internal.ws.streaming.XMLStreamReaderUtil;
43 import com.sun.xml.internal.ws.encoding.StringDataContentHandler;
44 import com.sun.xml.internal.ws.encoding.DataHandlerDataSource;
45
46 import javax.activation.DataHandler;
47 import javax.imageio.ImageIO;
48 import javax.jws.WebParam.Mode;
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 request {@link Message}, disassembles it, and moves obtained Java values
74 * to the expected places.
75 *
76 * @author Jitendra Kotamraju
77 */
78 public abstract class EndpointArgumentsBuilder {
79 /**
80 * Reads a request {@link Message}, disassembles it, and moves obtained
81 * Java values to the expected places.
82 *
83 * @param request
84 * The request {@link Message} to be de-composed.
85 * @param args
86 * The Java arguments given to the SEI method invocation.
87 * Some parts of the reply message may be set to {@link Holder}s in the arguments.
88 * @throws JAXBException
89 * if there's an error during unmarshalling the request message.
90 * @throws XMLStreamException
91 * if there's an error during unmarshalling the request message.
92 */
93 public abstract void readRequest(Message request, Object[] args)
94 throws JAXBException, XMLStreamException;
95
96 static final class None extends EndpointArgumentsBuilder {
97 private None(){
98 }
99 public void readRequest(Message msg, Object[] args) {
100 msg.consume();
101 }
102 }
103
104 /**
105 * The singleton instance that produces null return value.
106 * Used for operations that doesn't have any output.
107 */
108 public static EndpointArgumentsBuilder NONE = new None();
109
110 /**
111 * Returns the 'uninitialized' value for the given type.
112 *
113 * <p>
114 * For primitive types, it's '0', and for reference types, it's null.
115 */
116 public static Object getVMUninitializedValue(Type type) {
117 // if this map returns null, that means the 'type' is a reference type,
118 // in which case 'null' is the correct null value, so this code is correct.
119 return primitiveUninitializedValues.get(type);
120 }
121
122 private static final Map<Class,Object> primitiveUninitializedValues = new HashMap<Class, Object>();
123
124 static {
125 Map<Class, Object> m = primitiveUninitializedValues;
126 m.put(int.class,(int)0);
127 m.put(char.class,(char)0);
128 m.put(byte.class,(byte)0);
129 m.put(short.class,(short)0);
130 m.put(long.class,(long)0);
131 m.put(float.class,(float)0);
132 m.put(double.class,(double)0);
133 }
134
135 /**
136 * {@link EndpointArgumentsBuilder} that sets the VM uninitialized value to the type.
137 */
138 public static final class NullSetter extends EndpointArgumentsBuilder {
139 private final EndpointValueSetter setter;
140 private final Object nullValue;
141
142 public NullSetter(EndpointValueSetter setter, Object nullValue){
143 assert setter!=null;
144 this.nullValue = nullValue;
145 this.setter = setter;
146 }
147 public void readRequest(Message msg, Object[] args) {
148 setter.put(nullValue, args);
149 }
150 }
151
152 /**
153 * {@link EndpointArgumentsBuilder} that is a composition of multiple
154 * {@link EndpointArgumentsBuilder}s.
155 *
156 * <p>
157 * Sometimes we need to look at multiple parts of the reply message
158 * (say, two header params, one body param, and three attachments, etc.)
159 * and that's when this object is used to combine multiple {@link EndpointArgumentsBuilder}s
160 * (that each responsible for handling one part).
161 *
162 * <p>
163 * The model guarantees that only at most one {@link EndpointArgumentsBuilder} will
164 * return a value as a return value (and everything else has to go to
165 * {@link Holder}s.)
166 */
167 public static final class Composite extends EndpointArgumentsBuilder {
168 private final EndpointArgumentsBuilder[] builders;
169
170 public Composite(EndpointArgumentsBuilder... builders) {
171 this.builders = builders;
172 }
173
174 public Composite(Collection<? extends EndpointArgumentsBuilder> builders) {
175 this(builders.toArray(new EndpointArgumentsBuilder[builders.size()]));
176 }
177
178 public void readRequest(Message msg, Object[] args) throws JAXBException, XMLStreamException {
179 for (EndpointArgumentsBuilder builder : builders) {
180 builder.readRequest(msg,args);
181 }
182 }
183 }
184
185
186 /**
187 * Reads an Attachment into a Java parameter.
188 */
189 public static abstract class AttachmentBuilder extends EndpointArgumentsBuilder {
190 protected final EndpointValueSetter setter;
191 protected final ParameterImpl param;
192 protected final String pname;
193 protected final String pname1;
194
195 AttachmentBuilder(ParameterImpl param, EndpointValueSetter setter) {
196 this.setter = setter;
197 this.param = param;
198 this.pname = param.getPartName();
199 this.pname1 = "<"+pname;
200 }
201
202 /**
203 * Creates an AttachmentBuilder based on the parameter type
204 *
205 * @param param
206 * runtime Parameter that abstracts the annotated java parameter
207 * @param setter
208 * specifies how the obtained value is set into the argument. Takes
209 * care of Holder arguments.
210 */
211 public static EndpointArgumentsBuilder createAttachmentBuilder(ParameterImpl param, EndpointValueSetter setter) {
212 Class type = (Class)param.getTypeInfo().type;
213 if (DataHandler.class.isAssignableFrom(type)) {
214 return new DataHandlerBuilder(param, setter);
215 } else if (byte[].class==type) {
216 return new ByteArrayBuilder(param, setter);
217 } else if(Source.class.isAssignableFrom(type)) {
218 return new SourceBuilder(param, setter);
219 } else if(Image.class.isAssignableFrom(type)) {
220 return new ImageBuilder(param, setter);
221 } else if(InputStream.class==type) {
222 return new InputStreamBuilder(param, setter);
223 } else if(isXMLMimeType(param.getBinding().getMimeType())) {
224 return new JAXBBuilder(param, setter);
225 } else if(String.class.isAssignableFrom(type)) {
226 return new StringBuilder(param, setter);
227 } else {
228 throw new UnsupportedOperationException("Unknown Type="+type+" Attachment is not mapped.");
229 }
230 }
231
232 public void readRequest(Message msg, Object[] args) throws JAXBException, XMLStreamException {
233 boolean foundAttachment = false;
234 // TODO not to loop
235 for (Attachment att : msg.getAttachments()) {
236 String part = getWSDLPartName(att);
237 if (part == null) {
238 continue;
239 }
240 if(part.equals(pname) || part.equals(pname1)){
241 foundAttachment = true;
242 mapAttachment(att, args);
243 break;
244 }
245 }
246 if (!foundAttachment) {
247 throw new WebServiceException("Missing Attachment for "+pname);
248 }
249 }
250
251 abstract void mapAttachment(Attachment att, Object[] args) throws JAXBException;
252 }
253
254 private static final class DataHandlerBuilder extends AttachmentBuilder {
255 DataHandlerBuilder(ParameterImpl param, EndpointValueSetter setter) {
256 super(param, setter);
257 }
258
259 void mapAttachment(Attachment att, Object[] args) {
260 setter.put(att.asDataHandler(), args);
261 }
262 }
263
264 private static final class ByteArrayBuilder extends AttachmentBuilder {
265 ByteArrayBuilder(ParameterImpl param, EndpointValueSetter setter) {
266 super(param, setter);
267 }
268
269 void mapAttachment(Attachment att, Object[] args) {
270 setter.put(att.asByteArray(), args);
271 }
272 }
273
274 private static final class SourceBuilder extends AttachmentBuilder {
275 SourceBuilder(ParameterImpl param, EndpointValueSetter setter) {
276 super(param, setter);
277 }
278
279 void mapAttachment(Attachment att, Object[] args) {
280 setter.put(att.asSource(), args);
281 }
282 }
283
284 private static final class ImageBuilder extends AttachmentBuilder {
285 ImageBuilder(ParameterImpl param, EndpointValueSetter setter) {
286 super(param, setter);
287 }
288
289 void mapAttachment(Attachment att, Object[] args) {
290 Image image;
291 InputStream is = null;
292 try {
293 is = att.asInputStream();
294 image = ImageIO.read(is);
295 } catch(IOException ioe) {
296 throw new WebServiceException(ioe);
297 } finally {
298 if (is != null) {
299 try {
300 is.close();
301 } catch(IOException ioe) {
302 throw new WebServiceException(ioe);
303 }
304 }
305 }
306 setter.put(image, args);
307 }
308 }
309
310 private static final class InputStreamBuilder extends AttachmentBuilder {
311 InputStreamBuilder(ParameterImpl param, EndpointValueSetter setter) {
312 super(param, setter);
313 }
314
315 void mapAttachment(Attachment att, Object[] args) {
316 setter.put(att.asInputStream(), args);
317 }
318 }
319
320 private static final class JAXBBuilder extends AttachmentBuilder {
321 JAXBBuilder(ParameterImpl param, EndpointValueSetter setter) {
322 super(param, setter);
323 }
324
325 void mapAttachment(Attachment att, Object[] args) throws JAXBException {
326 Object obj = param.getXMLBridge().unmarshal(att.asInputStream());
327 setter.put(obj, args);
328 }
329 }
330
331 private static final class StringBuilder extends AttachmentBuilder {
332 StringBuilder(ParameterImpl param, EndpointValueSetter setter) {
333 super(param, setter);
334 }
335
336 void mapAttachment(Attachment att, Object[] args) {
337 att.getContentType();
338 StringDataContentHandler sdh = new StringDataContentHandler();
339 try {
340 String str = (String)sdh.getContent(new DataHandlerDataSource(att.asDataHandler()));
341 setter.put(str, args);
342 } catch(Exception e) {
343 throw new WebServiceException(e);
344 }
345 }
346 }
347
348 /**
349 * Gets the WSDL part name of this attachment.
350 *
351 * <p>
352 * According to WSI AP 1.0
353 * <PRE>
354 * 3.8 Value-space of Content-Id Header
355 * Definition: content-id part encoding
356 * The "content-id part encoding" consists of the concatenation of:
357 * 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:
358 * o Each disallowed character is converted to UTF-8 as one or more bytes.
359 * 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).
360 * o The original character is replaced by the resulting character sequence.
361 * The character '=' (0x3D).
362 * A globally unique value such as a UUID.
363 * The character '@' (0x40).
364 * A valid domain name under the authority of the entity constructing the message.
365 * </PRE>
366 *
367 * So a wsdl:part fooPart will be encoded as:
368 * <fooPart=somereallybignumberlikeauuid@example.com>
369 *
370 * @return null
371 * if the parsing fails.
372 */
373 public static final String getWSDLPartName(com.sun.xml.internal.ws.api.message.Attachment att){
374 String cId = att.getContentId();
375
376 int index = cId.lastIndexOf('@', cId.length());
377 if(index == -1){
378 return null;
379 }
380 String localPart = cId.substring(0, index);
381 index = localPart.lastIndexOf('=', localPart.length());
382 if(index == -1){
383 return null;
384 }
385 try {
386 return java.net.URLDecoder.decode(localPart.substring(0, index), "UTF-8");
387 } catch (UnsupportedEncodingException e) {
388 throw new WebServiceException(e);
389 }
390 }
391
392
393
394
395 /**
396 * Reads a header into a JAXB object.
397 */
398 public static final class Header extends EndpointArgumentsBuilder {
399 private final XMLBridge<?> bridge;
400 private final EndpointValueSetter setter;
401 private final QName headerName;
402 private final SOAPVersion soapVersion;
403
404 /**
405 * @param name
406 * The name of the header element.
407 * @param bridge
408 * specifies how to unmarshal a header into a JAXB object.
409 * @param setter
410 * specifies how the obtained value is returned to the client.
411 */
412 public Header(SOAPVersion soapVersion, QName name, XMLBridge<?> bridge, EndpointValueSetter setter) {
413 this.soapVersion = soapVersion;
414 this.headerName = name;
415 this.bridge = bridge;
416 this.setter = setter;
417 }
418
419 public Header(SOAPVersion soapVersion, ParameterImpl param, EndpointValueSetter setter) {
420 this(
421 soapVersion,
422 param.getTypeInfo().tagName,
423 param.getXMLBridge(),
424 setter);
425 assert param.getOutBinding()== ParameterBinding.HEADER;
426 }
427
428 private SOAPFaultException createDuplicateHeaderException() {
429 try {
430 SOAPFault fault = soapVersion.getSOAPFactory().createFault();
431 fault.setFaultCode(soapVersion.faultCodeClient);
432 fault.setFaultString(ServerMessages.DUPLICATE_PORT_KNOWN_HEADER(headerName));
433 return new SOAPFaultException(fault);
434 } catch(SOAPException e) {
435 throw new WebServiceException(e);
436 }
437 }
438
439 public void readRequest(Message msg, Object[] args) throws JAXBException {
440 com.sun.xml.internal.ws.api.message.Header header = null;
441 Iterator<com.sun.xml.internal.ws.api.message.Header> it =
442 msg.getHeaders().getHeaders(headerName,true);
443 if (it.hasNext()) {
444 header = it.next();
445 if (it.hasNext()) {
446 throw createDuplicateHeaderException();
447 }
448 }
449
450 if(header!=null) {
451 setter.put( header.readAsJAXB(bridge), args );
452 } else {
453 // header not found.
454 }
455 }
456 }
457
458 /**
459 * Reads the whole payload into a single JAXB bean.
460 */
461 public static final class Body extends EndpointArgumentsBuilder {
462 private final XMLBridge<?> bridge;
463 private final EndpointValueSetter setter;
464
465 /**
466 * @param bridge
467 * specifies how to unmarshal the payload into a JAXB object.
468 * @param setter
469 * specifies how the obtained value is returned to the client.
470 */
471 public Body(XMLBridge<?> bridge, EndpointValueSetter setter) {
472 this.bridge = bridge;
473 this.setter = setter;
474 }
475
476 public void readRequest(Message msg, Object[] args) throws JAXBException {
477 setter.put( msg.readPayloadAsJAXB(bridge), args );
478 }
479 }
480
481 /**
482 * Treats a payload as multiple parts wrapped into one element,
483 * and processes all such wrapped parts.
484 */
485 public static final class DocLit extends EndpointArgumentsBuilder {
486 /**
487 * {@link PartBuilder} keyed by the element name (inside the wrapper element.)
488 */
489 private final PartBuilder[] parts;
490
491 private final XMLBridge wrapper;
492 private final QName wrapperName;
493
494 public DocLit(WrapperParameter wp, Mode skipMode) {
495 wrapperName = wp.getName();
496 wrapper = wp.getXMLBridge();
497 Class wrapperType = (Class) wrapper.getTypeInfo().type;
498
499 List<PartBuilder> parts = new ArrayList<PartBuilder>();
500
501 List<ParameterImpl> children = wp.getWrapperChildren();
502 for (ParameterImpl p : children) {
503 if (p.getMode() == skipMode) {
504 continue;
505 }
506 /*
507 if(p.isIN())
508 continue;
509 */
510 QName name = p.getName();
511 try {
512 parts.add( new PartBuilder(
513 wp.getOwner().getBindingContext().getElementPropertyAccessor(
514 wrapperType,
515 name.getNamespaceURI(),
516 p.getName().getLocalPart()),
517 EndpointValueSetter.get(p)
518 ));
519 // wrapper parameter itself always bind to body, and
520 // so do all its children
521 assert p.getBinding()== ParameterBinding.BODY;
522 } catch (JAXBException e) {
523 throw new WebServiceException( // TODO: i18n
524 wrapperType+" do not have a property of the name "+name,e);
525 }
526 }
527
528 this.parts = parts.toArray(new PartBuilder[parts.size()]);
529 }
530
531 public void readRequest(Message msg, Object[] args) throws JAXBException, XMLStreamException {
532
533 if (parts.length>0) {
534 if (!msg.hasPayload()) {
535 throw new WebServiceException("No payload. Expecting payload with "+wrapperName+" element");
536 }
537 XMLStreamReader reader = msg.readPayload();
538 XMLStreamReaderUtil.verifyTag(reader, wrapperName);
539 Object wrapperBean = wrapper.unmarshal(reader, (msg.getAttachments() != null) ?
540 new AttachmentUnmarshallerImpl(msg.getAttachments()): null);
541
542 try {
543 for (PartBuilder part : parts) {
544 part.readRequest(args,wrapperBean);
545 }
546 } catch (DatabindingException e) {
547 // this can happen when the set method throw a checked exception or something like that
548 throw new WebServiceException(e); // TODO:i18n
549 }
550
551 // we are done with the body
552 reader.close();
553 XMLStreamReaderFactory.recycle(reader);
554 } else {
555 msg.consume();
556 }
557 }
558
559 /**
560 * Unmarshals each wrapped part into a JAXB object and moves it
561 * to the expected place.
562 */
563 static final class PartBuilder {
564 private final PropertyAccessor accessor;
565 private final EndpointValueSetter setter;
566
567 /**
568 * @param accessor
569 * specifies which portion of the wrapper bean to obtain the value from.
570 * @param setter
571 * specifies how the obtained value is returned to the client.
572 */
573 public PartBuilder(PropertyAccessor accessor, EndpointValueSetter setter) {
574 this.accessor = accessor;
575 this.setter = setter;
576 assert accessor!=null && setter!=null;
577 }
578
579 final void readRequest( Object[] args, Object wrapperBean ) {
580 Object obj = accessor.get(wrapperBean);
581 setter.put(obj,args);
582 }
583
584
585 }
586 }
587
588 /**
589 * Treats a payload as multiple parts wrapped into one element,
590 * and processes all such wrapped parts.
591 */
592 public static final class RpcLit extends EndpointArgumentsBuilder {
593 /**
594 * {@link PartBuilder} keyed by the element name (inside the wrapper element.)
595 */
596 private final Map<QName,PartBuilder> parts = new HashMap<QName,PartBuilder>();
597
598 private QName wrapperName;
599
600 public RpcLit(WrapperParameter wp) {
601 assert wp.getTypeInfo().type== WrapperComposite.class;
602
603 wrapperName = wp.getName();
604 List<ParameterImpl> children = wp.getWrapperChildren();
605 for (ParameterImpl p : children) {
606 parts.put( p.getName(), new PartBuilder(
607 p.getXMLBridge(), EndpointValueSetter.get(p)
608 ));
609 // wrapper parameter itself always bind to body, and
610 // so do all its children
611 assert p.getBinding()== ParameterBinding.BODY;
612 }
613 }
614
615 public void readRequest(Message msg, Object[] args) throws JAXBException, XMLStreamException {
616 if (!msg.hasPayload()) {
617 throw new WebServiceException("No payload. Expecting payload with "+wrapperName+" element");
618 }
619 XMLStreamReader reader = msg.readPayload();
620 XMLStreamReaderUtil.verifyTag(reader,wrapperName);
621 reader.nextTag();
622
623 while(reader.getEventType()==XMLStreamReader.START_ELEMENT) {
624 // TODO: QName has a performance issue
625 QName name = reader.getName();
626 PartBuilder part = parts.get(name);
627 if(part==null) {
628 // no corresponding part found. ignore
629 XMLStreamReaderUtil.skipElement(reader);
630 reader.nextTag();
631 } else {
632 part.readRequest(args,reader, msg.getAttachments());
633 }
634 // skip any whitespace
635 if (reader.getEventType() != XMLStreamConstants.START_ELEMENT &&
636 reader.getEventType() != XMLStreamConstants.END_ELEMENT) {
637 XMLStreamReaderUtil.nextElementContent(reader);
638 }
639 if(reader.getEventType() == XMLStreamConstants.END_ELEMENT && name.equals(reader.getName())) {
640 XMLStreamReaderUtil.nextElementContent(reader);
641 }
642 }
643
644 // we are done with the body
645 reader.close();
646 XMLStreamReaderFactory.recycle(reader);
647 }
648
649 /**
650 * Unmarshals each wrapped part into a JAXB object and moves it
651 * to the expected place.
652 */
653 static final class PartBuilder {
654 private final XMLBridge bridge;
655 private final EndpointValueSetter setter;
656
657 /**
658 * @param bridge
659 * specifies how the part is unmarshalled.
660 * @param setter
661 * specifies how the obtained value is returned to the endpoint.
662 */
663 public PartBuilder(XMLBridge bridge, EndpointValueSetter setter) {
664 this.bridge = bridge;
665 this.setter = setter;
666 }
667
668 final void readRequest( Object[] args, XMLStreamReader r, AttachmentSet att) throws JAXBException {
669 Object obj = bridge.unmarshal(r, (att != null)?new AttachmentUnmarshallerImpl(att):null);
670 setter.put(obj,args);
671 }
672 }
673 }
674
675 private static boolean isXMLMimeType(String mimeType){
676 return mimeType.equals("text/xml") || mimeType.equals("application/xml");
677 }
678 }

mercurial