001: /*
002: * Copyright 2006 the original author or authors.
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016:
017: package org.springframework.ws.server.endpoint;
018:
019: import java.io.ByteArrayInputStream;
020: import java.io.ByteArrayOutputStream;
021: import javax.xml.namespace.NamespaceContext;
022: import javax.xml.stream.XMLEventFactory;
023: import javax.xml.stream.XMLEventReader;
024: import javax.xml.stream.XMLEventWriter;
025: import javax.xml.stream.XMLStreamException;
026: import javax.xml.stream.events.XMLEvent;
027: import javax.xml.stream.util.XMLEventConsumer;
028: import javax.xml.transform.Result;
029: import javax.xml.transform.Source;
030: import javax.xml.transform.TransformerException;
031: import javax.xml.transform.stream.StreamResult;
032: import javax.xml.transform.stream.StreamSource;
033:
034: import org.springframework.ws.WebServiceMessage;
035: import org.springframework.ws.context.MessageContext;
036: import org.springframework.xml.transform.StaxResult;
037: import org.springframework.xml.transform.StaxSource;
038:
039: /**
040: * Abstract base class for endpoints that handle the message payload with event-based StAX. Allows subclasses to read
041: * the request with a <code>XMLEventReader</code>, and to create a response using a <code>XMLEventWriter</code>.
042: *
043: * @author Arjen Poutsma
044: * @see #invokeInternal(javax.xml.stream.XMLEventReader,javax.xml.stream.util.XMLEventConsumer,
045: *javax.xml.stream.XMLEventFactory)
046: * @see XMLEventReader
047: * @see XMLEventWriter
048: * @since 1.0.0
049: */
050: public abstract class AbstractStaxEventPayloadEndpoint extends
051: AbstractStaxPayloadEndpoint implements MessageEndpoint {
052:
053: private XMLEventFactory eventFactory;
054:
055: public final void invoke(MessageContext messageContext)
056: throws Exception {
057: XMLEventReader eventReader = getEventReader(messageContext
058: .getRequest().getPayloadSource());
059: XMLEventWriter streamWriter = new ResponseCreatingEventWriter(
060: messageContext);
061: invokeInternal(eventReader, streamWriter, getEventFactory());
062: streamWriter.flush();
063: }
064:
065: /**
066: * Create a <code>XMLEventFactory</code> that this endpoint will use to create <code>XMLEvent</code>s. Can be
067: * overridden in subclasses, adding further initialization of the factory. The resulting
068: * <code>XMLEventFactory</code> is cached, so this method will only be called once.
069: *
070: * @return the created <code>XMLEventFactory</code>
071: */
072: protected XMLEventFactory createXmlEventFactory() {
073: return XMLEventFactory.newInstance();
074: }
075:
076: /** Returns an <code>XMLEventFactory</code> to read XML from. */
077: private XMLEventFactory getEventFactory() {
078: if (eventFactory == null) {
079: eventFactory = createXmlEventFactory();
080: }
081: return eventFactory;
082: }
083:
084: private XMLEventReader getEventReader(Source source)
085: throws XMLStreamException, TransformerException {
086: XMLEventReader eventReader = null;
087: if (source instanceof StaxSource) {
088: StaxSource staxSource = (StaxSource) source;
089: eventReader = staxSource.getXMLEventReader();
090: if (eventReader == null
091: && staxSource.getXMLStreamReader() != null) {
092: try {
093: eventReader = getInputFactory()
094: .createXMLEventReader(
095: staxSource.getXMLStreamReader());
096: } catch (XMLStreamException ex) {
097: // ignore
098: }
099: }
100: }
101: if (eventReader == null) {
102: try {
103: eventReader = getInputFactory().createXMLEventReader(
104: source);
105: } catch (XMLStreamException ex) {
106: // ignore
107: }
108: }
109: if (eventReader == null) {
110: // as a final resort, transform the source to a stream, and read from that
111: ByteArrayOutputStream os = new ByteArrayOutputStream();
112: transform(source, new StreamResult(os));
113: ByteArrayInputStream is = new ByteArrayInputStream(os
114: .toByteArray());
115: eventReader = getInputFactory().createXMLEventReader(is);
116: }
117: return eventReader;
118: }
119:
120: private XMLEventWriter getEventWriter(Result result) {
121: XMLEventWriter eventWriter = null;
122: if (result instanceof StaxResult) {
123: StaxResult staxResult = (StaxResult) result;
124: eventWriter = staxResult.getXMLEventWriter();
125: }
126: if (eventWriter == null) {
127: try {
128: eventWriter = getOutputFactory().createXMLEventWriter(
129: result);
130: } catch (XMLStreamException ex) {
131: // ignore
132: }
133: }
134: return eventWriter;
135: }
136:
137: /**
138: * Template method. Subclasses must implement this. Offers the request payload as a <code>XMLEventReader</code>, and
139: * a <code>XMLEventWriter</code> to write the response payload to.
140: *
141: * @param eventReader the reader to read the payload events from
142: * @param eventWriter the writer to write payload events to
143: * @param eventFactory an <code>XMLEventFactory</code> that can be used to create events
144: */
145: protected abstract void invokeInternal(XMLEventReader eventReader,
146: XMLEventConsumer eventWriter, XMLEventFactory eventFactory)
147: throws Exception;
148:
149: /**
150: * Implementation of the <code>XMLEventWriter</code> interface that creates a response
151: * <code>WebServiceMessage</code> as soon as any method is called, thus lazily creating the response.
152: */
153: private class ResponseCreatingEventWriter implements XMLEventWriter {
154:
155: private XMLEventWriter eventWriter;
156:
157: private MessageContext messageContext;
158:
159: private ByteArrayOutputStream os;
160:
161: public ResponseCreatingEventWriter(MessageContext messageContext) {
162: this .messageContext = messageContext;
163: }
164:
165: public NamespaceContext getNamespaceContext() {
166: return eventWriter.getNamespaceContext();
167: }
168:
169: public void setNamespaceContext(NamespaceContext context)
170: throws XMLStreamException {
171: createEventWriter();
172: eventWriter.setNamespaceContext(context);
173: }
174:
175: public void add(XMLEventReader reader)
176: throws XMLStreamException {
177: createEventWriter();
178: while (reader.hasNext()) {
179: add(reader.nextEvent());
180: }
181: }
182:
183: public void add(XMLEvent event) throws XMLStreamException {
184: createEventWriter();
185: eventWriter.add(event);
186: if (event.isEndDocument()) {
187: if (os != null) {
188: eventWriter.flush();
189: // if we used an output stream cache, we have to transform it to the response again
190: try {
191: ByteArrayInputStream is = new ByteArrayInputStream(
192: os.toByteArray());
193: transform(new StreamSource(is), messageContext
194: .getResponse().getPayloadResult());
195: } catch (TransformerException ex) {
196: throw new XMLStreamException(ex);
197: }
198: }
199: }
200: }
201:
202: public void close() throws XMLStreamException {
203: if (eventWriter != null) {
204: eventWriter.close();
205: }
206: }
207:
208: public void flush() throws XMLStreamException {
209: if (eventWriter != null) {
210: eventWriter.flush();
211: }
212: }
213:
214: public String getPrefix(String uri) throws XMLStreamException {
215: createEventWriter();
216: return eventWriter.getPrefix(uri);
217: }
218:
219: public void setDefaultNamespace(String uri)
220: throws XMLStreamException {
221: createEventWriter();
222: eventWriter.setDefaultNamespace(uri);
223: }
224:
225: public void setPrefix(String prefix, String uri)
226: throws XMLStreamException {
227: createEventWriter();
228: eventWriter.setPrefix(prefix, uri);
229: }
230:
231: private void createEventWriter() throws XMLStreamException {
232: if (eventWriter == null) {
233: WebServiceMessage response = messageContext
234: .getResponse();
235: eventWriter = getEventWriter(response
236: .getPayloadResult());
237: if (eventWriter == null) {
238: // as a final resort, use a stream, and transform that at endDocument()
239: os = new ByteArrayOutputStream();
240: eventWriter = getOutputFactory()
241: .createXMLEventWriter(os);
242: }
243: }
244: }
245: }
246: }
|