001:/*
002: * The contents of this file are subject to the terms
003: * of the Common Development and Distribution License
004: * (the License). You may not use this file except in
005: * compliance with the License.
006: *
007: * You can obtain a copy of the license at
008: * https://glassfish.dev.java.net/public/CDDLv1.0.html.
009: * See the License for the specific language governing
010: * permissions and limitations under the License.
011: *
012: * When distributing Covered Code, include this CDDL
013: * Header Notice in each file and include the License file
014: * at https://glassfish.dev.java.net/public/CDDLv1.0.html.
015: * If applicable, add the following below the CDDL Header,
016: * with the fields enclosed by brackets [] replaced by
017: * you own identifying information:
018: * "Portions Copyrighted [year] [name of copyright owner]"
019: *
020: * Copyright 2006 Sun Microsystems Inc. All Rights Reserved
021: */
022:
023:package com.sun.xml.ws.security.opt.impl.incoming;
024:
025:
026:import com.sun.istack.NotNull;
027:import com.sun.istack.Nullable;
028:import com.sun.istack.XMLStreamReaderToContentHandler;
029:import com.sun.xml.bind.api.Bridge;
030:import com.sun.xml.stream.buffer.MutableXMLStreamBuffer;
031:import com.sun.xml.stream.buffer.stax.StreamReaderBufferCreator;
032:import com.sun.xml.ws.api.SOAPVersion;
033:import com.sun.xml.ws.api.message.AttachmentSet;
034:import com.sun.xml.ws.api.message.Header;
035:import com.sun.xml.ws.api.message.HeaderList;
036:import com.sun.xml.ws.api.message.Message;
037:import com.sun.xml.ws.api.streaming.XMLStreamReaderFactory;
038:import com.sun.xml.ws.encoding.TagInfoset;
039:import com.sun.xml.ws.message.AbstractMessageImpl;
040:import com.sun.xml.ws.message.AttachmentUnmarshallerImpl;
041:import com.sun.xml.ws.streaming.XMLStreamReaderUtil;
042:import com.sun.xml.ws.util.xml.DummyLocation;
043:import com.sun.xml.ws.util.xml.StAXSource;
044:import com.sun.xml.ws.util.xml.XMLStreamReaderToXMLStreamWriter;
045:import java.util.Map;
046:import javax.xml.ws.WebServiceException;
047:import org.xml.sax.ContentHandler;
048:import org.xml.sax.ErrorHandler;
049:import org.xml.sax.SAXException;
050:import org.xml.sax.SAXParseException;
051:import javax.xml.bind.JAXBException;
052:import javax.xml.bind.Unmarshaller;
053:import javax.xml.stream.Location;
054:import javax.xml.stream.XMLStreamConstants;
055:import static javax.xml.stream.XMLStreamConstants.START_DOCUMENT;
056:import static javax.xml.stream.XMLStreamConstants.START_ELEMENT;
057:import javax.xml.stream.XMLStreamException;
058:import javax.xml.stream.XMLStreamReader;
059:import javax.xml.stream.XMLStreamWriter;
060:import javax.xml.transform.Source;
061:import javax.xml.ws.WebServiceException;
062:import com.sun.xml.stream.buffer.MutableXMLStreamBuffer;
063:import com.sun.xml.wss.logging.LogDomainConstants;
064:import java.util.logging.Level;
065:import java.util.logging.Logger;
066:import com.sun.xml.wss.logging.impl.opt.LogStringsMessages;
067:import com.sun.xml.ws.security.opt.impl.util.VerifiedMessageXMLStreamReader;
068:
069:/**
070: * {@link Message} implementation backed by {@link XMLStreamReader}.
071: *
072: * TODO: we need another message class that keeps {@link XMLStreamReader} that points
073: * at the start of the envelope element.
074: */
075:public final class VerifiedStreamMessage extends AbstractMessageImpl {
076: /**
077: * The reader will be positioned at
078: * the first child of the SOAP body
079: */
080: private @NotNull XMLStreamReader reader;
081: private MutableXMLStreamBuffer buffer = null;
082: // lazily created
083: private @Nullable HeaderList headers;
084:
085: private final String payloadLocalName;
086:
087: private final String payloadNamespaceURI;
088:
089: private static final Logger logger = Logger.getLogger(LogDomainConstants.IMPL_OPT_DOMAIN,
090: LogDomainConstants.IMPL_OPT_DOMAIN_BUNDLE);
091:
092: private Map<String, String> bodyEnvNs;
093:
094: /**
095: * infoset about the SOAP envelope, header, and body.
096: *
097: * <p>
098: * If the creater of this object didn't care about those,
099: * we use stock values.
100: */
101: private /*almost final*/ @NotNull TagInfoset envelopeTag,headerTag,bodyTag;
102:
103: /**
104: * Creates a {@link StreamMessage} from a {@link XMLStreamReader}
105: * that points at the start element of the payload, and headers.
106: *
107: * <p>
108: * This method creaets a {@link Message} from a payload.
109: *
110: * @param headers
111: * if null, it means no headers. if non-null,
112: * it will be owned by this message.
113: * @param reader
114: * points at the start element/document of the payload (or the end element of the <s:Body>
115: * if there's no payload)
116: */
117:
118: public VerifiedStreamMessage(@Nullable HeaderList headers, @NotNull AttachmentSet attachmentSet, @NotNull XMLStreamReader reader, @NotNull SOAPVersion soapVersion, Map<String,String> bodyEnvNs) {
119: super (soapVersion);
120: this .headers = headers;
121: this .attachmentSet = attachmentSet;
122: this .reader = reader;
123: this .bodyEnvNs = bodyEnvNs;
124:
125: if(reader.getEventType()== START_DOCUMENT)
126: XMLStreamReaderUtil.nextElementContent(reader);
127:
128: //if the reader is pointing to the end element </soapenv:Body> then its empty message
129: // or no payload
130: if(reader.getEventType() == XMLStreamConstants.END_ELEMENT){
131: String body = reader.getLocalName();
132: String nsUri = reader.getNamespaceURI();
133: assert body != null;
134: assert nsUri != null;
135: //if its not soapenv:Body then throw exception, we received malformed stream
136: if(body.equals("Body") && nsUri.equals(soapVersion.nsUri)){
137: this .payloadLocalName = null;
138: this .payloadNamespaceURI = null;
139: }else{ //TODO: i18n and also we should be throwing better message that this
140: throw new WebServiceException("Malformed stream: {"+nsUri+"}"+body);
141: }
142: }else{
143: this .payloadLocalName = reader.getLocalName();
144: this .payloadNamespaceURI = reader.getNamespaceURI();
145: }
146:
147: // use the default infoset representation for headers
148: int base = soapVersion.ordinal()*3;
149: this .envelopeTag = DEFAULT_TAGS[base];
150: this .headerTag = DEFAULT_TAGS[base+1];
151: this .bodyTag = DEFAULT_TAGS[base+2];
152: }
153:
154: /**
155: * Creates a {@link StreamMessage} from a {@link XMLStreamReader}
156: * and the complete infoset of the SOAP envelope.
157: *
158: * <p>
159: * See {@link #StreamMessage(HeaderList, AttachmentSet, XMLStreamReader, SOAPVersion)} for
160: * the description of the basic parameters.
161: *
162: * @param headerTag
163: * Null if the message didn't have a header tag.
164: *
165: */
166: public VerifiedStreamMessage(@NotNull TagInfoset envelopeTag, @Nullable TagInfoset headerTag, @NotNull AttachmentSet attachmentSet, @Nullable HeaderList headers, @NotNull TagInfoset bodyTag, @NotNull XMLStreamReader reader, @NotNull SOAPVersion soapVersion, Map<String,String> bodyEnvNs) {
167: this (headers,attachmentSet,reader,soapVersion, bodyEnvNs);
168: assert envelopeTag!=null && bodyTag!=null;
169: this .envelopeTag = envelopeTag;
170: this .headerTag = headerTag!=null ? headerTag :
171: new TagInfoset(envelopeTag.nsUri,"Header",envelopeTag.prefix,EMPTY_ATTS);
172: this .bodyTag = bodyTag;
173: }
174:
175: public boolean hasHeaders() {
176: return headers!=null && !headers.isEmpty();
177: }
178:
179: public HeaderList getHeaders() {
180: if (headers == null) {
181: headers = new HeaderList();
182: }
183: return headers;
184: }
185:
186: @Override
187: public @NotNull AttachmentSet getAttachments() {
188: return attachmentSet;
189: }
190:
191: public String getPayloadLocalPart() {
192: return payloadLocalName;
193: }
194:
195: public String getPayloadNamespaceURI() {
196: return payloadNamespaceURI;
197: }
198:
199: public boolean hasPayload() {
200: return payloadLocalName!=null;
201: }
202:
203: public Source readPayloadAsSource() {
204: cacheMessage();
205: if(hasPayload()) {
206: assert unconsumed();
207: return new StAXSource(reader, true);
208: } else
209: return null;
210: }
211:
212: public Object readPayloadAsJAXB(Unmarshaller unmarshaller) throws JAXBException {
213: cacheMessage();
214: if(!hasPayload())
215: return null;
216: assert unconsumed();
217: // TODO: How can the unmarshaller process this as a fragment?
218: if(hasAttachments())
219: unmarshaller.setAttachmentUnmarshaller(new AttachmentUnmarshallerImpl(getAttachments()));
220: try {
221: return unmarshaller.unmarshal(reader);
222: } finally{
223: unmarshaller.setAttachmentUnmarshaller(null);
224: XMLStreamReaderUtil.close(reader);
225: XMLStreamReaderFactory.recycle(reader);
226: }
227: }
228:
229: public <T> T readPayloadAsJAXB(Bridge<T> bridge) throws JAXBException {
230: cacheMessage();
231: if(!hasPayload())
232: return null;
233: assert unconsumed();
234: T r = bridge.unmarshal(reader,
235: hasAttachments() ? new AttachmentUnmarshallerImpl(getAttachments()) : null);
236: XMLStreamReaderUtil.close(reader);
237: XMLStreamReaderFactory.recycle(reader);
238: return r;
239: }
240:
241: @Override
242: public void consume() {
243: assert unconsumed();
244: XMLStreamReaderFactory.recycle(reader);
245: }
246:
247: public XMLStreamReader readPayload() {
248: cacheMessage();
249: // TODO: What about access at and beyond </soap:Body>
250: assert unconsumed();
251: return this .reader;
252: }
253:
254: public void writePayloadTo(XMLStreamWriter writer)throws XMLStreamException {
255: if(payloadLocalName==null)
256: return; // no body
257: assert unconsumed();
258: XMLStreamReaderToXMLStreamWriter conv = new XMLStreamReaderToXMLStreamWriter();
259: while(reader.getEventType() != XMLStreamConstants.END_DOCUMENT){
260: String name = reader.getLocalName();
261: String nsUri = reader.getNamespaceURI();
262:
263: //after previous conv.bridge() call the cursor will be at
264: //END_ELEMENT. Check if its not soapenv:Body then move to next
265: // ELEMENT
266: if(reader.getEventType() == XMLStreamConstants.END_ELEMENT){
267: if(!name.equals("Body") || !nsUri.equals(soapVersion.nsUri)){
268: XMLStreamReaderUtil.nextElementContent(reader);
269: if(reader.getEventType() == XMLStreamConstants.END_DOCUMENT)
270: break;
271: name = reader.getLocalName();
272: nsUri = reader.getNamespaceURI();
273: }
274: }
275: if(name.equals("Body") && nsUri.equals(soapVersion.nsUri) || (reader.getEventType() == XMLStreamConstants.END_DOCUMENT))
276: break;
277: conv.bridge(reader,writer);
278: }
279: reader.close();
280: XMLStreamReaderFactory.recycle(reader);
281: }
282:
283: public void writeTo(XMLStreamWriter sw) throws XMLStreamException{
284: writeEnvelope(sw);
285: }
286:
287: /**
288: * This method should be called when the StreamMessage is created with a payload
289: * @param writer
290: */
291: private void writeEnvelope(XMLStreamWriter writer) throws XMLStreamException {
292: writer.writeStartDocument();
293: envelopeTag.writeStart(writer);
294:
295: //write headers
296: HeaderList hl = getHeaders();
297: if(hl.size() > 0){
298: headerTag.writeStart(writer);
299: for(Header h:hl){
300: h.writeTo(writer);
301: }
302: writer.writeEndElement();
303: }
304: bodyTag.writeStart(writer);
305: if(hasPayload())
306: writePayloadTo(writer);
307: writer.writeEndElement();
308: writer.writeEndElement();
309: writer.writeEndDocument();
310: }
311:
312: public void writePayloadTo(ContentHandler contentHandler, ErrorHandler errorHandler, boolean fragment) throws SAXException {
313: assert unconsumed();
314: try {
315: if(payloadLocalName==null)
316: return; // no body
317:
318: XMLStreamReaderToContentHandler conv =
319: new XMLStreamReaderToContentHandler(reader,contentHandler,true,fragment);
320:
321: while(reader.getEventType() != XMLStreamConstants.END_DOCUMENT){
322: String name = reader.getLocalName();
323: String nsUri = reader.getNamespaceURI();
324:
325: //after previous conv.bridge() call the cursor will be at
326: //END_ELEMENT. Check if its not soapenv:Body then move to next
327: // ELEMENT
328: if(reader.getEventType() == XMLStreamConstants.END_ELEMENT){
329: if(!name.equals("Body") || !nsUri.equals(soapVersion.nsUri)){
330: XMLStreamReaderUtil.nextElementContent(reader);
331: if(reader.getEventType() == XMLStreamConstants.END_DOCUMENT)
332: break;
333: name = reader.getLocalName();
334: nsUri = reader.getNamespaceURI();
335: }
336: }
337: if(name.equals("Body") && nsUri.equals(soapVersion.nsUri) || (reader.getEventType() == XMLStreamConstants.END_DOCUMENT))
338: break;
339:
340: conv.bridge();
341: }
342: reader.close();
343: XMLStreamReaderFactory.recycle(reader);
344: } catch (XMLStreamException e) {
345: Location loc = e.getLocation();
346: if(loc==null) loc = DummyLocation.INSTANCE;
347:
348: SAXParseException x = new SAXParseException(
349: e.getMessage(),loc.getPublicId(),loc.getSystemId(),loc.getLineNumber(),loc.getColumnNumber(),e);
350: errorHandler.error(x);
351: }
352: }
353:
354: public Message copy() {
355: try {
356: // copy the payload
357: XMLStreamReader clone;
358: XMLStreamReader clonedReader ;
359:
360: if(hasPayload()) {
361: assert unconsumed();
362: consumedAt = null; // but we don't want to mark it as consumed
363: MutableXMLStreamBuffer xsb = new MutableXMLStreamBuffer();
364:
365: //the boolean value tells the first body part is written.
366: //based on this we do the right thing
367: StreamReaderBufferCreator c = new StreamReaderBufferCreator(xsb);
368: while(reader.getEventType() != XMLStreamConstants.END_DOCUMENT){
369: String name = reader.getLocalName();
370: String nsUri = reader.getNamespaceURI();
371: if(name.equals("Body") && nsUri.equals(soapVersion.nsUri) || (reader.getEventType() == XMLStreamConstants.END_DOCUMENT))
372: break;
373: c.create(reader);
374: }
375: XMLStreamReaderFactory.recycle(reader);
376:
377: reader = xsb.readAsXMLStreamReader();
378: reader = new VerifiedMessageXMLStreamReader(reader,bodyEnvNs);
379: clone = xsb.readAsXMLStreamReader();
380: clonedReader = new VerifiedMessageXMLStreamReader(clone,bodyEnvNs);
381: // advance to the start tag of the first element
382: proceedToRootElement(reader);
383: proceedToRootElement(clonedReader);
384: } else {
385: // it's tempting to use EmptyMessageImpl, but it doesn't presere the infoset
386: // of <envelope>,<header>, and <body>, so we need to stick to StreamMessage.
387: clone = reader;
388: clonedReader = reader;
389: }
390:
391: return new VerifiedStreamMessage(envelopeTag, headerTag, attachmentSet, HeaderList.copy(headers), bodyTag, clone, soapVersion, this .bodyEnvNs);
392: } catch (XMLStreamException e) {
393: throw new WebServiceException("Failed to copy a message",e);
394: }
395: }
396:
397: private void proceedToRootElement(XMLStreamReader xsr) throws XMLStreamException {
398: assert xsr.getEventType()==START_DOCUMENT;
399: xsr.nextTag();
400: assert xsr.getEventType()==START_ELEMENT;
401: }
402:
403: public void writeTo( ContentHandler contentHandler, ErrorHandler errorHandler ) throws SAXException {
404: contentHandler.setDocumentLocator(NULL_LOCATOR);
405: contentHandler.startDocument();
406: envelopeTag.writeStart(contentHandler);
407: headerTag.writeStart(contentHandler);
408: if(hasHeaders()) {
409: HeaderList headers = getHeaders();
410: int len = headers.size();
411: for( int i=0; i<len; i++ ) {
412: // shouldn't JDK be smart enough to use array-style indexing for this foreach!?
413: headers.get(i).writeTo(contentHandler,errorHandler);
414: }
415: }
416: headerTag.writeEnd(contentHandler);
417: bodyTag.writeStart(contentHandler);
418: writePayloadTo(contentHandler,errorHandler, true);
419: bodyTag.writeEnd(contentHandler);
420: envelopeTag.writeEnd(contentHandler);
421:
422: }
423:
424: /**
425: * Used for an assertion. Returns true when the message is unconsumed,
426: * or otherwise throw an exception.
427: *
428: * <p>
429: * Calling this method also marks the stream as 'consumed'
430: */
431: private boolean unconsumed() {
432: if(payloadLocalName==null)
433: return true; // no payload. can be consumed multiple times.
434:
435: if(reader.getEventType()!=XMLStreamReader.START_ELEMENT) {
436: AssertionError error = new AssertionError("StreamMessage has been already consumed. See the nested exception for where it's consumed");
437: error.initCause(consumedAt);
438: throw error;
439: }
440: consumedAt = new Exception().fillInStackTrace();
441: return true;
442: }
443:
444: /**
445: * Used only for debugging. This records where the message was consumed.
446: */
447: private Throwable consumedAt;
448:
449: /**
450: * Default s:Envelope, s:Header, and s:Body tag infoset definitions.
451: *
452: * We need 3 for SOAP 1.1, 3 for SOAP 1.2.
453: */
454: private static final TagInfoset[] DEFAULT_TAGS;
455:
456: static {
457: DEFAULT_TAGS = new TagInfoset[6];
458: create(SOAPVersion.SOAP_11);
459: create(SOAPVersion.SOAP_12);
460: }
461:
462: private static void create(SOAPVersion v) {
463: int base = v.ordinal()*3;
464: DEFAULT_TAGS[base ] = new TagInfoset(v.nsUri,"Envelope","S",EMPTY_ATTS,"S",v.nsUri);
465: DEFAULT_TAGS[base+1] = new TagInfoset(v.nsUri,"Header","S",EMPTY_ATTS);
466: DEFAULT_TAGS[base+2] = new TagInfoset(v.nsUri,"Body","S",EMPTY_ATTS);
467: }
468:
469: private void cacheMessage() {
470:
471: if (buffer == null) {
472: try{
473: buffer = new com.sun.xml.stream.buffer.MutableXMLStreamBuffer();
474: buffer.createFromXMLStreamReader(reader);
475: }catch (javax.xml.stream.XMLStreamException ex) {
476: logger.log(Level.SEVERE,LogStringsMessages.WSS_1611_PROBLEM_CACHING(),ex);
477: throw new com.sun.xml.wss.impl.XWSSecurityRuntimeException(ex);
478: }
479:
480: try{
481: reader = buffer.readAsXMLStreamReader();
482: reader = new VerifiedMessageXMLStreamReader(reader,bodyEnvNs);
483: reader.next();
484: } catch (XMLStreamException ex) {
485: logger.log(Level.SEVERE,LogStringsMessages.WSS_1612_ERROR_READING_BUFFER(),ex);
486: throw new com.sun.xml.wss.impl.XWSSecurityRuntimeException(ex);
487: }
488: }
489:
490:
491: }
492:
493:}
|