View Javadoc

1   /*
2    * Copyright 2006 the original author or authors.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *      http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  
17  package org.springframework.ws.client.core;
18  
19  import java.io.ByteArrayOutputStream;
20  import java.io.IOException;
21  import java.util.List;
22  import javax.xml.transform.Result;
23  import javax.xml.transform.Source;
24  import javax.xml.transform.Transformer;
25  import javax.xml.transform.TransformerConfigurationException;
26  import javax.xml.transform.TransformerException;
27  
28  import org.apache.commons.logging.Log;
29  import org.apache.commons.logging.LogFactory;
30  
31  import org.springframework.beans.factory.BeanInitializationException;
32  import org.springframework.core.io.ClassPathResource;
33  import org.springframework.core.io.Resource;
34  import org.springframework.oxm.Marshaller;
35  import org.springframework.oxm.Unmarshaller;
36  import org.springframework.util.Assert;
37  import org.springframework.util.ClassUtils;
38  import org.springframework.util.ObjectUtils;
39  import org.springframework.ws.FaultAwareWebServiceMessage;
40  import org.springframework.ws.WebServiceMessage;
41  import org.springframework.ws.WebServiceMessageFactory;
42  import org.springframework.ws.client.WebServiceIOException;
43  import org.springframework.ws.client.WebServiceTransformerException;
44  import org.springframework.ws.client.WebServiceTransportException;
45  import org.springframework.ws.client.support.WebServiceAccessor;
46  import org.springframework.ws.soap.client.core.SoapFaultMessageResolver;
47  import org.springframework.ws.support.MarshallingUtils;
48  import org.springframework.ws.transport.FaultAwareWebServiceConnection;
49  import org.springframework.ws.transport.TransportException;
50  import org.springframework.ws.transport.WebServiceConnection;
51  import org.springframework.ws.transport.WebServiceMessageSender;
52  import org.springframework.ws.transport.context.DefaultTransportContext;
53  import org.springframework.ws.transport.context.TransportContext;
54  import org.springframework.ws.transport.context.TransportContextHolder;
55  import org.springframework.ws.transport.http.HttpUrlConnectionMessageSender;
56  import org.springframework.ws.transport.support.DefaultStrategiesHelper;
57  
58  /**
59   * <strong>The central class for client-side Web services.</strong> It provides a message-driven approach to sending and
60   * receiving {@link WebServiceMessage} instances.
61   * <p/>
62   * Code using this class need only implement callback interfaces, provide {@link Source} objects to read data from, or
63   * use the pluggable {@link Marshaller} support. For invoking the {@link #marshalSendAndReceive marshalling methods},
64   * the {@link #setMarshaller(Marshaller) marshaller} and {@link #setUnmarshaller(Unmarshaller) unmarshaller} properties
65   * must be set.
66   * <p/>
67   * This template uses a {@link SoapFaultMessageResolver} to handle fault response messages. Another {@link
68   * FaultMessageResolver} can be defined with with {@link #setFaultMessageResolver(FaultMessageResolver)
69   * faultMessageResolver} property. If this property is set to <code>null</code>, no fault resolving is performed.
70   * <p/>
71   * This template uses the following algorithm for sending and receiving. <ol> <li>Call to {@link
72   * #createConnection(String) createConnection()}.</li> <li>Call to {@link WebServiceMessageFactory#createWebServiceMessage()
73   * createWebServiceMessage()} on the registered message factory to create a request message.</li> <li>Invoke {@link
74   * WebServiceMessageCallback#doWithMessage(WebServiceMessage) doWithMessage()} on the request callback, if any. This
75   * step stores content in the request message, based on <code>Source</code>, marshalling, etc.</li> <li>Call {@link
76   * WebServiceConnection#send(WebServiceMessage) send()} on the connection.</li> <li>Call {@link
77   * #hasError(WebServiceConnection,WebServiceMessage) hasError()} to check if the connection has an error. For an HTTP
78   * transport, a status code other than <code>2xx</code> indicates an error. However, since a status code of 500 can also
79   * indicate a SOAP fault, the template verifies whether the error is not a fault.</li> <ul> <li>If the connection has an
80   * error, call the {@link #handleError handleError()} method, which by default throws a {@link
81   * WebServiceTransportException}.</li> <li>If the connection has no error, continue with the next step. </ul> <li>Invoke
82   * {@link WebServiceConnection#receive(WebServiceMessageFactory) receive} on the connection to read the response
83   * message, if any.</li> <ul> <li>If no response was received, return <code>null</code> or <code>false</code></li>
84   * <li>Call {@link #hasFault(WebServiceConnection,WebServiceMessage) hasFault()} to determine whether the response has a
85   * fault. If it has, call the {@link #handleFault handleFault()} method.</li> <li>Otherwise, invoke {@link
86   * WebServiceMessageExtractor#extractData(WebServiceMessage) extractData()} on the response extractor, or {@link
87   * WebServiceMessageCallback#doWithMessage(WebServiceMessage) doWithMessage} on the response callback.</li> </ul>
88   * <li>Call to {@link WebServiceConnection#close() close} on the connection.</li> </ol>
89   *
90   * @author Arjen Poutsma
91   * @since 1.0.0
92   */
93  public class WebServiceTemplate extends WebServiceAccessor implements WebServiceOperations {
94  
95      /** Log category to use for message tracing. */
96      public static final String MESSAGE_TRACING_LOG_CATEGORY = "org.springframework.ws.client.MessageTracing";
97  
98      /** Additional logger to use for message tracing. */
99      protected static final Log messageTracingLogger =
100             LogFactory.getLog(WebServiceTemplate.MESSAGE_TRACING_LOG_CATEGORY);
101 
102     private Marshaller marshaller;
103 
104     private Unmarshaller unmarshaller;
105 
106     private FaultMessageResolver faultMessageResolver;
107 
108     private String defaultUri;
109 
110     private boolean checkConnectionForFault = true;
111 
112     /** Creates a new <code>WebServiceTemplate</code> using default settings. */
113     public WebServiceTemplate() {
114         initDefaultStrategies();
115     }
116 
117     /**
118      * Creates a new <code>WebServiceTemplate</code> based on the given message factory.
119      *
120      * @param messageFactory the message factory to use
121      */
122     public WebServiceTemplate(WebServiceMessageFactory messageFactory) {
123         setMessageFactory(messageFactory);
124         initDefaultStrategies();
125     }
126 
127     /** Returns the default URI to be used on operations that do not have a URI parameter. */
128     public String getDefaultUri() {
129         return defaultUri;
130     }
131 
132     /**
133      * Set the default URI to be used on operations that do not have a URI parameter.
134      *
135      * @see #marshalSendAndReceive(Object)
136      * @see #marshalSendAndReceive(Object,WebServiceMessageCallback)
137      * @see #sendSourceAndReceiveToResult(Source,Result)
138      * @see #sendSourceAndReceiveToResult(Source,WebServiceMessageCallback,Result)
139      * @see #sendSourceAndReceive(Source,SourceExtractor)
140      * @see #sendSourceAndReceive(Source,WebServiceMessageCallback,SourceExtractor)
141      * @see #sendAndReceive(WebServiceMessageCallback,WebServiceMessageCallback)
142      */
143     public void setDefaultUri(String uri) {
144         defaultUri = uri;
145     }
146 
147     /** Returns the marshaller for this template. */
148     public Marshaller getMarshaller() {
149         return marshaller;
150     }
151 
152     /** Sets the marshaller for this template. */
153     public void setMarshaller(Marshaller marshaller) {
154         this.marshaller = marshaller;
155     }
156 
157     /** Returns the unmarshaller for this template. */
158     public Unmarshaller getUnmarshaller() {
159         return unmarshaller;
160     }
161 
162     /** Sets the unmarshaller for this template. */
163     public void setUnmarshaller(Unmarshaller unmarshaller) {
164         this.unmarshaller = unmarshaller;
165     }
166 
167     /** Returns the fault message resolver for this template. */
168     public FaultMessageResolver getFaultMessageResolver() {
169         return faultMessageResolver;
170     }
171 
172     /**
173      * Sets the fault resolver for this template. Default is the {@link SimpleFaultMessageResolver}, but may be set to
174      * <code>null</code> to disable fault handling.
175      */
176     public void setFaultMessageResolver(FaultMessageResolver faultMessageResolver) {
177         this.faultMessageResolver = faultMessageResolver;
178     }
179 
180     /**
181      * Indicates whether the {@link FaultAwareWebServiceConnection#hasFault() connection} should be checked for fault
182      * indicators (<code>true</code>), or whether we should rely on the {@link FaultAwareWebServiceMessage#hasFault()
183      * message} only (<code>false</code>). The default is <code>true</code>.
184      * <p/>
185      * When using a HTTP transport, this property defines whether to check the HTTP response status code for fault
186      * indicators. Both the SOAP specification and the WS-I Basic Profile define that a Web service must return a "500
187      * Internal Server Error" HTTP status code if the response envelope is a Fault. Setting this property to
188      * <code>false</code> allows this template to deal with non-conformant services.
189      *
190      * @see #hasFault(WebServiceConnection,WebServiceMessage)
191      * @see <a href="http://www.w3.org/TR/2000/NOTE-SOAP-20000508/#_Toc478383529">SOAP 1.1 specification</a>
192      * @see <a href="http://www.ws-i.org/Profiles/BasicProfile-1.1.html#HTTP_Server_Error_Status_Codes">WS-I Basic
193      *      Profile</a>
194      */
195     public void setCheckConnectionForFault(boolean checkConnectionForFault) {
196         this.checkConnectionForFault = checkConnectionForFault;
197     }
198 
199     /**
200      * Initialize the default implementations for the template's strategies: {@link SoapFaultMessageResolver}, {@link
201      * org.springframework.ws.soap.saaj.SaajSoapMessageFactory}, and {@link HttpUrlConnectionMessageSender}.
202      *
203      * @throws BeanInitializationException in case of initalization errors
204      * @see #setFaultMessageResolver(FaultMessageResolver)
205      * @see #setMessageFactory(WebServiceMessageFactory)
206      * @see #setMessageSender(WebServiceMessageSender)
207      */
208     protected void initDefaultStrategies() {
209         Resource resource = new ClassPathResource(ClassUtils.getShortName(getClass()) + ".properties", getClass());
210         DefaultStrategiesHelper strategiesHelper = new DefaultStrategiesHelper(resource);
211         if (getMessageFactory() == null) {
212             initMessageFactory(strategiesHelper);
213         }
214         if (ObjectUtils.isEmpty(getMessageSenders())) {
215             initMessageSenders(strategiesHelper);
216         }
217         if (getFaultMessageResolver() == null) {
218             initFaultMessageResolver(strategiesHelper);
219         }
220     }
221 
222     private void initMessageFactory(DefaultStrategiesHelper helper) throws BeanInitializationException {
223         WebServiceMessageFactory messageFactory = (WebServiceMessageFactory) helper
224                 .getDefaultStrategy(WebServiceMessageFactory.class);
225         setMessageFactory(messageFactory);
226     }
227 
228     private void initMessageSenders(DefaultStrategiesHelper helper) {
229         List messageSenders = helper.getDefaultStrategies(WebServiceMessageSender.class);
230         setMessageSenders(
231                 (WebServiceMessageSender[]) messageSenders.toArray(new WebServiceMessageSender[messageSenders.size()]));
232     }
233 
234     private void initFaultMessageResolver(DefaultStrategiesHelper helper) throws BeanInitializationException {
235         FaultMessageResolver faultMessageResolver =
236                 (FaultMessageResolver) helper.getDefaultStrategy(FaultMessageResolver.class);
237         setFaultMessageResolver(faultMessageResolver);
238     }
239 
240     //
241     // Marshalling methods
242     //
243 
244     public Object marshalSendAndReceive(final Object requestPayload) {
245         return marshalSendAndReceive(requestPayload, null);
246     }
247 
248     public Object marshalSendAndReceive(String uri, final Object requestPayload) {
249         return marshalSendAndReceive(uri, requestPayload, null);
250     }
251 
252     public Object marshalSendAndReceive(final Object requestPayload, final WebServiceMessageCallback requestCallback) {
253         return marshalSendAndReceive(getDefaultUri(), requestPayload, requestCallback);
254     }
255 
256     public Object marshalSendAndReceive(String uri,
257                                         final Object requestPayload,
258                                         final WebServiceMessageCallback requestCallback) {
259         if (getMarshaller() == null) {
260             throw new IllegalStateException("No marshaller registered. Check configuration of WebServiceTemplate.");
261         }
262         if (getUnmarshaller() == null) {
263             throw new IllegalStateException("No unmarshaller registered. Check configuration of WebServiceTemplate.");
264         }
265         return sendAndReceive(uri, new WebServiceMessageCallback() {
266 
267             public void doWithMessage(WebServiceMessage request) throws IOException, TransformerException {
268                 MarshallingUtils.marshal(getMarshaller(), requestPayload, request);
269                 if (requestCallback != null) {
270                     requestCallback.doWithMessage(request);
271                 }
272             }
273         }, new WebServiceMessageExtractor() {
274 
275             public Object extractData(WebServiceMessage response) throws IOException {
276                 return MarshallingUtils.unmarshal(getUnmarshaller(), response);
277             }
278         });
279     }
280 
281     //
282     // Result-handling methods
283     //
284 
285     public boolean sendSourceAndReceiveToResult(Source requestPayload, Result responseResult) {
286         return sendSourceAndReceiveToResult(requestPayload, null, responseResult);
287     }
288 
289     public boolean sendSourceAndReceiveToResult(String uri, Source requestPayload, Result responseResult) {
290         return sendSourceAndReceiveToResult(uri, requestPayload, null, responseResult);
291     }
292 
293     public boolean sendSourceAndReceiveToResult(Source requestPayload,
294                                                 WebServiceMessageCallback requestCallback,
295                                                 final Result responseResult) {
296         return sendSourceAndReceiveToResult(getDefaultUri(), requestPayload, requestCallback, responseResult);
297     }
298 
299     public boolean sendSourceAndReceiveToResult(String uri,
300                                                 Source requestPayload,
301                                                 WebServiceMessageCallback requestCallback,
302                                                 final Result responseResult) {
303         try {
304             final Transformer transformer = createTransformer();
305             Boolean retVal = (Boolean) doSendAndReceive(uri, transformer, requestPayload, requestCallback,
306                     new SourceExtractor() {
307 
308                         public Object extractData(Source source) throws IOException, TransformerException {
309                             transformer.transform(source, responseResult);
310                             return Boolean.TRUE;
311                         }
312                     });
313             return retVal != null && retVal.booleanValue();
314         }
315         catch (TransformerConfigurationException ex) {
316             throw new WebServiceTransformerException("Could not create transformer", ex);
317         }
318     }
319 
320     //
321     // Source-handling methods
322     //
323 
324     public Object sendSourceAndReceive(final Source requestPayload, final SourceExtractor responseExtractor) {
325         return sendSourceAndReceive(requestPayload, null, responseExtractor);
326     }
327 
328     public Object sendSourceAndReceive(String uri,
329                                        final Source requestPayload,
330                                        final SourceExtractor responseExtractor) {
331         return sendSourceAndReceive(uri, requestPayload, null, responseExtractor);
332     }
333 
334     public Object sendSourceAndReceive(final Source requestPayload,
335                                        final WebServiceMessageCallback requestCallback,
336                                        final SourceExtractor responseExtractor) {
337         return sendSourceAndReceive(getDefaultUri(), requestPayload, requestCallback, responseExtractor);
338     }
339 
340     public Object sendSourceAndReceive(String uri,
341                                        final Source requestPayload,
342                                        final WebServiceMessageCallback requestCallback,
343                                        final SourceExtractor responseExtractor) {
344 
345         try {
346             return doSendAndReceive(uri, createTransformer(), requestPayload, requestCallback, responseExtractor);
347         }
348         catch (TransformerConfigurationException ex) {
349             throw new WebServiceTransformerException("Could not create transformer", ex);
350         }
351     }
352 
353     private Object doSendAndReceive(String uri,
354                                     final Transformer transformer,
355                                     final Source requestPayload,
356                                     final WebServiceMessageCallback requestCallback,
357                                     final SourceExtractor responseExtractor) {
358         Assert.notNull(responseExtractor, "responseExtractor must not be null");
359         return sendAndReceive(uri, new WebServiceMessageCallback() {
360             public void doWithMessage(WebServiceMessage message) throws IOException, TransformerException {
361                 transformer.transform(requestPayload, message.getPayloadResult());
362                 if (requestCallback != null) {
363                     requestCallback.doWithMessage(message);
364                 }
365             }
366         }, new SourceExtractorMessageExtractor(responseExtractor));
367     }
368 
369     //
370     // WebServiceMessage-handling methods
371     //
372 
373     public boolean sendAndReceive(WebServiceMessageCallback requestCallback,
374                                   WebServiceMessageCallback responseCallback) {
375         return sendAndReceive(getDefaultUri(), requestCallback, responseCallback);
376     }
377 
378     public boolean sendAndReceive(String uri,
379                                   WebServiceMessageCallback requestCallback,
380                                   WebServiceMessageCallback responseCallback) {
381         Assert.notNull(responseCallback, "responseCallback must not be null");
382         Boolean result = (Boolean) sendAndReceive(uri, requestCallback,
383                 new WebServiceMessageCallbackMessageExtractor(responseCallback));
384         return result != null && result.booleanValue();
385     }
386 
387     public Object sendAndReceive(WebServiceMessageCallback requestCallback,
388                                  WebServiceMessageExtractor responseExtractor) {
389         return sendAndReceive(getDefaultUri(), requestCallback, responseExtractor);
390     }
391 
392     public Object sendAndReceive(String uri,
393                                  WebServiceMessageCallback requestCallback,
394                                  WebServiceMessageExtractor responseExtractor) {
395         Assert.notNull(responseExtractor, "'responseExtractor' must not be null");
396         Assert.hasLength(uri, "'uri' must not be empty");
397         TransportContext previousTransportContext = TransportContextHolder.getTransportContext();
398         WebServiceConnection connection = null;
399         try {
400             connection = createConnection(uri);
401             TransportContextHolder.setTransportContext(new DefaultTransportContext(connection));
402             WebServiceMessage request = getMessageFactory().createWebServiceMessage();
403             if (requestCallback != null) {
404                 requestCallback.doWithMessage(request);
405             }
406             sendRequest(connection, request);
407             if (hasError(connection, request)) {
408                 return handleError(connection, request);
409             }
410             WebServiceMessage response = connection.receive(getMessageFactory());
411             if (response != null) {
412                 if (hasFault(connection, response)) {
413                     return handleFault(connection, request, response);
414                 }
415                 else {
416                     logResponse(request, response);
417                     return responseExtractor.extractData(response);
418                 }
419             }
420             else {
421                 if (logger.isDebugEnabled()) {
422                     logger.debug("Received no response for request [" + request + "]");
423                 }
424                 return null;
425             }
426         }
427         catch (TransportException ex) {
428             throw new WebServiceTransportException("Could not use transport: " + ex.getMessage(), ex);
429         }
430         catch (TransformerException ex) {
431             throw new WebServiceTransformerException("Transformation error: " + ex.getMessage(), ex);
432         }
433         catch (IOException ex) {
434             throw new WebServiceIOException("I/O error: " + ex.getMessage(), ex);
435         }
436         finally {
437             if (connection != null) {
438                 try {
439                     connection.close();
440                 }
441                 catch (IOException ex) {
442                     logger.debug("Could not close WebServiceConnection", ex);
443                 }
444             }
445             TransportContextHolder.setTransportContext(previousTransportContext);
446         }
447     }
448 
449     /**
450      * Determines whether the given connection or message context has an error.
451      * <p/>
452      * This implementation checks the {@link WebServiceConnection#hasError() connection} first. If it indicates an
453      * error, it makes sure that it is not a {@link FaultAwareWebServiceConnection#hasFault() fault}.
454      *
455      * @param connection the connection (possibly a {@link FaultAwareWebServiceConnection}
456      * @param request    the response message (possibly a {@link FaultAwareWebServiceMessage}
457      * @return <code>true</code> if the connection has an error; <code>false</code> otherwise
458      * @throws IOException in case of I/O errors
459      */
460     protected boolean hasError(WebServiceConnection connection, WebServiceMessage request) throws IOException {
461         if (connection.hasError() && checkConnectionForFault) {
462             if (connection instanceof FaultAwareWebServiceConnection) {
463                 FaultAwareWebServiceConnection faultConnection = (FaultAwareWebServiceConnection) connection;
464                 return !(faultConnection.hasFault() && request instanceof FaultAwareWebServiceMessage);
465             }
466             else {
467                 return true;
468             }
469         }
470         return false;
471     }
472 
473     /**
474      * Handles an error on the given connection. The default implementation throws a {@link
475      * WebServiceTransportException}.
476      *
477      * @param connection the erronous connection
478      * @param request    the corresponding request message
479      * @return the object to be returned from {@link #sendAndReceive(String,WebServiceMessageCallback,
480      *         WebServiceMessageExtractor)}, if any
481      */
482     protected Object handleError(WebServiceConnection connection, WebServiceMessage request) throws IOException {
483         throw new WebServiceTransportException(connection.getErrorMessage());
484     }
485 
486     /**
487      * Determines whether the given connection or message has a fault.
488      * <p/>
489      * This implementation checks the {@link FaultAwareWebServiceConnection#hasFault() connection} if the {@link
490      * #setCheckConnectionForFault(boolean) checkConnectionForFault} property is true, and defaults to the {@link
491      * FaultAwareWebServiceMessage#hasFault() message} otherwise.
492      *
493      * @param connection the connection (possibly a {@link FaultAwareWebServiceConnection}
494      * @param response   the response message (possibly a {@link FaultAwareWebServiceMessage}
495      * @return <code>true</code> if either the connection or the message has a fault; <code>false</code> otherwise
496      * @throws IOException in case of I/O errors
497      */
498     protected boolean hasFault(WebServiceConnection connection, WebServiceMessage response) throws IOException {
499         if (checkConnectionForFault && connection instanceof FaultAwareWebServiceConnection) {
500             // check whether the connection has a fault (i.e. status code 500 in HTTP)
501             FaultAwareWebServiceConnection faultConnection = (FaultAwareWebServiceConnection) connection;
502             if (!faultConnection.hasFault()) {
503                 return false;
504             }
505         }
506         if (response instanceof FaultAwareWebServiceMessage) {
507             // either the connection has a fault, or checkConnectionForFault is false: let's verify the fault
508             FaultAwareWebServiceMessage faultMessage = (FaultAwareWebServiceMessage) response;
509             return faultMessage.hasFault();
510         }
511         return false;
512     }
513 
514     /**
515      * Handles an fault in the given response message. The default implementation invokes the {@link
516      * FaultMessageResolver fault resolver} if registered, or invokes {@link #handleError(WebServiceConnection,
517      * WebServiceMessage)} otherwise.
518      *
519      * @param connection the erronous connection
520      * @param request    the corresponding request message
521      * @param response   the fault response message
522      * @return the object to be returned from {@link #sendAndReceive(String,WebServiceMessageCallback,
523      *         WebServiceMessageExtractor)}, if any
524      */
525     protected Object handleFault(WebServiceConnection connection, WebServiceMessage request, WebServiceMessage response)
526             throws IOException {
527         if (getFaultMessageResolver() != null) {
528             logger.debug("Received Fault message for request [" + request + "]");
529             getFaultMessageResolver().resolveFault(response);
530             return null;
531         }
532         else {
533             return handleError(connection, request);
534         }
535     }
536 
537     /** Sends the request in the given message context over the connection. */
538     private void sendRequest(WebServiceConnection connection, WebServiceMessage request) throws IOException {
539         if (messageTracingLogger.isTraceEnabled()) {
540             ByteArrayOutputStream os = new ByteArrayOutputStream();
541             request.writeTo(os);
542             messageTracingLogger.trace("Sent request [" + os.toString("UTF-8") + "]");
543         }
544         else if (messageTracingLogger.isDebugEnabled()) {
545             messageTracingLogger.debug("Sent request [" + request + "]");
546         }
547         connection.send(request);
548     }
549 
550     private void logResponse(WebServiceMessage request, WebServiceMessage response) throws IOException {
551         if (messageTracingLogger.isTraceEnabled()) {
552             ByteArrayOutputStream requestStream = new ByteArrayOutputStream();
553             request.writeTo(requestStream);
554             ByteArrayOutputStream responseStream = new ByteArrayOutputStream();
555             response.writeTo(responseStream);
556             messageTracingLogger.trace("Received response [" + responseStream.toString("UTF-8") + "] for request [" +
557                     requestStream.toString("UTF-8") + "]");
558         }
559         else if (messageTracingLogger.isDebugEnabled()) {
560             messageTracingLogger.debug("Received response [" + response + "] for request [" + request + "]");
561         }
562     }
563 
564     /** Adapter to enable use of a WebServiceMessageCallback inside a WebServiceMessageExtractor. */
565     private static class WebServiceMessageCallbackMessageExtractor implements WebServiceMessageExtractor {
566 
567         private final WebServiceMessageCallback callback;
568 
569         private WebServiceMessageCallbackMessageExtractor(WebServiceMessageCallback callback) {
570             this.callback = callback;
571         }
572 
573         public Object extractData(WebServiceMessage message) throws IOException, TransformerException {
574             callback.doWithMessage(message);
575             return Boolean.TRUE;
576         }
577     }
578 
579     /** Adapter to enable use of a SourceExtractor inside a WebServiceMessageExtractor. */
580     private static class SourceExtractorMessageExtractor implements WebServiceMessageExtractor {
581 
582         private final SourceExtractor sourceExtractor;
583 
584         private SourceExtractorMessageExtractor(SourceExtractor sourceExtractor) {
585             this.sourceExtractor = sourceExtractor;
586         }
587 
588         public Object extractData(WebServiceMessage message) throws IOException, TransformerException {
589             return sourceExtractor.extractData(message.getPayloadSource());
590         }
591     }
592 
593 }