001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU
007: * General Public License Version 2 only ("GPL") or the Common Development
008: * and Distribution License("CDDL") (collectively, the "License"). You
009: * may not use this file except in compliance with the License. You can obtain
010: * a copy of the License at https://glassfish.dev.java.net/public/CDDL+GPL.html
011: * or glassfish/bootstrap/legal/LICENSE.txt. See the License for the specific
012: * language governing permissions and limitations under the License.
013: *
014: * When distributing the software, include this License Header Notice in each
015: * file and include the License file at glassfish/bootstrap/legal/LICENSE.txt.
016: * Sun designates this particular file as subject to the "Classpath" exception
017: * as provided by Sun in the GPL Version 2 section of the License file that
018: * accompanied this code. If applicable, add the following below the License
019: * Header, with the fields enclosed by brackets [] replaced by your own
020: * identifying information: "Portions Copyrighted [year]
021: * [name of copyright owner]"
022: *
023: * Contributor(s):
024: *
025: * If you wish your version of this file to be governed by only the CDDL or
026: * only the GPL Version 2, indicate your decision by adding "[Contributor]
027: * elects to include this software in this distribution under the [CDDL or GPL
028: * Version 2] license." If you don't indicate a single choice of license, a
029: * recipient has the option to distribute your version of this file under
030: * either the CDDL, the GPL Version 2 or to extend the choice of license to
031: * its licensees as provided above. However, if you add GPL Version 2 code
032: * and therefore, elected the GPL Version 2 license, then the option applies
033: * only if the new code is made subject to such option by the copyright
034: * holder.
035: */
036: package com.sun.xml.ws.server.sei;
037:
038: import com.sun.xml.bind.api.AccessorException;
039: import com.sun.xml.bind.api.Bridge;
040: import com.sun.xml.bind.api.CompositeStructure;
041: import com.sun.xml.bind.api.RawAccessor;
042: import com.sun.xml.ws.api.SOAPVersion;
043: import com.sun.xml.ws.api.message.Attachment;
044: import com.sun.xml.ws.api.message.AttachmentSet;
045: import com.sun.xml.ws.api.message.Message;
046: import com.sun.xml.ws.api.model.ParameterBinding;
047: import com.sun.xml.ws.api.streaming.XMLStreamReaderFactory;
048: import com.sun.xml.ws.message.AttachmentUnmarshallerImpl;
049: import com.sun.xml.ws.model.ParameterImpl;
050: import com.sun.xml.ws.model.WrapperParameter;
051: import com.sun.xml.ws.resources.ServerMessages;
052: import com.sun.xml.ws.streaming.XMLStreamReaderUtil;
053:
054: import javax.activation.DataHandler;
055: import javax.imageio.ImageIO;
056: import javax.jws.WebParam.Mode;
057: import javax.xml.bind.JAXBException;
058: import javax.xml.namespace.QName;
059: import javax.xml.soap.SOAPException;
060: import javax.xml.soap.SOAPFault;
061: import javax.xml.stream.XMLStreamException;
062: import javax.xml.stream.XMLStreamReader;
063: import javax.xml.stream.XMLStreamConstants;
064: import javax.xml.transform.Source;
065: import javax.xml.ws.Holder;
066: import javax.xml.ws.WebServiceException;
067: import javax.xml.ws.soap.SOAPFaultException;
068: import java.awt.Image;
069: import java.io.IOException;
070: import java.io.InputStream;
071: import java.io.UnsupportedEncodingException;
072: import java.lang.reflect.Type;
073: import java.util.ArrayList;
074: import java.util.Collection;
075: import java.util.HashMap;
076: import java.util.Iterator;
077: import java.util.List;
078: import java.util.Map;
079:
080: /**
081: * Reads a request {@link Message}, disassembles it, and moves obtained Java values
082: * to the expected places.
083: *
084: * @author Jitendra Kotamraju
085: */
086: abstract class EndpointArgumentsBuilder {
087: /**
088: * Reads a request {@link Message}, disassembles it, and moves obtained
089: * Java values to the expected places.
090: *
091: * @param request
092: * The request {@link Message} to be de-composed.
093: * @param args
094: * The Java arguments given to the SEI method invocation.
095: * Some parts of the reply message may be set to {@link Holder}s in the arguments.
096: * @throws JAXBException
097: * if there's an error during unmarshalling the request message.
098: * @throws XMLStreamException
099: * if there's an error during unmarshalling the request message.
100: */
101: abstract void readRequest(Message request, Object[] args)
102: throws JAXBException, XMLStreamException;
103:
104: static final class None extends EndpointArgumentsBuilder {
105: private None() {
106: }
107:
108: public void readRequest(Message msg, Object[] args) {
109: msg.consume();
110: }
111: }
112:
113: /**
114: * The singleton instance that produces null return value.
115: * Used for operations that doesn't have any output.
116: */
117: public static EndpointArgumentsBuilder NONE = new None();
118:
119: /**
120: * Returns the 'uninitialized' value for the given type.
121: *
122: * <p>
123: * For primitive types, it's '0', and for reference types, it's null.
124: */
125: public static Object getVMUninitializedValue(Type type) {
126: // if this map returns null, that means the 'type' is a reference type,
127: // in which case 'null' is the correct null value, so this code is correct.
128: return primitiveUninitializedValues.get(type);
129: }
130:
131: private static final Map<Class, Object> primitiveUninitializedValues = new HashMap<Class, Object>();
132:
133: static {
134: Map<Class, Object> m = primitiveUninitializedValues;
135: m.put(int.class, (int) 0);
136: m.put(char.class, (char) 0);
137: m.put(byte.class, (byte) 0);
138: m.put(short.class, (short) 0);
139: m.put(long.class, (long) 0);
140: m.put(float.class, (float) 0);
141: m.put(double.class, (double) 0);
142: }
143:
144: /**
145: * {@link EndpointArgumentsBuilder} that sets the VM uninitialized value to the type.
146: */
147: static final class NullSetter extends EndpointArgumentsBuilder {
148: private final EndpointValueSetter setter;
149: private final Object nullValue;
150:
151: public NullSetter(EndpointValueSetter setter, Object nullValue) {
152: assert setter != null;
153: this .nullValue = nullValue;
154: this .setter = setter;
155: }
156:
157: public void readRequest(Message msg, Object[] args) {
158: setter.put(nullValue, args);
159: }
160: }
161:
162: /**
163: * {@link EndpointArgumentsBuilder} that is a composition of multiple
164: * {@link EndpointArgumentsBuilder}s.
165: *
166: * <p>
167: * Sometimes we need to look at multiple parts of the reply message
168: * (say, two header params, one body param, and three attachments, etc.)
169: * and that's when this object is used to combine multiple {@link EndpointArgumentsBuilder}s
170: * (that each responsible for handling one part).
171: *
172: * <p>
173: * The model guarantees that only at most one {@link EndpointArgumentsBuilder} will
174: * return a value as a return value (and everything else has to go to
175: * {@link Holder}s.)
176: */
177: static final class Composite extends EndpointArgumentsBuilder {
178: private final EndpointArgumentsBuilder[] builders;
179:
180: public Composite(EndpointArgumentsBuilder... builders) {
181: this .builders = builders;
182: }
183:
184: public Composite(
185: Collection<? extends EndpointArgumentsBuilder> builders) {
186: this (builders.toArray(new EndpointArgumentsBuilder[builders
187: .size()]));
188: }
189:
190: public void readRequest(Message msg, Object[] args)
191: throws JAXBException, XMLStreamException {
192: for (EndpointArgumentsBuilder builder : builders) {
193: builder.readRequest(msg, args);
194: }
195: }
196: }
197:
198: /**
199: * Reads an Attachment into a Java parameter.
200: */
201: static abstract class AttachmentBuilder extends
202: EndpointArgumentsBuilder {
203: protected final EndpointValueSetter setter;
204: protected final ParameterImpl param;
205: protected final String pname;
206: protected final String pname1;
207:
208: AttachmentBuilder(ParameterImpl param,
209: EndpointValueSetter setter) {
210: this .setter = setter;
211: this .param = param;
212: this .pname = param.getPartName();
213: this .pname1 = "<" + pname;
214: }
215:
216: /**
217: * Creates an AttachmentBuilder based on the parameter type
218: *
219: * @param param
220: * runtime Parameter that abstracts the annotated java parameter
221: * @param setter
222: * specifies how the obtained value is set into the argument. Takes
223: * care of Holder arguments.
224: */
225: public static EndpointArgumentsBuilder createAttachmentBuilder(
226: ParameterImpl param, EndpointValueSetter setter) {
227: Class type = (Class) param.getTypeReference().type;
228: if (DataHandler.class.isAssignableFrom(type)) {
229: return new DataHandlerBuilder(param, setter);
230: } else if (byte[].class == type) {
231: return new ByteArrayBuilder(param, setter);
232: } else if (Source.class.isAssignableFrom(type)) {
233: return new SourceBuilder(param, setter);
234: } else if (Image.class.isAssignableFrom(type)) {
235: return new ImageBuilder(param, setter);
236: } else if (InputStream.class == type) {
237: return new InputStreamBuilder(param, setter);
238: } else if (isXMLMimeType(param.getBinding().getMimeType())) {
239: return new JAXBBuilder(param, setter);
240: } else {
241: throw new UnsupportedOperationException("Unknown Type="
242: + type + " Attachment is not mapped.");
243: }
244: }
245:
246: public void readRequest(Message msg, Object[] args)
247: throws JAXBException, XMLStreamException {
248: // TODO not to loop
249: for (Attachment att : msg.getAttachments()) {
250: String part = getWSDLPartName(att);
251: if (part == null) {
252: continue;
253: }
254: if (part.equals(pname) || part.equals(pname1)) {
255: mapAttachment(att, args);
256: break;
257: }
258: }
259: }
260:
261: abstract void mapAttachment(Attachment att, Object[] args)
262: throws JAXBException;
263: }
264:
265: private static final class DataHandlerBuilder extends
266: AttachmentBuilder {
267: DataHandlerBuilder(ParameterImpl param,
268: EndpointValueSetter setter) {
269: super (param, setter);
270: }
271:
272: void mapAttachment(Attachment att, Object[] args) {
273: setter.put(att.asDataHandler(), args);
274: }
275: }
276:
277: private static final class ByteArrayBuilder extends
278: AttachmentBuilder {
279: ByteArrayBuilder(ParameterImpl param, EndpointValueSetter setter) {
280: super (param, setter);
281: }
282:
283: void mapAttachment(Attachment att, Object[] args) {
284: setter.put(att.asByteArray(), args);
285: }
286: }
287:
288: private static final class SourceBuilder extends AttachmentBuilder {
289: SourceBuilder(ParameterImpl param, EndpointValueSetter setter) {
290: super (param, setter);
291: }
292:
293: void mapAttachment(Attachment att, Object[] args) {
294: setter.put(att.asSource(), args);
295: }
296: }
297:
298: private static final class ImageBuilder extends AttachmentBuilder {
299: ImageBuilder(ParameterImpl param, EndpointValueSetter setter) {
300: super (param, setter);
301: }
302:
303: void mapAttachment(Attachment att, Object[] args) {
304: Image image;
305: InputStream is = null;
306: try {
307: is = att.asInputStream();
308: image = ImageIO.read(is);
309: } catch (IOException ioe) {
310: throw new WebServiceException(ioe);
311: } finally {
312: if (is != null) {
313: try {
314: is.close();
315: } catch (IOException ioe) {
316: throw new WebServiceException(ioe);
317: }
318: }
319: }
320: setter.put(image, args);
321: }
322: }
323:
324: private static final class InputStreamBuilder extends
325: AttachmentBuilder {
326: InputStreamBuilder(ParameterImpl param,
327: EndpointValueSetter setter) {
328: super (param, setter);
329: }
330:
331: void mapAttachment(Attachment att, Object[] args) {
332: setter.put(att.asInputStream(), args);
333: }
334: }
335:
336: private static final class JAXBBuilder extends AttachmentBuilder {
337: JAXBBuilder(ParameterImpl param, EndpointValueSetter setter) {
338: super (param, setter);
339: }
340:
341: void mapAttachment(Attachment att, Object[] args)
342: throws JAXBException {
343: Object obj = param.getBridge().unmarshal(
344: att.asInputStream());
345: setter.put(obj, args);
346: }
347: }
348:
349: /**
350: * Gets the WSDL part name of this attachment.
351: *
352: * <p>
353: * According to WSI AP 1.0
354: * <PRE>
355: * 3.8 Value-space of Content-Id Header
356: * Definition: content-id part encoding
357: * The "content-id part encoding" consists of the concatenation of:
358: * 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:
359: * o Each disallowed character is converted to UTF-8 as one or more bytes.
360: * 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).
361: * o The original character is replaced by the resulting character sequence.
362: * The character '=' (0x3D).
363: * A globally unique value such as a UUID.
364: * The character '@' (0x40).
365: * A valid domain name under the authority of the entity constructing the message.
366: * </PRE>
367: *
368: * So a wsdl:part fooPart will be encoded as:
369: * <fooPart=somereallybignumberlikeauuid@example.com>
370: *
371: * @return null
372: * if the parsing fails.
373: */
374: public static final String getWSDLPartName(
375: com.sun.xml.ws.api.message.Attachment att) {
376: String cId = att.getContentId();
377:
378: int index = cId.lastIndexOf('@', cId.length());
379: if (index == -1) {
380: return null;
381: }
382: String localPart = cId.substring(0, index);
383: index = localPart.lastIndexOf('=', localPart.length());
384: if (index == -1) {
385: return null;
386: }
387: try {
388: return java.net.URLDecoder.decode(localPart.substring(0,
389: index), "UTF-8");
390: } catch (UnsupportedEncodingException e) {
391: throw new WebServiceException(e);
392: }
393: }
394:
395: /**
396: * Reads a header into a JAXB object.
397: */
398: static final class Header extends EndpointArgumentsBuilder {
399: private final Bridge<?> 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,
413: Bridge<?> bridge, EndpointValueSetter setter) {
414: this .soapVersion = soapVersion;
415: this .headerName = name;
416: this .bridge = bridge;
417: this .setter = setter;
418: }
419:
420: public Header(SOAPVersion soapVersion, ParameterImpl param,
421: EndpointValueSetter setter) {
422: this (soapVersion, param.getTypeReference().tagName, param
423: .getBridge(), setter);
424: assert param.getOutBinding() == ParameterBinding.HEADER;
425: }
426:
427: private SOAPFaultException createDuplicateHeaderException() {
428: try {
429: SOAPFault fault = soapVersion.saajSoapFactory
430: .createFault(
431: ServerMessages
432: .DUPLICATE_PORT_KNOWN_HEADER(headerName),
433: soapVersion.faultCodeClient);
434: return new SOAPFaultException(fault);
435: } catch (SOAPException e) {
436: throw new WebServiceException(e);
437: }
438: }
439:
440: public void readRequest(Message msg, Object[] args)
441: throws JAXBException {
442: com.sun.xml.ws.api.message.Header header = null;
443: Iterator<com.sun.xml.ws.api.message.Header> it = msg
444: .getHeaders().getHeaders(headerName, true);
445: if (it.hasNext()) {
446: header = it.next();
447: if (it.hasNext()) {
448: throw createDuplicateHeaderException();
449: }
450: }
451:
452: if (header != null) {
453: setter.put(header.readAsJAXB(bridge), args);
454: } else {
455: // header not found.
456: }
457: }
458: }
459:
460: /**
461: * Reads the whole payload into a single JAXB bean.
462: */
463: static final class Body extends EndpointArgumentsBuilder {
464: private final Bridge<?> bridge;
465: private final EndpointValueSetter setter;
466:
467: /**
468: * @param bridge
469: * specifies how to unmarshal the payload into a JAXB object.
470: * @param setter
471: * specifies how the obtained value is returned to the client.
472: */
473: public Body(Bridge<?> bridge, EndpointValueSetter setter) {
474: this .bridge = bridge;
475: this .setter = setter;
476: }
477:
478: public void readRequest(Message msg, Object[] args)
479: throws JAXBException {
480: setter.put(msg.readPayloadAsJAXB(bridge), args);
481: }
482: }
483:
484: /**
485: * Treats a payload as multiple parts wrapped into one element,
486: * and processes all such wrapped parts.
487: */
488: static final class DocLit extends EndpointArgumentsBuilder {
489: /**
490: * {@link PartBuilder} keyed by the element name (inside the wrapper element.)
491: */
492: private final PartBuilder[] parts;
493:
494: private final Bridge wrapper;
495:
496: public DocLit(WrapperParameter wp, Mode skipMode) {
497: wrapper = wp.getBridge();
498: Class wrapperType = (Class) wrapper.getTypeReference().type;
499:
500: List<PartBuilder> parts = new ArrayList<PartBuilder>();
501:
502: List<ParameterImpl> children = wp.getWrapperChildren();
503: for (ParameterImpl p : children) {
504: if (p.getMode() == skipMode) {
505: continue;
506: }
507: /*
508: if(p.isIN())
509: continue;
510: */
511: QName name = p.getName();
512: try {
513: parts.add(new PartBuilder(wp.getOwner()
514: .getJAXBContext()
515: .getElementPropertyAccessor(wrapperType,
516: name.getNamespaceURI(),
517: p.getName().getLocalPart()),
518: EndpointValueSetter.get(p)));
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
525: + " do not have a property of the name "
526: + name, e);
527: }
528: }
529:
530: this .parts = parts.toArray(new PartBuilder[parts.size()]);
531: }
532:
533: public void readRequest(Message msg, Object[] args)
534: throws JAXBException, XMLStreamException {
535:
536: if (parts.length > 0) {
537: XMLStreamReader reader = msg.readPayload();
538: Object wrapperBean = wrapper
539: .unmarshal(
540: reader,
541: (msg.getAttachments() != null) ? new AttachmentUnmarshallerImpl(
542: msg.getAttachments())
543: : null);
544:
545: try {
546: for (PartBuilder part : parts) {
547: part.readRequest(args, wrapperBean);
548: }
549: } catch (AccessorException e) {
550: // this can happen when the set method throw a checked exception or something like that
551: throw new WebServiceException(e); // TODO:i18n
552: }
553:
554: // we are done with the body
555: reader.close();
556: XMLStreamReaderFactory.recycle(reader);
557: } else {
558: msg.consume();
559: }
560: }
561:
562: /**
563: * Unmarshals each wrapped part into a JAXB object and moves it
564: * to the expected place.
565: */
566: static final class PartBuilder {
567: private final RawAccessor accessor;
568: private final EndpointValueSetter setter;
569:
570: /**
571: * @param accessor
572: * specifies which portion of the wrapper bean to obtain the value from.
573: * @param setter
574: * specifies how the obtained value is returned to the client.
575: */
576: public PartBuilder(RawAccessor accessor,
577: EndpointValueSetter setter) {
578: this .accessor = accessor;
579: this .setter = setter;
580: assert accessor != null && setter != null;
581: }
582:
583: final void readRequest(Object[] args, Object wrapperBean)
584: throws AccessorException {
585: Object obj = accessor.get(wrapperBean);
586: setter.put(obj, args);
587: }
588:
589: }
590: }
591:
592: /**
593: * Treats a payload as multiple parts wrapped into one element,
594: * and processes all such wrapped parts.
595: */
596: static final class RpcLit extends EndpointArgumentsBuilder {
597: /**
598: * {@link PartBuilder} keyed by the element name (inside the wrapper element.)
599: */
600: private final Map<QName, PartBuilder> parts = new HashMap<QName, PartBuilder>();
601:
602: private QName wrapperName;
603:
604: public RpcLit(WrapperParameter wp) {
605: assert wp.getTypeReference().type == CompositeStructure.class;
606:
607: wrapperName = wp.getName();
608: List<ParameterImpl> children = wp.getWrapperChildren();
609: for (ParameterImpl p : children) {
610: parts.put(p.getName(), new PartBuilder(p.getBridge(),
611: EndpointValueSetter.get(p)));
612: // wrapper parameter itself always bind to body, and
613: // so do all its children
614: assert p.getBinding() == ParameterBinding.BODY;
615: }
616: }
617:
618: public void readRequest(Message msg, Object[] args)
619: throws JAXBException, XMLStreamException {
620: XMLStreamReader reader = msg.readPayload();
621: if (!reader.getName().equals(wrapperName))
622: throw new WebServiceException( // TODO: i18n
623: "Unexpected request element "
624: + reader.getName() + " expected: "
625: + wrapperName);
626: reader.nextTag();
627:
628: while (reader.getEventType() == XMLStreamReader.START_ELEMENT) {
629: // TODO: QName has a performance issue
630: PartBuilder part = parts.get(reader.getName());
631: if (part == null) {
632: // no corresponding part found. ignore
633: XMLStreamReaderUtil.skipElement(reader);
634: reader.nextTag();
635: } else {
636: part
637: .readRequest(args, reader, msg
638: .getAttachments());
639: }
640: // skip any whitespace
641: if (reader.getEventType() != XMLStreamConstants.START_ELEMENT
642: && reader.getEventType() != XMLStreamConstants.END_ELEMENT) {
643: XMLStreamReaderUtil.nextElementContent(reader);
644: }
645: }
646:
647: // we are done with the body
648: reader.close();
649: XMLStreamReaderFactory.recycle(reader);
650: }
651:
652: /**
653: * Unmarshals each wrapped part into a JAXB object and moves it
654: * to the expected place.
655: */
656: static final class PartBuilder {
657: private final Bridge bridge;
658: private final EndpointValueSetter setter;
659:
660: /**
661: * @param bridge
662: * specifies how the part is unmarshalled.
663: * @param setter
664: * specifies how the obtained value is returned to the endpoint.
665: */
666: public PartBuilder(Bridge bridge, EndpointValueSetter setter) {
667: this .bridge = bridge;
668: this .setter = setter;
669: }
670:
671: final void readRequest(Object[] args, XMLStreamReader r,
672: AttachmentSet att) throws JAXBException {
673: Object obj = bridge.unmarshal(r,
674: (att != null) ? new AttachmentUnmarshallerImpl(
675: att) : null);
676: setter.put(obj, args);
677: }
678: }
679: }
680:
681: private static boolean isXMLMimeType(String mimeType) {
682: return mimeType.equals("text/xml")
683: || mimeType.equals("application/xml");
684: }
685: }
|