In this chapter, we will cover:
Logging the message payload manually
Logging both request and response SOAP Envelopes using log4j
Logging both request and response using Spring-WS's Interceptors
Using Eclipse IDE to debug a Spring-WS
Logging and tracing refers to capturing and recording events and data structures about a software program's execution to provide an audit trail. It helps the developers and support team to collect runtime information on the execution of the software program. For any serious software development team, it is very important to implement logging in their system.
For Web-Service development, it is quite useful to be able to see the SOAP messages being transported between client and server. Spring Web-Services offer logging and tracing of SOAP messages, when they arrive, or just before they are sent. Logging, in Spring-WS, is managed by the standard Commons Logging interface.
Generally, log4j is used as the concrete logging library in Spring Projects (as Spring logging feature are build upon log4j). This chapter illustrates a few simple ways of logging SOAP messages.
The recipes illustrated here can be applied to project sources of any recipe in this book. For demonstration purpose, an existing project source of the recipe Setting up an endpoint by annotating the payload-root is used, as this can be applied to any project used in this book.
Message payload is the content of the SOAP message element, SOAP-ENV:Body
. This is the exact message part of the whole SOAP Envelope for both request and response.
This recipe demonstrates logging the message payload manually from inside the code.
In this recipe, the project's name is LiveRestaurant_R-5.1
(for the server-side Web-Service) and has the following Maven dependencies:
spring-ws-core-2.0.1.RELEASE.jar
log4j-1.2.9.jar
And LiveRestaurant_R-5.1-Client
(for the client side), with the following Maven dependencies:
spring-ws-core-2.0.1.RELEASE.jar
log4j-1.2.9.jar
This recipe uses projects used in the recipe Setting up an endpoint by annotating the payload-root in Chapter 1,Building SOAP Web-Services.
Modify log4j.properties
to default the log level into INFO
. Remove any type of debug setting for any package or API in log4j.properties
.
Modify OrderServiceEndpoint
to create two xmlToString
methods and call these two methods to convert incoming messages into String and log it.
Build and deploy the project in the Maven-embedded Tomcat server. Run mvn clean package tomcat:run
from the root of the project in a command line window.
To test this, open a new command line window and go to the folder LiveRestaurant_R-5.1-Client
and run: mvn clean package exec:java
.
Here is the output from the server-side console:
INFO [http-8080-1] (OrderSeviceEndpoint.java:49) -
Message Payload method handlePlaceOrderRequest start ====
<?xml version="1.0" encoding="UTF-8"?>
<tns:placeOrderRequest xmlns:tns="....">
<tns:order>
<tns:refNumber>9999</tns:refNumber>
..........
</tns:customer>
<tns:dateSubmitted>2008-09-29T05:49:45</tns:dateSubmitted>
<tns:orderDate>2014-09-19T03:18:33</tns:orderDate>
<tns:items>
<tns:type>Snacks</tns:type>
<tns:name>Pitza</tns:name>
<tns:quantity>2</tns:quantity>
</tns:items>
</tns:order>
</tns:placeOrderRequest>
==== Message Payload End
........................
INFO [http-8080-1] (OrderSeviceEndpoint.java:67) -
Message Payload method handleCancelOrderRequest start ====
<?xml version="1.0" encoding="UTF-8"?>
<tns:cancelOrderRequest xmlns:tns="...">
<tns:refNumber>9999</tns:refNumber>
</tns:cancelOrderRequest>
==== Message Payload End
The code simply logs the message payload manually, without any configuration change anywhere in the application. The changes in the log4j.properties
makes sure that the log messages are printed to the console (as the appender is the ConsoleAppender)
and no debug messages are printed:
log4j.rootLogger=INFO, stdout log4j.appender.stdout=org.apache.log4j.ConsoleAppender log4j.appender.stdout.layout=org.apache.log4j.PatternLayout # Pattern to output the caller's file name and line number. log4j.appender.stdout.layout.ConversionPattern=%5p [%t] (%F:%L) - %m%n
The method xmlToString(...)
transforms the XML Source/Element Object into a String
using a StringWriter:
private String xmlToString(Node node) { try { Source source = new DOMSource(node); StringWriter stringWriter = new StringWriter(); Result result = new StreamResult(stringWriter); TransformerFactory factory = TransformerFactory.newInstance(); Transformer transformer = factory.newTransformer(); transformer.transform(source, result); return stringWriter.getBuffer().toString(); } catch (TransformerConfigurationException e) { e.printStackTrace(); } catch (TransformerException e) { e.printStackTrace(); } return null; } private static String xmlToString(Source source) { try { StringWriter stringWriter = new StringWriter(); Result result = new StreamResult(stringWriter); TransformerFactory factory = TransformerFactory.newInstance(); Transformer transformer = factory.newTransformer(); transformer.transform(source, result); return stringWriter.getBuffer().toString(); } catch (TransformerConfigurationException e) { e.printStackTrace(); } catch (TransformerException e) { e.printStackTrace(); } return null; }
In the handleCancelOrderRequest()
and handlePlaceOrderRequest()
methods, xmlToString()
is invoked passing the Source/Element of the RequestPayload
to return the message payload as a String instance, which is then logged into the configured logging appender (console in this case):
@PayloadRoot(localPart = "placeOrderRequest", namespace = SERVICE_NS) public @ResponsePayload Source handlePlaceOrderRequest(@RequestPayload Source source) throws Exception { String placeOrderRequestMessage = xmlToString(source); logger.info(" Message Payload method handlePlaceOrderRequest start ==== " + placeOrderRequestMessage + " ==== Message Payload End "); //extract data from input parameter String fName="John"; String lName="Smith"; String refNumber="1234"; return new StringSource( "<tns:placeOrderResponse xmlns:tns="http://www.packtpub.com/liverestaurant/OrderService/schema"><tns:refNumber>"+orderService.placeOrder(fName, lName, refNumber)+"</tns:refNumber></tns:placeOrderResponse>"); } @PayloadRoot(namespace = SERVICE_NS, localPart = "cancelOrderRequest") @ResponsePayload public Source handleCancelOrderRequest(@RequestPayload Element cancelOrderRequest) throws Exception { String refNumber=cancelOrderRequest.getElementsByTagNameNS(SERVICE_NS, "refNumber") .item(0).getTextContent(); String cancelOrderRequestMessage = xmlToString(cancelOrderRequest); logger.info(" Message Payload method handleCancelOrderRequest start ==== " + cancelOrderRequestMessage + " ==== Message Payload End "); return new StringSource( "<tns:cancelOrderResponse xmlns:tns="http://www.packtpub.com/liverestaurant/OrderService/schema"><tns:cancelled>"+orderService.cancelOrder(refNumber)+"</tns:cancelled></tns:cancelOrderResponse>"); }
The example given in this recipe makes use of SimpleMethodEndpointMapping
, which receives the message payload in the form of XML Source (javax.xml.transform.Source)
or the Element (org.w3c.dom.Element)
object as the method argument, with the help of the RequestPayload
annotation, whereas in other cases, the incoming message will be in a different form. For example, marshalling endpoint the input is already a marshalled object. You will need to adopt the appropriate mechanisms to transform the incoming argument in those cases. The recipes after that will give you insights on other approaches of logging and tracing.
Setting up an endpoint by annotating the payload-root in Chapter 1,Building SOAP Web-Services.
Spring-WS framework allows the developer to log the entire SOAP message using simple logger configuration. This recipe illustrates configuring this internal logging of SOAP messages by the framework with log4j logger framework.
In this recipe, the project's name is LiveRestaurant_R-5.2
(for the server-side Web Service) and has the following Maven dependencies:
spring-ws-core-2.0.1.RELEASE.jar
log4j-1.2.9.jar
It also has LiveRestaurant_R-5.2-Client
(for the client side) with the following Maven dependencies:
spring-ws-core-2.0.1.RELEASE.jar
log4j-1.2.9.jar
This recipe uses projects used in the recipe Setting up an endpoint by annotating the payload-root:
Modify log4j.properties
to set message tracing.
Build and deploy the project in the Maven-embedded Tomcat server. Run mvn clean package tomcat:run
from the root of the project in a command-line window.
To test this, open a new command-line window, go to the folder LiveRestaurant_R-5.1-Client
, and run mvn clean package exec:java
.
The following is the output from the server-side console (please note the SOAP-Env:Envelope
element of the Web-Service response generated in the message):
DEBUG [http-8080-1] (MessageDispatcher.java:167) - Received request
....
<SOAP-ENV:Envelope xmlns:SOAP-ENV="..."><SOAP-ENV:Body>
....
<tns:placeOrderRequest xmlns:tns="......">
<tns:order>
<tns:refNumber>9999</tns:refNumber>
<tns:customer>
......
</tns:customer>
<tns:dateSubmitted>2008-09-29T05:49:45</tns:dateSubmitted>
<tns:orderDate>2014-09-19T03:18:33</tns:orderDate>
<tns:items>
<tns:type>Snacks</tns:type>
<tns:name>Pitza</tns:name>
<tns:quantity>2</tns:quantity>
</tns:items>
</tns:order>
</tns:placeOrderRequest>
</SOAP-ENV:Body></SOAP-ENV:Envelope>
....
DEBUG [http-8080-1] (MessageDispatcher.java:177) - Sent response
....
<SOAP-ENV:Envelope xmlns:SOAP-ENV="..."><SOAP-ENV:Body>
.....
<tns:placeOrderResponse xmlns:tns="...">
<tns:refNumber>order-John_Smith_1234</tns:refNumber></tns:placeOrderResponse>
</SOAP-ENV:Body></SOAP-ENV:Envelope>
...
DEBUG [http-8080-1] (MessageDispatcher.java:167) - Received request ...
<SOAP-ENV:Envelope xmlns:SOAP-ENV="..."><SOAP-ENV:Body>
....
<tns:cancelOrderRequest xmlns:tns="....">
<tns:refNumber>9999</tns:refNumber>
</tns:cancelOrderRequest>
</SOAP-ENV:Body></SOAP-ENV:Envelope>
...
DEBUG [http-8080-1] (MessageDispatcher.java:177) - Sent response
...
<SOAP-ENV:Envelope xmlns:SOAP-ENV="..."><SOAP-ENV:Body>
.....
<tns:cancelOrderResponse xmlns:tns="....">
<tns:cancelled>true</tns:cancelled></tns:cancelOrderResponse>
</SOAP-ENV:Body></SOAP-ENV:Envelope>
...
The very core component of Spring-WS framework, namely, MessageDispatcher
, logs every incoming SOAP message as soon as it receives it in the receive()
method, after extracting the message content from the MessageContext
, if logging is enabled for tracing or debugging.
In the receive()
method, it checks for log settings for a named log instance, org.springframework.ws.server.MessageTracing.received
checks for logging SOAP requests, and org.springframework.ws.server.MessageTracing.sent
checks for SOAP responses. If those settings are given a value of either TRACE
or DEBUG
, it prints the entire SOAP Envelope of the corresponding request or response:
log4j.rootLogger=INFO, stdout, R log4j.appender.stdout=org.apache.log4j.ConsoleAppender log4j.appender.stdout.layout=org.apache.log4j.PatternLayout # Pattern to output the caller's file name and line number. log4j.appender.stdout.layout.ConversionPattern=%5p [%t] (%F:%L) - %m%n #RollingFileAppender log4j.appender.R=org.apache.log4j.RollingFileAppender log4j.appender.R.File=LiveRestaurant.log log4j.appender.R.MaxFileSize=100KB # Keep one backup file log4j.appender.R.MaxBackupIndex=1 log4j.appender.R.layout=org.apache.log4j.PatternLayout log4j.appender.R.layout.ConversionPattern=%p %t %c - %m%n log4j.logger.org.springframework.ws.server.MessageTracing.received=TRACE log4j.logger.org.springframework.ws.server.MessageTracing.sent=TRACE
The easiest setting for the log tracing or debugging is in log4j.properties
, as mentioned previously.
Setting up an endpoint by annotating the payload-root in Chapter 1,Building SOAP Web-Services.
Spring-WS provides features to log incoming/outgoing messages. These facilities are provided by using the PayloadLoggingInterceptor
and SoapEnvelopeLoggingInterceptor
classes that log using Commons Logging Log. While PayloadLoggingInterceptor
logs only a message's payload, SoapEnvelopeLoggingInterceptor
logs the whole SOAP Envelope including headers. To activate logging features using these two interceptors, log property within the log4j
properties file should be set to debug for interceptors package.
In this recipe, logging Web-Service messages using PayloadLoggingInterceptor
and SoapEnvelopeLoggingInterceptor
are explained.
In this recipe, the project's name is LiveRestaurant_R-5.3
(for the server-side Web-Service) and has the following Maven dependencies:
spring-ws-core-2.0.1.RELEASE.jar
log4j-1.2.9.jar
And LiveRestaurant_R-5.3-Client
(for the client side) with the following Maven dependencies:
spring-ws-core-2.0.1.RELEASE.jar
log4j-1.2.9.jar
This recipe uses projects used in the recipe Setting up an endpoint by annotating the payload-root:
Open log4j.properties
and set logging to debug the package org.springframework.ws.server.endpoint.interceptor
.
Register PayloadLoggingInterceptor
in the server-side application context.
Build and deploy the project in the Maven-embedded Tomcat server. Run mvn clean package tomcat:run
from the root of the project in a command-line window.
To test this, open a new command-line window, go to the folder LiveRestaurant_R-5.3-Client
, and run mvn clean package exec:java
.
Here is the output from the server-side console:
DEBUG [http-8080-1] (AbstractLoggingInterceptor.java:160) - Request:
<tns:placeOrderRequest xmlns:tns=".....">
<tns:order>
<tns:refNumber>9999</tns:refNumber>
<tns:customer>
......
</tns:customer>
<tns:dateSubmitted>2008-09-29T05:49:45</tns:dateSubmitted>
<tns:orderDate>2014-09-19T03:18:33</tns:orderDate>
<tns:items>
<tns:type>Snacks</tns:type>
<tns:name>Pitza</tns:name>
<tns:quantity>2</tns:quantity>
</tns:items>
</tns:order>
</tns:placeOrderRequest>
DEBUG [http-8080-1] (AbstractLoggingInterceptor.java:160) - Response:
<tns:placeOrderResponse xmlns:tns="...">
<tns:refNumber>order-John_Smith_1234</tns:refNumber></tns:placeOrderResponse>
DEBUG [http-8080-1] (AbstractLoggingInterceptor.java:160) - Request:
<tns:cancelOrderRequest xmlns:tns="...">
<tns:refNumber>9999</tns:refNumber>
</tns:cancelOrderRequest>
DEBUG [http-8080-1] (AbstractLoggingInterceptor.java:160) - Response:
<tns:cancelOrderResponse xmlns:tns="...">
<tns:cancelled>true</tns:cancelled>
</tns:cancelOrderResponse>
To log Web-Service messages using SoapEnvelopeLoggingInterceptor
, follow these steps:
Register SoapEnvelopeLoggingInterceptor
in the server-side application context.
Open log4j.properties
and set logging to debug the package org.springframework.ws.soap.server.endpoint.interceptor
.
Build and deploy the project in the Maven-embedded Tomcat server. Run mvn clean package tomcat:run
from the root of the project in a command-line window.
To test this, open a new command-line window, go to folder LiveRestaurant_R-5.3-Client
, and run mvn clean package exec:java
.
Here is the output from the server-side console:
DEBUG [http-8080-1] (AbstractLoggingInterceptor.java:160) - Request:
<SOAP-ENV:Envelope xmlns:SOAP-ENV=....">
<SOAP-ENV:Header/><SOAP-ENV:Body>
<tns:placeOrderRequest xmlns:tns="....">
<tns:order>
<tns:refNumber>9999</tns:refNumber>
<tns:customer>
.....
</tns:customer>
<tns:dateSubmitted>2008-09-29T05:49:45</tns:dateSubmitted>
<tns:orderDate>2014-09-19T03:18:33</tns:orderDate>
<tns:items>
<tns:type>Snacks</tns:type>
<tns:name>Pitza</tns:name>
<tns:quantity>2</tns:quantity>
</tns:items>
</tns:order>
</tns:placeOrderRequest>
</SOAP-ENV:Body></SOAP-ENV:Envelope>
DEBUG [http-8080-1] (AbstractLoggingInterceptor.java:160) - Response:
<SOAP-ENV:Envelope xmlns:SOAP-ENV=..."><SOAP-ENV:Header/><SOAP-ENV:Body>
<tns:placeOrderResponse xmlns:tns="...">
<tns:refNumber>order-John_Smith_1234</tns:refNumber>
</tns:placeOrderResponse>
</SOAP-ENV:Body></SOAP-ENV:Envelope>
DEBUG [http-8080-1] (AbstractLoggingInterceptor.java:160) - Request:
<SOAP-ENV:Envelope xmlns:SOAP-ENV="..."><SOAP-ENV:Header/><SOAP-ENV:Body>
<tns:cancelOrderRequest xmlns:tns="...">
<tns:refNumber>9999</tns:refNumber>
</tns:cancelOrderRequest>
</SOAP-ENV:Body></SOAP-ENV:Envelope>
DEBUG [http-8080-1] (AbstractLoggingInterceptor.java:160) - Response:
<SOAP-ENV:Envelope xmlns:SOAP-ENV="..."><SOAP-ENV:Header/><SOAP-ENV:Body>
<tns:cancelOrderResponse xmlns:tns="...a">
<tns:cancelled>true</tns:cancelled></tns:cancelOrderResponse>
</SOAP-ENV:Body></SOAP-ENV:Envelope>
MessageDispatcherServlet
calls the Interceptor (if any) when the message is received as well as before calling the handler method in the endpoint and before sending back the response to the client.
Registering PayloadLoggingInterceptor
inside spring-ws-servlet.xml
only logs the message's payload:
<sws:interceptors> <bean class="org.springframework.ws.server.endpoint.interceptor.PayloadLoggingInterceptor"/> </sws:interceptors>
Similarly, registering SoapEnvelopeLoggingInterceptor
inside spring-ws-servlet.xml
logs the whole message's SOAP Envelope:
<sws:interceptors> <bean class="org.springframework.ws.soap.server.endpoint.interceptor.SoapEnvelopeLoggingInterceptor"/> </sws:interceptors>
In both cases, the package name of these Interceptors should be set to debug for logging purpose:
........ log4j.appender.R.layout=org.apache.log4j.PatternLayout log4j.appender.R.layout.ConversionPattern=%p %t %c - %m%n log4j.logger.org.springframework.ws.soap.server.endpoint.interceptor=debug log4j.logger.org.springframework.ws.server.endpoint.interceptor=debug
Setting the logRequest
and logResponse
properties of PayloadLoggingInterceptor
to true/false, enables/disables logging for request/response messages.
<bean class="org.springframework.ws.server.endpoint.interceptor.PayloadLoggingInterceptor"> <property name="logRequest" value="false" /> <property name="logResponse" value="true" /> </bean>
In addition to logRequest
and logResponse
, there is a logFault
property for SoapEnvelopeLoggingInterceptor
that setting these to true/false, enables/disables logging for request/response/fault messages:
.... <bean class="org.springframework.ws.soap.server.endpoint.interceptor.SoapEnvelopeLoggingInterceptor"> <property name="logRequest" value="false" /> <property name="logResponse" value="true" /> <property name="logFault" value="true" ></property> </bean>
Setting up an endpoint by annotating the payload-root in Chapter 1,Building SOAP Web-Services.
Logging both request and response SOAP Envelope using Log4j
The ability to debug an application during the development phase is one of the most important features of an IDE, as it helps the developers to find out the bugs easily and hence speeds up the development. For a server-side application, which is more complex, the debug ability is more important for defect-discovery. A remote debugger attached to an IDE like Eclipse can shorten the problem analysis time significantly and make the process more enjoyable.
Eclipse can be configured for debugging within a web/app server with both embedded and remote servers. This recipe explains how to debug a Spring-WS project as a web application from inside Eclipse, with an external remote Tomcat instance.
To get started:
Install Apache-Tomcat-6.0.14.
Download and install Eclipse IDE for Java EE Developers Helios.
In this recipe, the project's name is LiveRestaurant_R-5.4
(for the server-side WebService) and has the following Maven dependencies:
spring-ws-core-2.0.1.RELEASE.jar
log4j-1.2.9.jar
It also has LiveRestaurant_R-5.4-Client
(for the client side) with the following Maven dependencies:
spring-ws-core-2.0.1.RELEASE.jar
log4j-1.2.9.jar
Modify the profile in user home (/home/weblogic)
for Linux, or in the system variable in Windows for Tomcat.
After installing Tomcat: On Linux > edit .profile>, add these lines for Tomcat:
export TOMCAT_HOME=/opt2/apache-tomcat-6.0.14 export PATH=$TOMCAT_HOME:$PATH
On Windows >edit system variable, set the system variable for Tomcat, as shown in the following screenshot:
In the $TOMCAT_HOME/conf/tomcat-users.xml
file, set the role as manager
and username
and password
as follows:
<?xml version='1.0' encoding='utf-8'?> <tomcat-users> <role rolename="manager"/> <user username="tomcat" password="tomcat" roles="manager"/> </tomcat-users>
In the MAVEN_HOME/conf/settings.xml
file and if any .m2/settings.xml
(.m2
is maven repository folder), add a user login configuration named tomcat
with the password tomcat
as follows:
<server> <id>myserver</id> <username>tomcat</username> <password>tomcat</password> </server>
Modify debug.sh/debug.bat TOMCAT_HOME/bin/
at the end of the file:
On Windows, modify debug.bat:
set JPDA_TRANSPORT=dt_socket set JPDA_ADDRESS=8000 call "%EXECUTABLE%" jpda start %CMD_LINE_ARGS%
On Linux, modify debug.sh:
export JPDA_ADDRESS=8000 export JPDA_TRANSPORT=dt_socket exec "$PRGDIR"/"$EXECUTABLE" jpda start "$@"
Run Tomcat on Linux/Windows using debug.sh/debug.bat
from TOMCAT_HOME/bin/
.
Modify the pom.xml
file of LiveRestaurant_R-5.4:
<!-- <plugin> <groupId>org.codehaus.mojo</groupId> <artifactId>tomcat-maven-plugin</artifactId> <version>1.1</version> </plugin> --> <plugin> <groupId>org.codehaus.mojo</groupId> <artifactId>tomcat-maven-plugin</artifactId> <version>1.1</version> <configuration> <server>myserver</server> <path>/LiveRestaurant</path> </configuration> </plugin>
Import the project LiveRestaurant_R-5.4
into Eclipse and set a break point in the class com.packtpub.liverestaurant.service.endpoint.OrderEndpoint.java
in the method handleCancelOrderRequest
.
Run mvn clean package
from LiveRestaurant_R-5.4
and then copy the WAR file into tomcat/webapp
(the application will be deployed into Tomcat).
In Eclipse, set Maven installation: Windows | Preferences | Maven | Installations, click on the Add button, and set external Maven:
Open Eclipse. Right-click on LiveRestaurant_R-5.4 | Debug as | Debug Configurations | Remote Java Application, click on New, and then click on the Debug button:
From the project LiveRestaurant_R-5.4-Client
, run mvn clean package
Now you can try different options for debugging the application, such as:
Step Over (F5)
Step Into (F5)
Step Out (F7)
Watch
Inspect
This recipe makes use of the Java Debugger (JDB) tool that helps find and fix bugs in the Java language programs both locally and on the server. JDB is part of the Java Platform Debugging Architecture (JPDA) that provides the infrastructure you need to build end-user debugger applications for the Java platform.
To use JDB in a Java EE application server or a servlet container, such as Tomcat, you must first launch it with debugging enabled and attach it to the server from the debugger through a JPDA port (the default port is 1044). At step 4, the JPDA port is set to 8000. Instead of the run.bat/run.sh
, this recipe starts the server using the debug.bat/debug.sh
, which means the server is started in debug mode.
The JDB parameters specify the way the debugger will operate. For instance, JPDA_TRANSPORT=dt_socket
instructs the JVM that the debugger connections will be made through a socket, while the JPDA_ADDRESS=8000
parameter informs it that the port number will be 8000.
The Eclipse IDE is then attached to a JVM that accepts debugging connections. The project is set as a Remote Java Application inside Eclipse that listens to the same port, that is, 8000, for any debugging activity. In the next step, the break point will be set inside the service class that would be managed and redirected to the IDE by the JDB at runtime.
When the LiveRestaurant_R-5.4-Client
project is executed as the client program of the service, the service class, OrderServiceEndpoint
, is invoked and the break point is hit at the JVM, which is in the debug mode. It notifies the frontend as to where the JDI is implemented and which is the IDE in this case.
Similar to the Tomcat server, you can attach any application server to any IDE such as Eclipse, Net Beans, or JDeveloper. The concepts are the same. However, the steps may vary for each application server and IDE.