1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.springframework.ws.soap.saaj;
18
19 import java.io.BufferedInputStream;
20 import java.io.IOException;
21 import java.io.InputStream;
22 import java.io.PushbackInputStream;
23 import java.util.Iterator;
24 import java.util.Map;
25 import java.util.StringTokenizer;
26 import javax.xml.soap.MessageFactory;
27 import javax.xml.soap.MimeHeaders;
28 import javax.xml.soap.SOAPConstants;
29 import javax.xml.soap.SOAPException;
30 import javax.xml.soap.SOAPMessage;
31
32 import org.springframework.beans.factory.InitializingBean;
33 import org.springframework.util.CollectionUtils;
34 import org.springframework.util.StringUtils;
35 import org.springframework.ws.InvalidXmlException;
36 import org.springframework.ws.soap.SoapMessageCreationException;
37 import org.springframework.ws.soap.SoapMessageFactory;
38 import org.springframework.ws.soap.SoapVersion;
39 import org.springframework.ws.soap.saaj.support.SaajUtils;
40 import org.springframework.ws.transport.TransportConstants;
41 import org.springframework.ws.transport.TransportInputStream;
42
43 import org.apache.commons.logging.Log;
44 import org.apache.commons.logging.LogFactory;
45 import org.xml.sax.SAXParseException;
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60 public class SaajSoapMessageFactory implements SoapMessageFactory, InitializingBean {
61
62 private static final Log logger = LogFactory.getLog(SaajSoapMessageFactory.class);
63
64 private MessageFactory messageFactory;
65
66 private String messageFactoryProtocol;
67
68 private boolean langAttributeOnSoap11FaultString = true;
69
70 private Map<String, ?> messageProperties;
71
72
73 public SaajSoapMessageFactory() {
74 }
75
76
77 public SaajSoapMessageFactory(MessageFactory messageFactory) {
78 this.messageFactory = messageFactory;
79 }
80
81
82 public MessageFactory getMessageFactory() {
83 return messageFactory;
84 }
85
86
87 public void setMessageFactory(MessageFactory messageFactory) {
88 this.messageFactory = messageFactory;
89 }
90
91
92
93
94
95 public void setMessageProperties(Map<String, ?> messageProperties) {
96 this.messageProperties = messageProperties;
97 }
98
99
100
101
102
103
104
105
106
107 public void setLangAttributeOnSoap11FaultString(boolean langAttributeOnSoap11FaultString) {
108 this.langAttributeOnSoap11FaultString = langAttributeOnSoap11FaultString;
109 }
110
111 public void setSoapVersion(SoapVersion version) {
112 if (SaajUtils.getSaajVersion() >= SaajUtils.SAAJ_13) {
113 if (SoapVersion.SOAP_11 == version) {
114 messageFactoryProtocol = SOAPConstants.SOAP_1_1_PROTOCOL;
115 }
116 else if (SoapVersion.SOAP_12 == version) {
117 messageFactoryProtocol = SOAPConstants.SOAP_1_2_PROTOCOL;
118 }
119 else {
120 throw new IllegalArgumentException(
121 "Invalid version [" + version + "]. Expected the SOAP_11 or SOAP_12 constant");
122 }
123 }
124 else if (SoapVersion.SOAP_11 != version) {
125 throw new IllegalArgumentException("SAAJ 1.1 and 1.2 only support SOAP 1.1");
126 }
127 }
128
129 public void afterPropertiesSet() {
130 if (messageFactory == null) {
131 try {
132 if (SaajUtils.getSaajVersion() >= SaajUtils.SAAJ_13) {
133 if (!StringUtils.hasLength(messageFactoryProtocol)) {
134 messageFactoryProtocol = SOAPConstants.SOAP_1_1_PROTOCOL;
135 }
136 if (logger.isInfoEnabled()) {
137 logger.info("Creating SAAJ 1.3 MessageFactory with " + messageFactoryProtocol);
138 }
139 messageFactory = MessageFactory.newInstance(messageFactoryProtocol);
140 }
141 else if (SaajUtils.getSaajVersion() == SaajUtils.SAAJ_12) {
142 logger.info("Creating SAAJ 1.2 MessageFactory");
143 messageFactory = MessageFactory.newInstance();
144 }
145 else if (SaajUtils.getSaajVersion() == SaajUtils.SAAJ_11) {
146 logger.info("Creating SAAJ 1.1 MessageFactory");
147 messageFactory = MessageFactory.newInstance();
148 }
149 else {
150 throw new IllegalStateException(
151 "SaajSoapMessageFactory requires SAAJ 1.1, which was not found on the classpath");
152 }
153 }
154 catch (NoSuchMethodError ex) {
155 throw new SoapMessageCreationException(
156 "Could not create SAAJ MessageFactory. Is the version of the SAAJ specification interfaces [" +
157 SaajUtils.getSaajVersionString() +
158 "] the same as the version supported by the application server?", ex);
159 }
160 catch (SOAPException ex) {
161 throw new SoapMessageCreationException("Could not create SAAJ MessageFactory: " + ex.getMessage(), ex);
162 }
163 }
164 if (logger.isDebugEnabled()) {
165 logger.debug("Using MessageFactory class [" + messageFactory.getClass().getName() + "]");
166 }
167 }
168
169 public SaajSoapMessage createWebServiceMessage() {
170 try {
171 SOAPMessage saajMessage = messageFactory.createMessage();
172 postProcess(saajMessage);
173 return new SaajSoapMessage(saajMessage, langAttributeOnSoap11FaultString, messageFactory);
174 }
175 catch (SOAPException ex) {
176 throw new SoapMessageCreationException("Could not create empty message: " + ex.getMessage(), ex);
177 }
178 }
179
180 public SaajSoapMessage createWebServiceMessage(InputStream inputStream) throws IOException {
181 MimeHeaders mimeHeaders = parseMimeHeaders(inputStream);
182 try {
183 inputStream = checkForUtf8ByteOrderMark(inputStream);
184 SOAPMessage saajMessage = messageFactory.createMessage(mimeHeaders, inputStream);
185 postProcess(saajMessage);
186 return new SaajSoapMessage(saajMessage, langAttributeOnSoap11FaultString, messageFactory);
187 }
188 catch (SOAPException ex) {
189
190
191 String contentType = StringUtils
192 .arrayToCommaDelimitedString(mimeHeaders.getHeader(TransportConstants.HEADER_CONTENT_TYPE));
193 if (contentType.contains("startinfo")) {
194 contentType = contentType.replace("startinfo", "start-info");
195 mimeHeaders.setHeader(TransportConstants.HEADER_CONTENT_TYPE, contentType);
196 try {
197 SOAPMessage saajMessage = messageFactory.createMessage(mimeHeaders, inputStream);
198 postProcess(saajMessage);
199 return new SaajSoapMessage(saajMessage,
200 langAttributeOnSoap11FaultString);
201 }
202 catch (SOAPException e) {
203
204 }
205 }
206 throw new SoapMessageCreationException("Could not create message from InputStream: " + ex.getMessage(), ex);
207 } catch (SaajSoapEnvelopeException ex) {
208 SAXParseException parseException = getSAXParseException(ex);
209 if (parseException != null) {
210 throw new InvalidXmlException("Could not parse XML", parseException);
211 } else {
212 throw ex;
213 }
214 }
215 }
216
217 private SAXParseException getSAXParseException(Throwable ex) {
218 if (ex instanceof SAXParseException) {
219 return (SAXParseException) ex;
220 } else if (ex.getCause() != null) {
221 return getSAXParseException(ex.getCause());
222 } else {
223 return null;
224 }
225 }
226
227 private MimeHeaders parseMimeHeaders(InputStream inputStream) throws IOException {
228 MimeHeaders mimeHeaders = new MimeHeaders();
229 if (inputStream instanceof TransportInputStream) {
230 TransportInputStream transportInputStream = (TransportInputStream) inputStream;
231 for (Iterator<String> headerNames = transportInputStream.getHeaderNames(); headerNames.hasNext();) {
232 String headerName = headerNames.next();
233 for (Iterator<String> headerValues = transportInputStream.getHeaders(headerName); headerValues.hasNext();) {
234 String headerValue = headerValues.next();
235 StringTokenizer tokenizer = new StringTokenizer(headerValue, ",");
236 while (tokenizer.hasMoreTokens()) {
237 mimeHeaders.addHeader(headerName, tokenizer.nextToken().trim());
238 }
239 }
240 }
241 }
242 return mimeHeaders;
243 }
244
245
246
247
248
249
250
251 private InputStream checkForUtf8ByteOrderMark(InputStream inputStream) throws IOException {
252 PushbackInputStream pushbackInputStream = new PushbackInputStream(new BufferedInputStream(inputStream), 3);
253 byte[] bytes = new byte[3];
254 int bytesRead = 0;
255 while (bytesRead < bytes.length) {
256 int n = pushbackInputStream.read(bytes, bytesRead, bytes.length - bytesRead);
257 if (n > 0) {
258 bytesRead += n;
259 } else {
260 break;
261 }
262 }
263 if (bytesRead > 0) {
264
265 if (!isByteOrderMark(bytes)) {
266 pushbackInputStream.unread(bytes, 0, bytesRead);
267 }
268 }
269 return pushbackInputStream;
270 }
271
272 private boolean isByteOrderMark(byte[] bytes) {
273 return bytes.length == 3 && bytes[0] == (byte) 0xEF && bytes[1] == (byte) 0xBB && bytes[2] == (byte) 0xBF;
274 }
275
276
277
278
279
280
281
282 protected void postProcess(SOAPMessage soapMessage) throws SOAPException {
283 if (!CollectionUtils.isEmpty(messageProperties)) {
284 for (Map.Entry<String, ?> entry : messageProperties.entrySet()) {
285 soapMessage.setProperty(entry.getKey(), entry.getValue());
286 }
287 }
288 }
289
290 public String toString() {
291 StringBuilder builder = new StringBuilder("SaajSoapMessageFactory[");
292 builder.append(SaajUtils.getSaajVersionString());
293 if (SaajUtils.getSaajVersion() >= SaajUtils.SAAJ_13) {
294 builder.append(',');
295 builder.append(messageFactoryProtocol);
296 }
297 builder.append(']');
298 return builder.toString();
299 }
300 }