1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.springframework.ws.soap.axiom;
18
19 import java.io.File;
20 import java.io.IOException;
21 import java.io.InputStream;
22 import java.util.Iterator;
23 import java.util.Locale;
24 import javax.xml.stream.XMLInputFactory;
25 import javax.xml.stream.XMLStreamException;
26 import javax.xml.stream.XMLStreamReader;
27
28 import org.apache.axiom.attachments.Attachments;
29 import org.apache.axiom.om.OMException;
30 import org.apache.axiom.om.impl.MTOMConstants;
31 import org.apache.axiom.soap.SOAP11Constants;
32 import org.apache.axiom.soap.SOAP12Constants;
33 import org.apache.axiom.soap.SOAPFactory;
34 import org.apache.axiom.soap.SOAPMessage;
35 import org.apache.axiom.soap.impl.builder.MTOMStAXSOAPModelBuilder;
36 import org.apache.axiom.soap.impl.builder.StAXSOAPModelBuilder;
37 import org.apache.axiom.soap.impl.llom.soap11.SOAP11Factory;
38 import org.apache.axiom.soap.impl.llom.soap12.SOAP12Factory;
39 import org.apache.commons.logging.Log;
40 import org.apache.commons.logging.LogFactory;
41
42 import org.springframework.beans.factory.InitializingBean;
43 import org.springframework.util.Assert;
44 import org.springframework.util.StringUtils;
45 import org.springframework.ws.WebServiceMessage;
46 import org.springframework.ws.server.endpoint.interceptor.PayloadLoggingInterceptor;
47 import org.springframework.ws.server.endpoint.mapping.PayloadRootQNameEndpointMapping;
48 import org.springframework.ws.soap.SoapMessageFactory;
49 import org.springframework.ws.soap.SoapVersion;
50 import org.springframework.ws.soap.server.endpoint.interceptor.PayloadValidatingInterceptor;
51 import org.springframework.ws.soap.server.endpoint.mapping.SoapActionEndpointMapping;
52 import org.springframework.ws.soap.support.SoapUtils;
53 import org.springframework.ws.transport.TransportConstants;
54 import org.springframework.ws.transport.TransportInputStream;
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82 public class AxiomSoapMessageFactory implements SoapMessageFactory, InitializingBean {
83
84 private static final String CHARSET_PARAMETER = "charset";
85
86 private static final String DEFAULT_CHARSET_ENCODING = "UTF-8";
87
88 private static final String MULTI_PART_RELATED_CONTENT_TYPE = "multipart/related";
89
90 private static final Log logger = LogFactory.getLog(AxiomSoapMessageFactory.class);
91
92 private XMLInputFactory inputFactory;
93
94 private boolean payloadCaching = true;
95
96 private boolean attachmentCaching = false;
97
98 private File attachmentCacheDir;
99
100 private int attachmentCacheThreshold = 4096;
101
102
103 private SOAPFactory soapFactory = new SOAP11Factory();
104
105 private boolean langAttributeOnSoap11FaultString = true;
106
107
108 public AxiomSoapMessageFactory() {
109 inputFactory = XMLInputFactory.newInstance();
110 }
111
112
113
114
115
116
117
118 public void setPayloadCaching(boolean payloadCaching) {
119 this.payloadCaching = payloadCaching;
120 }
121
122
123
124
125
126
127
128 public void setAttachmentCaching(boolean attachmentCaching) {
129 this.attachmentCaching = attachmentCaching;
130 }
131
132
133
134
135
136
137
138
139 public void setAttachmentCacheDir(File attachmentCacheDir) {
140 Assert.notNull(attachmentCacheDir, "'attachmentCacheDir' must not be null");
141 Assert.isTrue(attachmentCacheDir.isDirectory(), "'attachmentCacheDir' must be a directory");
142 Assert.isTrue(attachmentCacheDir.canWrite(), "'attachmentCacheDir' must be writable");
143 this.attachmentCacheDir = attachmentCacheDir;
144 }
145
146
147
148
149
150
151
152
153 public void setAttachmentCacheThreshold(int attachmentCacheThreshold) {
154 Assert.isTrue(attachmentCacheThreshold > 0, "'attachmentCacheThreshold' must be larger than 0");
155 this.attachmentCacheThreshold = attachmentCacheThreshold;
156 }
157
158 public void setSoapVersion(SoapVersion version) {
159 if (SoapVersion.SOAP_11 == version) {
160 soapFactory = new SOAP11Factory();
161 }
162 else if (SoapVersion.SOAP_12 == version) {
163 soapFactory = new SOAP12Factory();
164 }
165 else {
166 throw new IllegalArgumentException(
167 "Invalid version [" + version + "]. " + "Expected the SOAP_11 or SOAP_12 constant");
168 }
169 }
170
171
172
173
174
175
176
177
178
179 public void setlangAttributeOnSoap11FaultString(boolean langAttributeOnSoap11FaultString) {
180 this.langAttributeOnSoap11FaultString = langAttributeOnSoap11FaultString;
181 }
182
183 public void afterPropertiesSet() throws Exception {
184 if (logger.isInfoEnabled()) {
185 logger.info(payloadCaching ? "Enabled payload caching" : "Disabled payload caching");
186 }
187 if (attachmentCacheDir == null) {
188 String tempDir = System.getProperty("java.io.tmpdir");
189 setAttachmentCacheDir(new File(tempDir));
190 }
191 }
192
193 public WebServiceMessage createWebServiceMessage() {
194 return new AxiomSoapMessage(soapFactory, payloadCaching, langAttributeOnSoap11FaultString);
195 }
196
197 public WebServiceMessage createWebServiceMessage(InputStream inputStream) throws IOException {
198 Assert.isInstanceOf(TransportInputStream.class, inputStream,
199 "AxiomSoapMessageFactory requires a TransportInputStream");
200 TransportInputStream transportInputStream = (TransportInputStream) inputStream;
201 String contentType = getHeaderValue(transportInputStream, TransportConstants.HEADER_CONTENT_TYPE);
202 if (!StringUtils.hasLength(contentType)) {
203 if (logger.isDebugEnabled()) {
204 logger.debug("TransportInputStream has no Content-Type header; defaulting to \"" +
205 SoapVersion.SOAP_11.getContentType() + "\"");
206 }
207 contentType = SoapVersion.SOAP_11.getContentType();
208 }
209 String soapAction = getHeaderValue(transportInputStream, TransportConstants.HEADER_SOAP_ACTION);
210 if (!StringUtils.hasLength(soapAction)) {
211 soapAction = SoapUtils.extractActionFromContentType(contentType);
212 }
213 try {
214 if (isMultiPartRelated(contentType)) {
215 return createMultiPartAxiomSoapMessage(inputStream, contentType, soapAction);
216 }
217 else {
218 return createAxiomSoapMessage(inputStream, contentType, soapAction);
219 }
220 }
221 catch (XMLStreamException ex) {
222 throw new AxiomSoapMessageCreationException("Could not parse request: " + ex.getMessage(), ex);
223 }
224 catch (OMException ex) {
225 throw new AxiomSoapMessageCreationException("Could not create message: " + ex.getMessage(), ex);
226 }
227 }
228
229 private String getHeaderValue(TransportInputStream transportInputStream, String header) throws IOException {
230 String contentType = null;
231 Iterator iterator = transportInputStream.getHeaders(header);
232 if (iterator.hasNext()) {
233 contentType = (String) iterator.next();
234 }
235 return contentType;
236 }
237
238 private boolean isMultiPartRelated(String contentType) {
239 contentType = contentType.toLowerCase(Locale.ENGLISH);
240 return contentType.indexOf(MULTI_PART_RELATED_CONTENT_TYPE) != -1;
241 }
242
243
244 private WebServiceMessage createAxiomSoapMessage(InputStream inputStream, String contentType, String soapAction)
245 throws XMLStreamException {
246 XMLStreamReader reader = inputFactory.createXMLStreamReader(inputStream, getCharSetEncoding(contentType));
247 String envelopeNamespace = getSoapEnvelopeNamespace(contentType);
248 StAXSOAPModelBuilder builder = new StAXSOAPModelBuilder(reader, soapFactory, envelopeNamespace);
249 SOAPMessage soapMessage = builder.getSoapMessage();
250 return new AxiomSoapMessage(soapMessage, soapAction, payloadCaching, langAttributeOnSoap11FaultString);
251 }
252
253
254 private AxiomSoapMessage createMultiPartAxiomSoapMessage(InputStream inputStream,
255 String contentType,
256 String soapAction) throws XMLStreamException {
257 Attachments attachments =
258 new Attachments(inputStream, contentType, attachmentCaching, attachmentCacheDir.getAbsolutePath(),
259 Integer.toString(attachmentCacheThreshold));
260 XMLStreamReader reader = inputFactory.createXMLStreamReader(attachments.getSOAPPartInputStream(),
261 getCharSetEncoding(attachments.getSOAPPartContentType()));
262 StAXSOAPModelBuilder builder;
263 String envelopeNamespace = getSoapEnvelopeNamespace(contentType);
264 if (MTOMConstants.SWA_TYPE.equals(attachments.getAttachmentSpecType()) ||
265 MTOMConstants.SWA_TYPE_12.equals(attachments.getAttachmentSpecType())) {
266 builder = new StAXSOAPModelBuilder(reader, soapFactory, envelopeNamespace);
267 }
268 else if (MTOMConstants.MTOM_TYPE.equals(attachments.getAttachmentSpecType())) {
269 builder = new MTOMStAXSOAPModelBuilder(reader, attachments, envelopeNamespace);
270 }
271 else {
272 throw new AxiomSoapMessageCreationException(
273 "Unknown attachment type: [" + attachments.getAttachmentSpecType() + "]");
274 }
275 return new AxiomSoapMessage(builder.getSoapMessage(), attachments, soapAction, payloadCaching,
276 langAttributeOnSoap11FaultString);
277 }
278
279 private String getSoapEnvelopeNamespace(String contentType) {
280 if (contentType.indexOf(SOAP11Constants.SOAP_11_CONTENT_TYPE) != -1) {
281 return SOAP11Constants.SOAP_ENVELOPE_NAMESPACE_URI;
282 }
283 else if (contentType.indexOf(SOAP12Constants.SOAP_12_CONTENT_TYPE) != -1) {
284 return SOAP12Constants.SOAP_ENVELOPE_NAMESPACE_URI;
285 }
286 else {
287 throw new AxiomSoapMessageCreationException("Unknown content type '" + contentType + "'");
288 }
289
290 }
291
292
293
294
295
296
297 protected String getCharSetEncoding(String contentType) {
298 int charSetIdx = contentType.indexOf(CHARSET_PARAMETER);
299 if (charSetIdx == -1) {
300 return DEFAULT_CHARSET_ENCODING;
301 }
302 int eqIdx = contentType.indexOf("=", charSetIdx);
303
304 int indexOfSemiColon = contentType.indexOf(";", eqIdx);
305 String value;
306
307 if (indexOfSemiColon > 0) {
308 value = contentType.substring(eqIdx + 1, indexOfSemiColon);
309 }
310 else {
311 value = contentType.substring(eqIdx + 1, contentType.length()).trim();
312 }
313 if (value.startsWith("\"")) {
314 value = value.substring(1);
315 }
316 if (value.endsWith("\"")) {
317 return value.substring(0, value.length() - 1);
318 }
319 if ("null".equalsIgnoreCase(value)) {
320 return DEFAULT_CHARSET_ENCODING;
321 }
322 else {
323 return value.trim();
324 }
325 }
326
327 public String toString() {
328 StringBuffer buffer = new StringBuffer("AxiomSoapMessageFactory[");
329 if (soapFactory instanceof SOAP11Factory) {
330 buffer.append("SOAP 1.1");
331 }
332 else if (soapFactory instanceof SOAP12Factory) {
333 buffer.append("SOAP 1.2");
334 }
335 buffer.append(']');
336 return buffer.toString();
337 }
338 }