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.IOException;
020: import java.lang.reflect.Constructor;
021: import java.lang.reflect.InvocationTargetException;
022: import java.lang.reflect.Method;
023: import javax.xml.parsers.DocumentBuilder;
024: import javax.xml.parsers.DocumentBuilderFactory;
025: import javax.xml.transform.Result;
026: import javax.xml.transform.Source;
027: import javax.xml.transform.dom.DOMSource;
028: import javax.xml.transform.sax.SAXSource;
029: import javax.xml.transform.stream.StreamSource;
030:
031: import nu.xom.Builder;
032: import nu.xom.Document;
033: import nu.xom.Element;
034: import nu.xom.NodeFactory;
035: import nu.xom.Nodes;
036: import nu.xom.ParsingException;
037: import nu.xom.converters.DOMConverter;
038: import org.springframework.util.ClassUtils;
039: import org.springframework.xml.transform.TransformerObjectSupport;
040: import org.w3c.dom.Node;
041: import org.xml.sax.InputSource;
042:
043: /**
044: * Abstract base class for endpoints that handle the message payload as XOM elements. Offers the message payload as a
045: * XOM <code>Element</code>, and allows subclasses to create a response by returning an <code>Element</code>.
046: * <p/>
047: * An <code>AbstractXomPayloadEndpoint</code> only accept one payload element. Multiple payload elements are not in
048: * accordance with WS-I.
049: * <p/>
050: * This class tries to use Java reflection to access some of the non-public classes of XOM
051: * (<code>nu.xom.xslt.XOMResult</code> and <code>nu.xom.xslt.XOMSource</code>). If these classes cannot be accessed
052: * because of security restrictions, a slower approach is used. You can specify whether you want to use the faster, but
053: * non-public reflection-based approach by calling {@link #AbstractXomPayloadEndpoint(boolean)}.
054: *
055: * @author Arjen Poutsma
056: * @see Element
057: * @since 1.0.0
058: */
059: public abstract class AbstractXomPayloadEndpoint extends
060: TransformerObjectSupport implements PayloadEndpoint {
061:
062: private Constructor xomResultConstructor;
063:
064: private Method xomResultGetResultMethod;
065:
066: private Constructor xomSourceConstructor;
067:
068: private boolean useReflection = true;
069:
070: private DocumentBuilderFactory documentBuilderFactory;
071:
072: /**
073: * Creates a new instance of <code>AbstractXomPayloadEndpoint</code> using reflection to access faster, but
074: * non-public XOM classes.
075: */
076: protected AbstractXomPayloadEndpoint() {
077: this (true);
078: }
079:
080: /**
081: * Creates a new instance of <code>AbstractXomPayloadEndpoint</code>.
082: *
083: * @param useReflection specifies whether to use faster, but non-public XOM classes (<code>true</code>); or to use a
084: * converting approach (<code>false</code>)
085: */
086: protected AbstractXomPayloadEndpoint(boolean useReflection) {
087: this .useReflection = useReflection;
088: if (useReflection) {
089: try {
090: Class xomResultClass = ClassUtils
091: .forName("nu.xom.xslt.XOMResult");
092: xomResultConstructor = xomResultClass
093: .getDeclaredConstructor(new Class[] { NodeFactory.class });
094: xomResultConstructor.setAccessible(true);
095: xomResultGetResultMethod = xomResultClass
096: .getDeclaredMethod("getResult", new Class[0]);
097: xomResultGetResultMethod.setAccessible(true);
098: Class xomSourceClass = ClassUtils
099: .forName("nu.xom.xslt.XOMSource");
100: xomSourceConstructor = xomSourceClass
101: .getDeclaredConstructor(new Class[] { Nodes.class });
102: xomSourceConstructor.setAccessible(true);
103: } catch (Exception e) {
104: this .useReflection = false;
105: createDocumentBuilderFactory();
106: }
107: }
108: }
109:
110: public final Source invoke(Source request) throws Exception {
111: if (useReflection) {
112: return invokeUsingReflection(request);
113: } else {
114: return invokeUsingTransformation(request);
115: }
116: }
117:
118: private Source invokeUsingReflection(Source request)
119: throws Exception {
120: try {
121: Element requestElement = null;
122: if (request != null) {
123: Result xomResult = createXomResult();
124: transform(request, xomResult);
125: requestElement = getRequestElement(xomResult);
126: }
127: Element responseElement = invokeInternal(requestElement);
128: return responseElement != null ? createXomSource(responseElement)
129: : null;
130: } catch (IllegalAccessException ex) {
131: useReflection = false;
132: throw ex;
133: } catch (InvocationTargetException ex) {
134: useReflection = false;
135: throw ex;
136: } catch (InstantiationException ex) {
137: useReflection = false;
138: throw ex;
139: }
140: }
141:
142: private Source invokeUsingTransformation(Source request)
143: throws Exception {
144: Element requestElement = null;
145: if (request != null) {
146: if (request instanceof DOMSource) {
147: requestElement = handleDomSource(request);
148: } else if (request instanceof SAXSource) {
149: requestElement = handleSaxSource(request);
150: } else if (request instanceof StreamSource) {
151: requestElement = handleStreamSource(request);
152: } else {
153: throw new IllegalArgumentException(
154: "Source ["
155: + request.getClass().getName()
156: + "] is neither SAXSource, DOMSource, nor StreamSource");
157: }
158: }
159: Element responseElement = invokeInternal(requestElement);
160: if (responseElement != null) {
161: if (documentBuilderFactory == null) {
162: createDocumentBuilderFactory();
163: }
164: DocumentBuilder documentBuilder = documentBuilderFactory
165: .newDocumentBuilder();
166: Document responseDocument = new Document(responseElement);
167: org.w3c.dom.Document w3cDocument = DOMConverter.convert(
168: responseDocument, documentBuilder
169: .getDOMImplementation());
170: return new DOMSource(w3cDocument);
171: } else {
172: return null;
173: }
174:
175: }
176:
177: private Result createXomResult() throws IllegalAccessException,
178: InvocationTargetException, InstantiationException {
179: return (Result) xomResultConstructor
180: .newInstance(new Object[] { new NodeFactory() });
181: }
182:
183: private Element getRequestElement(Result xomResult)
184: throws IllegalAccessException, InvocationTargetException {
185: Nodes result = (Nodes) xomResultGetResultMethod.invoke(
186: xomResult, new Object[0]);
187: if (result.size() == 0) {
188: return null;
189: } else {
190: return (Element) result.get(0);
191: }
192: }
193:
194: private Source createXomSource(Element responseElement)
195: throws IllegalAccessException, InvocationTargetException,
196: InstantiationException {
197: Nodes nodes = new Nodes(responseElement);
198: return (Source) xomSourceConstructor
199: .newInstance(new Object[] { nodes });
200: }
201:
202: private Element handleStreamSource(Source request)
203: throws ParsingException, IOException {
204: StreamSource streamSource = (StreamSource) request;
205: Builder builder = new Builder();
206: Document document = null;
207: if (streamSource.getInputStream() != null) {
208: document = builder.build(streamSource.getInputStream());
209: } else if (streamSource.getReader() != null) {
210: document = builder.build(streamSource.getReader());
211: } else {
212: throw new IllegalArgumentException(
213: "StreamSource contains neither byte stream nor character stream");
214: }
215: return document.getRootElement();
216: }
217:
218: private Element handleSaxSource(Source request)
219: throws ParsingException, IOException {
220: SAXSource saxSource = (SAXSource) request;
221: Builder builder = new Builder(saxSource.getXMLReader());
222: InputSource inputSource = saxSource.getInputSource();
223: Document document = null;
224: if (inputSource.getByteStream() != null) {
225: document = builder.build(inputSource.getByteStream());
226: } else if (inputSource.getCharacterStream() != null) {
227: document = builder.build(inputSource.getCharacterStream());
228: } else {
229: throw new IllegalArgumentException(
230: "InputSource in SAXSource contains neither byte stream nor "
231: + "character stream");
232: }
233: return document.getRootElement();
234: }
235:
236: private Element handleDomSource(Source request) {
237: Node w3cNode = ((DOMSource) request).getNode();
238: org.w3c.dom.Element w3cElement = null;
239: if (w3cNode.getNodeType() == Node.ELEMENT_NODE) {
240: w3cElement = (org.w3c.dom.Element) w3cNode;
241: } else if (w3cNode.getNodeType() == Node.DOCUMENT_NODE) {
242: org.w3c.dom.Document w3cDocument = (org.w3c.dom.Document) w3cNode;
243: w3cElement = w3cDocument.getDocumentElement();
244: }
245: return DOMConverter.convert(w3cElement);
246: }
247:
248: private void createDocumentBuilderFactory() {
249: documentBuilderFactory = DocumentBuilderFactory.newInstance();
250: documentBuilderFactory.setNamespaceAware(true);
251: }
252:
253: /**
254: * Template method. Subclasses must implement this. Offers the request payload as a XOM <code>Element</code>, and
255: * allows subclasses to return a response <code>Element</code>.
256: *
257: * @param requestElement the contents of the SOAP message as XOM element
258: * @return the response element. Can be <code>null</code> to specify no response.
259: */
260: protected abstract Element invokeInternal(Element requestElement)
261: throws Exception;
262:
263: }
|