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.springframework.beans.factory.InitializingBean;
29 import org.springframework.util.Assert;
30 import org.springframework.util.StringUtils;
31 import org.springframework.ws.server.endpoint.interceptor.PayloadLoggingInterceptor;
32 import org.springframework.ws.server.endpoint.mapping.PayloadRootQNameEndpointMapping;
33 import org.springframework.ws.soap.SoapMessageFactory;
34 import org.springframework.ws.soap.SoapVersion;
35 import org.springframework.ws.soap.server.endpoint.interceptor.PayloadValidatingInterceptor;
36 import org.springframework.ws.soap.server.endpoint.mapping.SoapActionEndpointMapping;
37 import org.springframework.ws.soap.support.SoapUtils;
38 import org.springframework.ws.transport.TransportConstants;
39 import org.springframework.ws.transport.TransportInputStream;
40
41 import org.apache.axiom.attachments.Attachments;
42 import org.apache.axiom.om.OMAbstractFactory;
43 import org.apache.axiom.om.OMException;
44 import org.apache.axiom.om.impl.MTOMConstants;
45 import org.apache.axiom.soap.SOAP11Constants;
46 import org.apache.axiom.soap.SOAP11Version;
47 import org.apache.axiom.soap.SOAP12Constants;
48 import org.apache.axiom.soap.SOAP12Version;
49 import org.apache.axiom.soap.SOAPFactory;
50 import org.apache.axiom.soap.SOAPMessage;
51 import org.apache.axiom.soap.impl.builder.MTOMStAXSOAPModelBuilder;
52 import org.apache.axiom.soap.impl.builder.StAXSOAPModelBuilder;
53 import org.apache.commons.logging.Log;
54 import org.apache.commons.logging.LogFactory;
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 final XMLInputFactory inputFactory = createXmlInputFactory();
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 = OMAbstractFactory.getSOAP11Factory();
104
105 private boolean langAttributeOnSoap11FaultString = true;
106
107
108
109
110
111
112
113
114 public void setPayloadCaching(boolean payloadCaching) {
115 this.payloadCaching = payloadCaching;
116 }
117
118
119
120
121
122
123
124 public void setAttachmentCaching(boolean attachmentCaching) {
125 this.attachmentCaching = attachmentCaching;
126 }
127
128
129
130
131
132
133
134
135 public void setAttachmentCacheDir(File attachmentCacheDir) {
136 Assert.notNull(attachmentCacheDir, "'attachmentCacheDir' must not be null");
137 Assert.isTrue(attachmentCacheDir.isDirectory(), "'attachmentCacheDir' must be a directory");
138 Assert.isTrue(attachmentCacheDir.canWrite(), "'attachmentCacheDir' must be writable");
139 this.attachmentCacheDir = attachmentCacheDir;
140 }
141
142
143
144
145
146
147
148
149 public void setAttachmentCacheThreshold(int attachmentCacheThreshold) {
150 Assert.isTrue(attachmentCacheThreshold > 0, "'attachmentCacheThreshold' must be larger than 0");
151 this.attachmentCacheThreshold = attachmentCacheThreshold;
152 }
153
154 public void setSoapVersion(SoapVersion version) {
155 if (SoapVersion.SOAP_11 == version) {
156 soapFactory = OMAbstractFactory.getSOAP11Factory();
157 }
158 else if (SoapVersion.SOAP_12 == version) {
159 soapFactory = OMAbstractFactory.getSOAP12Factory();
160 }
161 else {
162 throw new IllegalArgumentException(
163 "Invalid version [" + version + "]. " + "Expected the SOAP_11 or SOAP_12 constant");
164 }
165 }
166
167
168
169
170
171
172
173
174
175 public void setLangAttributeOnSoap11FaultString(boolean langAttributeOnSoap11FaultString) {
176 this.langAttributeOnSoap11FaultString = langAttributeOnSoap11FaultString;
177 }
178
179 public void afterPropertiesSet() throws Exception {
180 if (logger.isInfoEnabled()) {
181 logger.info(payloadCaching ? "Enabled payload caching" : "Disabled payload caching");
182 }
183 if (attachmentCacheDir == null) {
184 String tempDir = System.getProperty("java.io.tmpdir");
185 setAttachmentCacheDir(new File(tempDir));
186 }
187 }
188
189 public AxiomSoapMessage createWebServiceMessage() {
190 return new AxiomSoapMessage(soapFactory, payloadCaching, langAttributeOnSoap11FaultString);
191 }
192
193 public AxiomSoapMessage createWebServiceMessage(InputStream inputStream) throws IOException {
194 Assert.isInstanceOf(TransportInputStream.class, inputStream,
195 "AxiomSoapMessageFactory requires a TransportInputStream");
196 TransportInputStream transportInputStream = (TransportInputStream) inputStream;
197 String contentType = getHeaderValue(transportInputStream, TransportConstants.HEADER_CONTENT_TYPE);
198 if (!StringUtils.hasLength(contentType)) {
199 if (logger.isDebugEnabled()) {
200 logger.debug("TransportInputStream has no Content-Type header; defaulting to \"" +
201 SoapVersion.SOAP_11.getContentType() + "\"");
202 }
203 contentType = SoapVersion.SOAP_11.getContentType();
204 }
205 String soapAction = getHeaderValue(transportInputStream, TransportConstants.HEADER_SOAP_ACTION);
206 if (!StringUtils.hasLength(soapAction)) {
207 soapAction = SoapUtils.extractActionFromContentType(contentType);
208 }
209 try {
210 if (isMultiPartRelated(contentType)) {
211 return createMultiPartAxiomSoapMessage(inputStream, contentType, soapAction);
212 }
213 else {
214 return createAxiomSoapMessage(inputStream, contentType, soapAction);
215 }
216 }
217 catch (XMLStreamException ex) {
218 throw new AxiomSoapMessageCreationException("Could not parse request: " + ex.getMessage(), ex);
219 }
220 catch (OMException ex) {
221 throw new AxiomSoapMessageCreationException("Could not create message: " + ex.getMessage(), ex);
222 }
223 }
224
225 private String getHeaderValue(TransportInputStream transportInputStream, String header) throws IOException {
226 String contentType = null;
227 Iterator<String> iterator = transportInputStream.getHeaders(header);
228 if (iterator.hasNext()) {
229 contentType = iterator.next();
230 }
231 return contentType;
232 }
233
234 private boolean isMultiPartRelated(String contentType) {
235 contentType = contentType.toLowerCase(Locale.ENGLISH);
236 return contentType.contains(MULTI_PART_RELATED_CONTENT_TYPE);
237 }
238
239
240 private AxiomSoapMessage createAxiomSoapMessage(InputStream inputStream, String contentType, String soapAction)
241 throws XMLStreamException {
242 XMLStreamReader reader = inputFactory.createXMLStreamReader(inputStream, getCharSetEncoding(contentType));
243 String envelopeNamespace = getSoapEnvelopeNamespace(contentType);
244 StAXSOAPModelBuilder builder = new StAXSOAPModelBuilder(reader, soapFactory, envelopeNamespace);
245 SOAPMessage soapMessage = builder.getSoapMessage();
246 return new AxiomSoapMessage(soapMessage, soapAction, payloadCaching, langAttributeOnSoap11FaultString);
247 }
248
249
250 private AxiomSoapMessage createMultiPartAxiomSoapMessage(InputStream inputStream,
251 String contentType,
252 String soapAction) throws XMLStreamException {
253 Attachments attachments =
254 new Attachments(inputStream, contentType, attachmentCaching, attachmentCacheDir.getAbsolutePath(),
255 Integer.toString(attachmentCacheThreshold));
256 XMLStreamReader reader = inputFactory.createXMLStreamReader(attachments.getRootPartInputStream(),
257 getCharSetEncoding(attachments.getRootPartContentType()));
258 StAXSOAPModelBuilder builder;
259 String envelopeNamespace = getSoapEnvelopeNamespace(contentType);
260 if (MTOMConstants.SWA_TYPE.equals(attachments.getAttachmentSpecType()) ||
261 MTOMConstants.SWA_TYPE_12.equals(attachments.getAttachmentSpecType())) {
262 builder = new StAXSOAPModelBuilder(reader, soapFactory, envelopeNamespace);
263 }
264 else if (MTOMConstants.MTOM_TYPE.equals(attachments.getAttachmentSpecType())) {
265 builder = new MTOMStAXSOAPModelBuilder(reader, attachments, envelopeNamespace);
266 }
267 else {
268 throw new AxiomSoapMessageCreationException(
269 "Unknown attachment type: [" + attachments.getAttachmentSpecType() + "]");
270 }
271 return new AxiomSoapMessage(builder.getSoapMessage(), attachments, soapAction, payloadCaching,
272 langAttributeOnSoap11FaultString);
273 }
274
275 private String getSoapEnvelopeNamespace(String contentType) {
276 if (contentType.contains(SOAP11Constants.SOAP_11_CONTENT_TYPE)) {
277 return SOAP11Constants.SOAP_ENVELOPE_NAMESPACE_URI;
278 }
279 else if (contentType.contains(SOAP12Constants.SOAP_12_CONTENT_TYPE)) {
280 return SOAP12Constants.SOAP_ENVELOPE_NAMESPACE_URI;
281 }
282 else {
283 throw new AxiomSoapMessageCreationException("Unknown content type '" + contentType + "'");
284 }
285
286 }
287
288
289
290
291
292
293 protected String getCharSetEncoding(String contentType) {
294 int charSetIdx = contentType.indexOf(CHARSET_PARAMETER);
295 if (charSetIdx == -1) {
296 return DEFAULT_CHARSET_ENCODING;
297 }
298 int eqIdx = contentType.indexOf("=", charSetIdx);
299
300 int indexOfSemiColon = contentType.indexOf(";", eqIdx);
301 String value;
302
303 if (indexOfSemiColon > 0) {
304 value = contentType.substring(eqIdx + 1, indexOfSemiColon);
305 }
306 else {
307 value = contentType.substring(eqIdx + 1, contentType.length()).trim();
308 }
309 if (value.startsWith("\"")) {
310 value = value.substring(1);
311 }
312 if (value.endsWith("\"")) {
313 return value.substring(0, value.length() - 1);
314 }
315 if ("null".equalsIgnoreCase(value)) {
316 return DEFAULT_CHARSET_ENCODING;
317 }
318 else {
319 return value.trim();
320 }
321 }
322
323
324
325
326
327
328
329
330
331 protected XMLInputFactory createXmlInputFactory() {
332 return XMLInputFactory.newInstance();
333 }
334
335
336 public String toString() {
337 StringBuilder builder = new StringBuilder("AxiomSoapMessageFactory[");
338 if (soapFactory.getSOAPVersion() == SOAP11Version.getSingleton()) {
339 builder.append("SOAP 1.1");
340 }
341 else if (soapFactory.getSOAPVersion() == SOAP12Version.getSingleton()) {
342 builder.append("SOAP 1.2");
343 }
344 builder.append(',');
345 if (payloadCaching) {
346 builder.append("PayloadCaching enabled");
347 }
348 else {
349 builder.append("PayloadCaching disabled");
350 }
351 builder.append(']');
352 return builder.toString();
353 }
354 }