View Javadoc

1   /*
2    * Copyright 2005 the original author or authors.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *      http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  
17  package org.springframework.ws.server.endpoint;
18  
19  import java.io.ByteArrayInputStream;
20  import java.io.ByteArrayOutputStream;
21  import javax.xml.namespace.NamespaceContext;
22  import javax.xml.stream.XMLEventReader;
23  import javax.xml.stream.XMLStreamException;
24  import javax.xml.stream.XMLStreamReader;
25  import javax.xml.stream.XMLStreamWriter;
26  import javax.xml.transform.Result;
27  import javax.xml.transform.Source;
28  import javax.xml.transform.TransformerException;
29  import javax.xml.transform.stream.StreamResult;
30  import javax.xml.transform.stream.StreamSource;
31  
32  import org.springframework.ws.WebServiceMessage;
33  import org.springframework.ws.context.MessageContext;
34  import org.springframework.xml.stream.XmlEventStreamReader;
35  import org.springframework.xml.transform.TraxUtils;
36  
37  /**
38   * Abstract base class for endpoints that handle the message payload with streaming StAX. Allows subclasses to read the
39   * request with a <code>XMLStreamReader</code>, and to create a response using a <code>XMLStreamWriter</code>.
40   *
41   * @author Arjen Poutsma
42   * @see #invokeInternal(javax.xml.stream.XMLStreamReader,javax.xml.stream.XMLStreamWriter)
43   * @see XMLStreamReader
44   * @see XMLStreamWriter
45   * @since 1.0.0
46   */
47  public abstract class AbstractStaxStreamPayloadEndpoint extends AbstractStaxPayloadEndpoint implements MessageEndpoint {
48  
49      public final void invoke(MessageContext messageContext) throws Exception {
50          XMLStreamReader streamReader = getStreamReader(messageContext.getRequest().getPayloadSource());
51          XMLStreamWriter streamWriter = new ResponseCreatingStreamWriter(messageContext);
52          invokeInternal(streamReader, streamWriter);
53          streamWriter.close();
54      }
55  
56      private XMLStreamReader getStreamReader(Source source) throws XMLStreamException, TransformerException {
57          if (source == null) {
58              return null;
59          }
60          XMLStreamReader streamReader = null;
61          if (TraxUtils.isStaxSource(source)) {
62              streamReader = TraxUtils.getXMLStreamReader(source);
63              if (streamReader == null) {
64                  XMLEventReader eventReader = TraxUtils.getXMLEventReader(source);
65                  if (eventReader != null) {
66                      try {
67                          streamReader = new XmlEventStreamReader(eventReader);
68                      }
69                      catch (XMLStreamException ex) {
70                          streamReader = null;
71                      }
72                  }
73              }
74  
75          }
76          if (streamReader == null) {
77              try {
78                  streamReader = getInputFactory().createXMLStreamReader(source);
79              }
80              catch (XMLStreamException ex) {
81                  streamReader = null;
82              }
83              catch (UnsupportedOperationException ex) {
84                  streamReader = null;
85              }
86          }
87          if (streamReader == null) {
88              // as a final resort, transform the source to a stream, and read from that
89              ByteArrayOutputStream os = new ByteArrayOutputStream();
90              transform(source, new StreamResult(os));
91              ByteArrayInputStream is = new ByteArrayInputStream(os.toByteArray());
92              streamReader = getInputFactory().createXMLStreamReader(is);
93          }
94          return streamReader;
95      }
96  
97      private XMLStreamWriter getStreamWriter(Result result) {
98          XMLStreamWriter streamWriter = null;
99          if (TraxUtils.isStaxResult(result)) {
100             streamWriter = TraxUtils.getXMLStreamWriter(result);
101         }
102         if (streamWriter == null) {
103             try {
104                 streamWriter = getOutputFactory().createXMLStreamWriter(result);
105             }
106             catch (XMLStreamException ex) {
107                 // ignore
108             }
109         }
110         return streamWriter;
111     }
112 
113     /**
114      * Template method. Subclasses must implement this. Offers the request payload as a <code>XMLStreamReader</code>,
115      * and a <code>XMLStreamWriter</code> to write the response payload to.
116      *
117      * @param streamReader the reader to read the payload from
118      * @param streamWriter the writer to write the payload to
119      */
120     protected abstract void invokeInternal(XMLStreamReader streamReader, XMLStreamWriter streamWriter) throws Exception;
121 
122     /**
123      * Implementation of the <code>XMLStreamWriter</code> interface that creates a response
124      * <code>WebServiceMessage</code> as soon as any method is called, thus lazily creating the response.
125      */
126     private class ResponseCreatingStreamWriter implements XMLStreamWriter {
127 
128         private MessageContext messageContext;
129 
130         private XMLStreamWriter streamWriter;
131 
132         private ByteArrayOutputStream os;
133 
134         private ResponseCreatingStreamWriter(MessageContext messageContext) {
135             this.messageContext = messageContext;
136         }
137 
138         public NamespaceContext getNamespaceContext() {
139             return streamWriter.getNamespaceContext();
140         }
141 
142         public void setNamespaceContext(NamespaceContext context) throws XMLStreamException {
143             createStreamWriter();
144             streamWriter.setNamespaceContext(context);
145         }
146 
147         public void close() throws XMLStreamException {
148             if (streamWriter != null) {
149                 streamWriter.close();
150                 if (os != null) {
151                     streamWriter.flush();
152                     // if we used an output stream cache, we have to transform it to the response again
153                     try {
154                         ByteArrayInputStream is = new ByteArrayInputStream(os.toByteArray());
155                         transform(new StreamSource(is), messageContext.getResponse().getPayloadResult());
156                         os = null;
157                     }
158                     catch (TransformerException ex) {
159                         throw new XMLStreamException(ex);
160                     }
161                 }
162                 streamWriter = null;
163             }
164 
165         }
166 
167         public void flush() throws XMLStreamException {
168             if (streamWriter != null) {
169                 streamWriter.flush();
170             }
171         }
172 
173         public String getPrefix(String uri) throws XMLStreamException {
174             createStreamWriter();
175             return streamWriter.getPrefix(uri);
176         }
177 
178         public Object getProperty(String name) throws IllegalArgumentException {
179             return streamWriter.getProperty(name);
180         }
181 
182         public void setDefaultNamespace(String uri) throws XMLStreamException {
183             createStreamWriter();
184             streamWriter.setDefaultNamespace(uri);
185         }
186 
187         public void setPrefix(String prefix, String uri) throws XMLStreamException {
188             createStreamWriter();
189             streamWriter.setPrefix(prefix, uri);
190         }
191 
192         public void writeAttribute(String localName, String value) throws XMLStreamException {
193             createStreamWriter();
194             streamWriter.writeAttribute(localName, value);
195         }
196 
197         public void writeAttribute(String namespaceURI, String localName, String value) throws XMLStreamException {
198             createStreamWriter();
199             streamWriter.writeAttribute(namespaceURI, localName, value);
200         }
201 
202         public void writeAttribute(String prefix, String namespaceURI, String localName, String value)
203                 throws XMLStreamException {
204             createStreamWriter();
205             streamWriter.writeAttribute(prefix, namespaceURI, localName, value);
206         }
207 
208         public void writeCData(String data) throws XMLStreamException {
209             createStreamWriter();
210             streamWriter.writeCData(data);
211         }
212 
213         public void writeCharacters(String text) throws XMLStreamException {
214             createStreamWriter();
215             streamWriter.writeCharacters(text);
216         }
217 
218         public void writeCharacters(char[] text, int start, int len) throws XMLStreamException {
219             createStreamWriter();
220             streamWriter.writeCharacters(text, start, len);
221         }
222 
223         public void writeComment(String data) throws XMLStreamException {
224             createStreamWriter();
225             streamWriter.writeComment(data);
226         }
227 
228         public void writeDTD(String dtd) throws XMLStreamException {
229             createStreamWriter();
230             streamWriter.writeDTD(dtd);
231         }
232 
233         public void writeDefaultNamespace(String namespaceURI) throws XMLStreamException {
234             createStreamWriter();
235             streamWriter.writeDefaultNamespace(namespaceURI);
236         }
237 
238         public void writeEmptyElement(String localName) throws XMLStreamException {
239             createStreamWriter();
240             streamWriter.writeEmptyElement(localName);
241         }
242 
243         public void writeEmptyElement(String namespaceURI, String localName) throws XMLStreamException {
244             createStreamWriter();
245             streamWriter.writeEmptyElement(namespaceURI, localName);
246         }
247 
248         public void writeEmptyElement(String prefix, String localName, String namespaceURI) throws XMLStreamException {
249             createStreamWriter();
250             streamWriter.writeEmptyElement(prefix, localName, namespaceURI);
251         }
252 
253         public void writeEndDocument() throws XMLStreamException {
254             createStreamWriter();
255             streamWriter.writeEndDocument();
256         }
257 
258         public void writeEndElement() throws XMLStreamException {
259             createStreamWriter();
260             streamWriter.writeEndElement();
261         }
262 
263         public void writeEntityRef(String name) throws XMLStreamException {
264             createStreamWriter();
265             streamWriter.writeEntityRef(name);
266         }
267 
268         public void writeNamespace(String prefix, String namespaceURI) throws XMLStreamException {
269             createStreamWriter();
270             streamWriter.writeNamespace(prefix, namespaceURI);
271         }
272 
273         public void writeProcessingInstruction(String target) throws XMLStreamException {
274             createStreamWriter();
275             streamWriter.writeProcessingInstruction(target);
276         }
277 
278         public void writeProcessingInstruction(String target, String data) throws XMLStreamException {
279             createStreamWriter();
280             streamWriter.writeProcessingInstruction(target, data);
281         }
282 
283         public void writeStartDocument() throws XMLStreamException {
284             createStreamWriter();
285             streamWriter.writeStartDocument();
286         }
287 
288         public void writeStartDocument(String version) throws XMLStreamException {
289             createStreamWriter();
290             streamWriter.writeStartDocument(version);
291         }
292 
293         public void writeStartDocument(String encoding, String version) throws XMLStreamException {
294             createStreamWriter();
295             streamWriter.writeStartDocument(encoding, version);
296         }
297 
298         public void writeStartElement(String localName) throws XMLStreamException {
299             createStreamWriter();
300             streamWriter.writeStartElement(localName);
301         }
302 
303         public void writeStartElement(String namespaceURI, String localName) throws XMLStreamException {
304             createStreamWriter();
305             streamWriter.writeStartElement(namespaceURI, localName);
306         }
307 
308         public void writeStartElement(String prefix, String localName, String namespaceURI) throws XMLStreamException {
309             createStreamWriter();
310             streamWriter.writeStartElement(prefix, localName, namespaceURI);
311         }
312 
313         private void createStreamWriter() throws XMLStreamException {
314             if (streamWriter == null) {
315                 WebServiceMessage response = messageContext.getResponse();
316                 streamWriter = getStreamWriter(response.getPayloadResult());
317                 if (streamWriter == null) {
318                     // as a final resort, use a stream, and transform that at endDocument()
319                     os = new ByteArrayOutputStream();
320                     streamWriter = getOutputFactory().createXMLStreamWriter(os);
321                 }
322             }
323         }
324     }
325 }