Friday, June 11, 2010

log4j ... Dynamic Log File Name


Background:
We developed a Bulk Loader component using core Java api's and used log4j for writing all the developer debugs, info, warn etc messages to a log file. The Bulk Loader, as the name suggests, sent bulk requests to application which enabled Load Testing. The Bulk Loader was developed in such a way that multiple instances could run on the same machine : this enabled controlling the load pushed to the application from various Bulk Loader instances.


Problem scenario:
During testing, if was found that all the Bulk Loader instances running on one machine, used the same log file for writing their messages to. This made the log file illegible. Messages from different instances were interwoven into a single log file which made debugging a very tough job. A solution was needed for each instance to create and write to its own log file.

Method 1: Modify the java code for setting the correct log file name to the FileAppender
  1. Get hold of the all Appenders  from the Logger object
  2. Loop through each and find the Appender of interest. This will typically be the FileAppender
  3. Call the setFile( String fileName ) api for setting desired log file name
  4. Call the activateOptions for Log4j framework to pick the newly set log file name
Though most of us would like and prefer to use the above method, there are two stark limitations :
  1. There can be multiple File Appenders defined in the log4j.xml. For example, my log4j.xml has two RollingFileAppenders; one with name as Process the other with Audit. In such cases, the logic of setting the log file name dynamically starts getting complex if lot of conditional checks
  2. If the source code of api responsible for initializing the Logger is not available (this could be due to the fact that you, as a part of Services team, have the ability to invoke an exposed log( String message, int level ) method and initialization and configuration of the Logger is taken care by the Product team), then you definitely cannot get hold to any of the Appenders and change the log file name.
Method 2: Modify the log4j.xml and Use -D switch
This method does not require any code changes and hence no need to recompile. This method is only related to configuration of log4j.xml and the start command of java program.
  • Open the log4j.xml and introduce a variable in front of the log file name definition. The variable name should be wrapped with special characters as follows: ${variable name}
  • Following is the snippet from my log4j.xml

         <appender name="Process" class="org.apache.log4j.DailyRollingFileAppender">

             <param name="File" value="./logs/${log_file_name}_Log.txt"/>

         <appender name="AuditAppender" class="org.apache.log4j.RollingFileAppender">

         <param name="File" value="./logs/${log_file_name}_Audit_log.txt"/>
  • Modify the start command of java application as below:

           java -Dlog_file_name=Items -cp %CLASSPATH% HelloWorld
  • This will create two log files by the name Items_Log.txt and Items_Audit.txt in the logs folder
Hope this post was helpful. Happy Logging !

Saturday, April 3, 2010

A Webservice that WORKS !!!


I faintly recollect the last webservice which I deployed on Tomcat in the year 2005. That was an achievement for me as no one in our team knew how to develop and deploy one. Four years and counting, I was facing the same challenge again. Unfortunately, I lost the reference project from year 2005 and here I was back to square one.
Searching on Google indeed returned lot of results; but; I doubt if anybody has been able to get them work. Samples from sun-java tutorials always talk about using ant scripts to compile and prepare the target war files. Seldom do we use Sun's J2EE server in our projects; most of the times the application server used is WebSphere or Weblogic or JBOSS. The next problem is to build the component for successful deployment on Weblogic or JBOSS etc.
I am being candid here. I tried a lot and hit many obstacles on the way. After breaking my head during leisure time, I managed to successfully expose and consume a webservice. The following section is my attempt to help folks who are in similar situation. I have tried to explain the following things:

Steps to develop a simple webservice
  1. Most of the webservices tutorials use simple types while passing and return from the service. Frankly speaking, a webservice accepting an int and returning a String is of no use to anyone of us. We always have complex types i.e. custom objects which are used as request (i.e. input argument to the webservice) and response (i.e. return parameter from the webservice).
  2. Deployment on JBoss 4.0.5 GA
  3. Deployment on Weblogic 9.2
  4. Test Client Standalone
  5. Test Client Weblogic
  6. Important points to remember
Hope you are successful too !

Software versions: Jboss 4.0.5 GA, Weblogic 9.2, JDK 1.5.0.*, JWSDP 2.0 (very important)

Steps:

1. Create the folder structure
The final folder from which the deployment ear/war/jar will be created is as follows:
 
 
2. 2. Create the remote interface, implementation class and the input & output valueobjects. An important point to be considered is the nomenclature of the implementation class. When using servlets to masquerade the webservice, we are inclined to put the name as XYZServlet. This works fine when deploying the webservice in Weblogic. But Jboss has a problem; if the name of the servlet class has 'Servlet' in it, then that class is ignored during webservice deployment and is treated as a valid Servlet instead.
Hence I recommend sticking to the XYZService nomenclature. XYZ will be remote interface a.k.a service end point and XYZService will be the implementation of the service.

Adjust your IDE settings such that the ouput from compilation of the below mentioned java files, results into creation of class files under webapp\web-services\WEB-INF\classes folder

Interceptor.java
=================
package org.jboss.webservices.queuing;
import java.rmi.Remote;
import java.rmi.RemoteException;
import org.jboss.webservices.valueobjects.InterceptorRequest;
import org.jboss.webservices.valueobjects.InterceptorResponse;


public interface Interceptor extends Remote {
  public InterceptorResponse addToQueue( InterceptorRequest req ) throws RemoteException;
  public String removeFromQueue( int positionNumber ) throws RemoteException;
}
InterceptorService.java
=======================
package org.jboss.webservices.queuing;
import java.rmi.RemoteException;
import org.jboss.webservices.valueobjects.InterceptorRequest;
import org.jboss.webservices.valueobjects.InterceptorResponse;
public class InterceptorService {
  public InterceptorResponse addToQueue( InterceptorRequest req ) throws RemoteException {
    System.out.println( "Interceptor request received is : " + req );


    InterceptorResponse res = new InterceptorResponse();
    String[] result = new String[ 3 ];
    result[ 0 ] = "First element";
    result[ 1 ] = "Second element";
    result[ 2 ] = "Third element";
    res.setResult( result );
    return res;
  }
  public String removeFromQueue( int positionNumber ) throws RemoteException {
    String retString = "Removed element at position " + positionNumber;
    System.out.println( retString );
    return retString;
  }
}
InterceptorRequest.java
=======================
package org.jboss.webservices.valueobjects;
import java.io.Serializable;
public class InterceptorRequest implements Serializable {
  private String _soapMsg = null;
  private int _demuxId = -1;
  private long _entryTimeInMillis = 0;
  private String[] _tcgsUrls = null;


  public String getSoapMsg() {
    return _soapMsg;
  }
  public void setSoapMsg(String soapMsg) {
    this._soapMsg = soapMsg;
  }
  public int getDemuxId() {
    return _demuxId;
  }
  public void setDemuxId(int demuxId) {
    this._demuxId = demuxId;
  }


and so on ... create getter and setter for each property
}
InterceptorResponse.java
========================
package org.jboss.webservices.valueobjects;


import java.io.Serializable;
public class InterceptorResponse implements Serializable {


  private String[] _result = null;
  public String[] getResult() {
    return _result;
  }
  public void setResult(String[] result) {
    this._result = result;
  }
}
3. Masquerade the webservice as a servlet: For this, we will add a web.xml with entries similar to the ones required while defining the servlet. The web.xml will reside at the following location : webapp\web-services\WEB-INF
web.xml
=======
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"
version="2.4">


<servlet>
<servlet-name>InterceptorService</servlet-name>
<servlet-class>org.jboss.webservices.queuing.InterceptorService</servlet-class>
</servlet>


<servlet-mapping>
<servlet-name>InterceptorService</servlet-name>
<url-pattern>/Intercept</url-pattern>
</servlet-mapping>


</web-app>

Important points while developing the web xml:
  1. The servlet name should match in the servlet and servlet-mapping tag.
  2. The servlet class holds the fully qualified class name of the service implementation class.
  3. The url-pattern should be unique and not used anywhere else in the web xml.
4. Build the configuration descriptor for webservice. Place this in a file called as interceptor_config.xml.
<?xml version="1.0" encoding="UTF-8"?>
<configuration xmlns="http://java.sun.com/xml/ns/jax-rpc/ri/config">
<service name="InterceptorService"
targetNamespace="http://queuing.webservice.jboss.org/"
typeNamespace="http://queuing.webservice.jboss.org/types"
packageName="org.jboss.webservices.queuing">
<interface name="org.jboss.webservices.queuing.Interceptor"/>
</service>
</configuration>

Important points while developing the configuration descriptor:

The interface name is fully qualified class name of the service endpoint i.e. the remote interface
As a rule of thumb, I put the target namespace and type namespace in reverse order of the pakcage name of the service endpoint

5. Build the service using wscompile.bat file available in the jwsdp2-0\jaxrpc\bin folder. The command is as below:

wscompile -verbose -classpath webapp/web-services/WEB-INF/classes -gen:server -f:rpcliteral -f:unwrap -d webapp/web-services/WEB-INF/classes -nd webapp/web-services/WEB-INF/wsdl -mapping webapp/web-services/WEB-INF/interceptor_mapping.xml interceptor_config.xml

The wscompile command line arguments explanation is :

-verbose : gives the detailed console output of wscompile
-classpath : represents the folder holding classes from which the webservice needs to be generated
-d : represents the destination where the service classes need to be generated
-nd : represents the destination where all the non class need to be generated
-mapping : represents the destination where the jaxrpc mapping xml needs to be generated.

On successful execution of wscompile, you should find Interceptor Stub, TIE, Literal Serializer and Deserializer class files generated in the folder given against the -d operator. You should also have an InterceptorService.wsdl file generated in the wsdl folder under WEB-INF. Given below are the classes generated on my machine along with the wsdl and jaxrpc-mapping file.

classes generated by javac
==========================
Interceptor.class
InterceptorService.class

classes generated by wscompile
==========================
InterceptorService_SerializerRegistry.class
Interceptor_addToQueue_RequestStruct.class
Interceptor_addToQueue_RequestStruct_LiteralSerializer.class
Interceptor_addToQueue_ResponseStruct.class
Interceptor_addToQueue_ResponseStruct_LiteralSerializer.class
Interceptor_removeFromQueue_RequestStruct.class
Interceptor_removeFromQueue_RequestStruct_LiteralSerializer.class
Interceptor_removeFromQueue_ResponseStruct.class
Interceptor_removeFromQueue_ResponseStruct_LiteralSerializer.class
Interceptor_Tie.class

WSDL
====
<?xml version="1.0" encoding="UTF-8"?>
<definitions name="InterceptorService" targetNamespace="http://queuing.webservice.jboss.org/" xmlns:tns="http://queuing.webservice.jboss.org/" xmlns="http://schemas.xmlsoap.org/wsdl/" xmlns:ns2="http://queuing.webservice.jboss.org/types" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/">
<types>
<schema targetNamespace="http://queuing.webservice.jboss.org/types" xmlns:tns="http://queuing.webservice.jboss.org/types" xmlns:soap11-enc="http://schemas.xmlsoap.org/soap/encoding/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" xmlns="http://www.w3.org/2001/XMLSchema">
<complexType name="InterceptorRequest">
<sequence>
<element name="demuxId" type="int"/>
<element name="entryTimeInMillis" type="long"/>
<element name="soapMsg" type="string" nillable="true"/>
<element name="tcgsUrls" type="string" nillable="true" minOccurs="0" maxOccurs="unbounded"/>
</sequence>
</complexType>
<complexType name="InterceptorResponse">
<sequence>
<element name="result" type="string" nillable="true" minOccurs="0" maxOccurs="unbounded"/>
</sequence>
</complexType>
</schema>
</types>
<message name="Interceptor_addToQueue">
<part name="InterceptorRequest_1" type="ns2:InterceptorRequest"/>
</message>
<message name="Interceptor_addToQueueResponse">
<part name="result" type="ns2:InterceptorResponse"/>
</message>
<message name="Interceptor_removeFromQueue">
<part name="int_1" type="xsd:int"/>
</message>
<message name="Interceptor_removeFromQueueResponse">
<part name="result" type="xsd:string"/>
</message>
<portType name="Interceptor">
<operation name="addToQueue" parameterOrder="InterceptorRequest_1">
<input message="tns:Interceptor_addToQueue"/>
<output message="tns:Interceptor_addToQueueResponse"/></operation>
<operation name="removeFromQueue" parameterOrder="int_1">
<input message="tns:Interceptor_removeFromQueue"/>
<output message="tns:Interceptor_removeFromQueueResponse"/>
</operation>
</portType>
<binding name="InterceptorBinding" type="tns:Interceptor">
<soap:binding transport="http://schemas.xmlsoap.org/soap/http" style="rpc"/>
<operation name="addToQueue">
<soap:operation soapAction=""/>
<input>
<soap:body use="literal" namespace="http://queuing.webservice.jboss.org/"/></input>
<output>
<soap:body use="literal" namespace="http://queuing.webservice.jboss.org/"/></output></operation>
<operation name="removeFromQueue">
<soap:operation soapAction=""/>
<input>
<soap:body use="literal" namespace="http://queuing.webservice.jboss.org/"/>
</input>
<output>
<soap:body use="literal" namespace="http://queuing.webservice.jboss.org/"/>
</output>
</operation>
</binding>
<service name="InterceptorService">
<port name="InterceptorPort" binding="tns:InterceptorBinding">
<soap:address location="REPLACE_WITH_ACTUAL_URL"/>
</port>
</service>
</definitions>


interceptor_mapping.xml i.e. jaxrpc mapping
===========================================
<?xml version="1.0" encoding="UTF-8"?>
<java-wsdl-mapping xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.1" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://www.ibm.com/webservices/xsd/j2ee_jaxrpc_mapping_1_1.xsd">
<package-mapping>
<package-type>org.jboss.webservices.queuing</package-type>
<namespaceURI>http://queuing.webservice.jboss.org/types</namespaceURI>
</package-mapping>
<package-mapping>
<package-type>org.jboss.webservices.queuing</package-type>
<namespaceURI>http://queuing.webservice.jboss.org/</namespaceURI>
</package-mapping>
<java-xml-type-mapping>
<java-type>org.jboss.webservices.valueobjects.InterceptorResponse</java-type>
<root-type-qname xmlns:typeNS="http://queuing.webservice.jboss.org/types">typeNS:InterceptorResponse</root-type-qname>
<qname-scope>complexType</qname-scope>
<variable-mapping>
<java-variable-name>result</java-variable-name>
<xml-element-name>result</xml-element-name>
</variable-mapping>
</java-xml-type-mapping>
<java-xml-type-mapping>
<java-type>org.jboss.webservices.valueobjects.InterceptorRequest</java-type>
<root-type-qname xmlns:typeNS="http://queuing.webservice.jboss.org/types">typeNS:InterceptorRequest</root-type-qname>
<qname-scope>complexType</qname-scope>
<variable-mapping>
<java-variable-name>demuxId</java-variable-name>
<xml-element-name>demuxId</xml-element-name>
</variable-mapping>
<variable-mapping>
<java-variable-name>entryTimeInMillis</java-variable-name>
<xml-element-name>entryTimeInMillis</xml-element-name>
</variable-mapping>
<variable-mapping>
<java-variable-name>soapMsg</java-variable-name>
<xml-element-name>soapMsg</xml-element-name>
</variable-mapping>
<variable-mapping>
<java-variable-name>tcgsUrls</java-variable-name>
<xml-element-name>tcgsUrls</xml-element-name>
</variable-mapping>
</java-xml-type-mapping>
<service-interface-mapping>
<service-interface>org.jboss.webservices.queuing.InterceptorService</service-interface>
<wsdl-service-name xmlns:serviceNS="http://queuing.webservice.jboss.org/">serviceNS:InterceptorService</wsdl-service-name>
<port-mapping>
<port-name>InterceptorPort</port-name>
<java-port-name>InterceptorPort</java-port-name>
</port-mapping>
</service-interface-mapping>
<service-endpoint-interface-mapping>
<service-endpoint-interface>org.jboss.webservices.queuing.Interceptor</service-endpoint-interface>
<wsdl-port-type xmlns:portTypeNS="http://queuing.webservice.jboss.org/">portTypeNS:Interceptor</wsdl-port-type>
<wsdl-binding xmlns:bindingNS="http://queuing.webservice.jboss.org/">bindingNS:InterceptorBinding</wsdl-binding>
<service-endpoint-method-mapping>
<java-method-name>addToQueue</java-method-name>
<wsdl-operation>addToQueue</wsdl-operation>
<method-param-parts-mapping>
<param-position>0</param-position>
<param-type>org.jboss.webservices.valueobjects.InterceptorRequest</param-type>
<wsdl-message-mapping>
<wsdl-message xmlns:wsdlMsgNS="http://queuing.webservice.jboss.org/">wsdlMsgNS:Interceptor_addToQueue</wsdl-message>
<wsdl-message-part-name>InterceptorRequest_1</wsdl-message-part-name>
<parameter-mode>IN</parameter-mode>
</wsdl-message-mapping>
</method-param-parts-mapping>
<wsdl-return-value-mapping>
<method-return-value>org.jboss.webservices.valueobjects.InterceptorResponse</method-return-value>
<wsdl-message xmlns:wsdlMsgNS="http://queuing.webservice.jboss.org/">wsdlMsgNS:Interceptor_addToQueueResponse</wsdl-message>
<wsdl-message-part-name>result</wsdl-message-part-name>
</wsdl-return-value-mapping>
</service-endpoint-method-mapping>
<service-endpoint-method-mapping>
<java-method-name>removeFromQueue</java-method-name>
<wsdl-operation>removeFromQueue</wsdl-operation>
<method-param-parts-mapping>
<param-position>0</param-position>
<param-type>int</param-type>
<wsdl-message-mapping>
<wsdl-message xmlns:wsdlMsgNS="http://queuing.webservice.jboss.org/">wsdlMsgNS:Interceptor_removeFromQueue</wsdl-message>
<wsdl-message-part-name>int_1</wsdl-message-part-name>
<parameter-mode>IN</parameter-mode>
</wsdl-message-mapping>
</method-param-parts-mapping>
<wsdl-return-value-mapping>
<method-return-value>java.lang.String</method-return-value>
<wsdl-message xmlns:wsdlMsgNS="http://queuing.webservice.jboss.org/">wsdlMsgNS:Interceptor_removeFromQueueResponse</wsdl-message>
<wsdl-message-part-name>result</wsdl-message-part-name>
</wsdl-return-value-mapping>
</service-endpoint-method-mapping>
</service-endpoint-interface-mapping>
</java-wsdl-mapping>

6. Link the webservice and servlet through the webservice.xml. Place the webservices.xml under webapp\web-services\WEB-INF
webservices.xml
===============
<webservices xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://www.ibm.com/webservices/xsd/j2ee_web_services_1_1.xsd" version="1.1">
<webservice-description>
<webservice-description-name>InterceptorService</webservice-description-name>
<wsdl-file>WEB-INF/wsdl/InterceptorService.wsdl</wsdl-file>
<jaxrpc-mapping-file>WEB-INF/interceptor_mapping.xml</jaxrpc-mapping-file>
<port-component>
<port-component-name xmlns:sns="http://queuing.webservice.jboss.org/">sns:Interceptor</port-component-name>
<wsdl-port xmlns:sns="http://queuing.webservice.jboss.org/">sns:InterceptorPort</wsdl-port>
<service-endpoint-interface>org.jboss.webservices.queuing.Interceptor</service-endpoint-interface>
<service-impl-bean>
<servlet-link>InterceptorService</servlet-link>
</service-impl-bean>
</port-component>
</webservice-description>
</webservices>

Important points:
  1. The webservice description name and the servlet name should match.
  2. The wsdl file name along with relative path from WEB-INF folder should be crrect
  3. The jaxrpc mapping file should point to the interceptor_mapping xml file
  4. The port component name should match the value of the portType tag's name attribute in the wsdl file
  5. The wsdl port should match the value of the port tags name attribute in the wsdl file
  6. The service endpont interface holds the fully qualified name of our remote interface
  7. The service impl bean holds a link to the servlet name.

7. The finalstructure of the webapp folder:
8. Open command prompt. Navigate inside the webapp/web-services folder. Create a war file of all contents in the web-services folder. Command for creating the war file is :
jar -cvf web-services.war *
This creates a web-services.war file. Copy the web-services war file in the %JBOSS_HOME%\server\default\deploy folder. Start jboss and check the console. You should find the following lines:


Navigate to the %JBOSS_HOME%\server\default\data\wsdl folder. You should see a web-services.war folder in there. Inside this folder, you should find the InterceptorService.wsdl file. This means that the webservice is successfully deployed. In order to consume the webservice, a manual change in the deployed wsdl is necessary. This is shortcoming of JBOSS 4.0.5 GA. Open the InterceptorService.wsdl file and go to the end where the service is defined as follows:

<service name="InterceptorService">
<port name="InterceptorPort" binding="tns:InterceptorBinding">
<soap:address location="http://pni3p103:8080/web-services/Intercept"/>
</port>
</service>

Change the port number in the URL of the location attribute of the address tag. The port number is defaulted to 8080 by JBOSS. Change this to the appropriate port number which JBOSS is listening too. In my case, I have changed it to 7001. In case the editor flags a "sharing or access violation" while saving the modified wsdl; don't panic. This means that the app server has successfully loaded the wsdl and you need not explicitly change the ports. My Jboss allowed me to change the wsdl most of the times; but I did observe this file being locked by the appserver which did not allow modification to the port number. But I was able to access the wsdl successfully though !

<service name="InterceptorService">
<port name="InterceptorPort" binding="tns:InterceptorBinding">
<soap:address location="http://pni3p103:7001/web-services/Intercept"/>
</port>
</service>

Try accessing the url by suffixing ?WSDL to it : http://pni3p103:7001/web-services/Intercept?WSDL This should render the wsdl on your internet browser. Your webservice is successfully deployed !

9. Deploy in Weblogic:
Weblogic gives us ability to deploy the folder instead of packaging it into a war file; we will use this feature. I recommend making a copy of the webapp folder so that weblogic files do not meddle with jboss files. Create a folder Weblogic and copy the webapp folder under this. Start your Weblogic server; remember to shutdown JBoss in case you have both on a single machine. I typically make all the app servers listen to the same port due to which I can have only one instance running at a time. Once the Weblogic server has started, log into the administration console. The url is http://machine-name:port-number/console Login to the application. Click on Lock and Edit button --> click on Deployments --> click on Install button. Browse to the Weblogic\webapps\web-services. Select web-services folder and deploy as an application by clicking Next and selecting all others as defaults. Finally click on the Activate changes green button. Restart Weblogic instance and login to the administration console again.

In the Deployments, you should see web-services application in the Active state. If it is in prepared state, select the web-service application and click on Start --> Start servicing all requests. Click on the plus sign beside the web-service application and it will display the InterceptorService. Now there are couple of additional steps required by Weblogic. Click on the InterceptorService --> Click on the Configuration tab --> Select View Dynamic WSL Enabled and click Save. This will bring up a Deployment Plan Assistance. Select WEB-INF radio button and click Finish. Activate the Changes and restart Weblogic instance. Try to access the URL for the WSDL. If your Jboss and Weblogic listen the same port, the URL will remain same.
10. Test from standalone client

Create a separate java project. Create an interceptor_client_config.xml file with the following entries
intercept_client_config.xml
===========================
<?xml version="1.0" encoding="UTF-8"?>
<configuration
xmlns="http://java.sun.com/xml/ns/jax-rpc/ri/config">
<wsdl location="http://localhost:7001/web-services/Intercept?WSDL"
packageName="org.jboss.webservices.queuing"/>
</configuration>

Build client stubs using the wscompile. The command is:


wscompile -verbose -d . -gen:client intercept_client_config.xml



Create a WebservicesClient.java. Put the following code in it.
import java.net.URL;
import javax.xml.namespace.QName;
import javax.xml.rpc.Service;
import javax.xml.rpc.ServiceFactory;
import org.jboss.webservices.hello.Hello;
import org.jboss.webservices.hello.SayHello;
import org.jboss.webservices.queuing.Interceptor;
import org.jboss.webservices.queuing.InterceptorRequest;
import org.jboss.webservices.queuing.InterceptorResponse;


public class WebservicesClient
{
  public static void main(String[] args) throws Exception {
    addToQueue();
    removeFromQueue();
}


private static void removeFromQueue() throws Exception {
String urlstr = "http://localhost:7001/web-services/Intercept?WSDL";
System.out.println("Contacting webservice at " + urlstr);
URL url = new URL(urlstr);
QName qname = new QName("http://queuing.webservice.jboss.org/", "InterceptorService");
ServiceFactory factory = ServiceFactory.newInstance();
Service service = factory.createService(url, qname);
Interceptor intercept = ( Interceptor ) service.getPort( Interceptor.class );
System.out.println("Interceptor.removeFromQueue( 0 )");
Object o = intercept.removeFromQueue( 0 );
System.out.println( "Class ::>> " + o.getClass() );
System.out.println( "output : " + o );
}


private static void addToQueue() throws Exception {
String urlstr = "http://localhost:7001/web-services/Intercept?WSDL";
System.out.println("Contacting webservice at " + urlstr);
URL url = new URL(urlstr);
QName qname = new QName("http://queuing.webservice.jboss.org/", "InterceptorService");
ServiceFactory factory = ServiceFactory.newInstance();
Service service = factory.createService(url, qname);
Interceptor intercept = ( Interceptor ) service.getPort( Interceptor.class );
InterceptorRequest req = new InterceptorRequest();
req.setDemuxId( 1 );
req.setEntryTimeInMillis( 1000L );
req.setSoapMsg( "ameybhide soap msg" );
req.setTcgsUrls( new String[] { "a", "m", "e", "y" } );
System.out.println("Interceptor.addToQueue( " + req + " )");
Object o = intercept.addToQueue( req );
System.out.println( "Class ::>> " + o.getClass() );
InterceptorResponse res = ( InterceptorResponse )o;
System.out.println( "output ::>> [InterceptorResponse]: " );
if( res.getResult() != null && res.getResult().length > 0 ) {
System.out.println( "Elements ::>> " );
for ( int counter = 0; counter < res.getResult().length; counter++ ) {
System.out.println( res.getResult()[ counter ] );
}
} else {


System.out.println( "Empty" );
}
}
}

You will need the following jar files on your classpath before you run the above class file
- the stub classes generated as a result of the wscompile command
- jwsdp2-0/jaxrpc/lib/jaxrpc-api.jar
- jwsdp2-0/jaxrpc/lib/jaxrpc-impl.jar
- jwsdp2-0/jaxrpc/lib/jaxrpc-spi.jar
- jboss-4.0.5.GA/client/activation.jar
- jboss-4.0.5.GA/client/mail.jar
- jboss-4.0.5.GA/client/namespace.jar
- jwsdp2-0/saaj/lib/saaj-api.jar
- jwsdp2-0/saaj/lib/saaj-impl.jar
- jwsdp2-0/fastinfoset/lib/FastInfoset.jar
- jwsdp2-0/sjsxp/lib/jsr173_api.jar

Run the WebservicesClient. You should see the following output:
Contacting webservice at http://localhost:7001/web-services/Intercept?WSDL
Interceptor.addToQueue( org.jboss.webservices.queuing.InterceptorRequest@273686 )
Class ::>> class org.jboss.webservices.queuing.InterceptorResponse
output ::>> [InterceptorResponse]:

Elements ::>>
First element
Second element
Third element
Contacting webservice at http://localhost:7001/web-services/Intercept?WSDL
Interceptor.removeFromQueue( 0 )
Class ::>> class java.lang.String
output : Removed element at position 0
 
All the Best !!!

Friday, March 12, 2010

Remote Debugging with Eclipse


My eyes were glued to the monitor, fingers doing a tap dance on the F6 key periodically using F5 and F8 too. A colleague walked over to my cubicle and asked me what I was doing. After giving him a I-didn't-want-to-be-disturbed look, I told him I was debugging a server side Java code. He was surprized. He knew how to debug a stand alone Java application using Eclipse. It was as simple as running the application through the Debug mode, introducing a breakpoint and changing the perspective of Eclipse to Debug. He was not aware that we could do the same on a code getting executed inside an application server.

Simple ... I told him. The only change required is to setup a Remote Java application in Eclipse and modify the start up scripts of your application sever. Piece of cake !

We use Websphere, Weblogic and JBoss applicaiton servers the most in our projects. I have listed down steps for configuring Eclipse and these application servers for debugging your J2EE components. Hope it saves your time spent in restart, recompile, redeploy of J2EE components. Finding a problem is very easy when you have the power of debugger !

Weblogic 9.2:
  • Open the startWebLogic.cmd file present in the bin folder withing your user domain.
  • Search for text -Xrunjdwp in this cmd file. If you find one, stick to modifying it. If you don't find one, then you will need to add the following line:
         set JAVA_OPTIONS=%JAVA_OPTIONS% -Xdebug -Xrunjdwp:transport=dt_socket,address=1044,server=y,suspend=n      
  • Some advice to set the debug parameters in the DEBUG_OPTS variable. But I prefer sticking to the JAVA_OPTIONS as these are definitely put on the command for launching the weblogic application server.
  • The address=1044 tells the application server to connect the debugger over 1044 port. In case the port is in user, change it to some other port like 7002 or 9001  whichever is free.
  • The suspend=n tells the application server to continue with the startup and execution even if no debugger is attached
  • If the value of suspend attribute is changed to y i.e. suspend=y, then the application server's JVM starts in suspended mode and stays suspended till some debugger gets attached to it.
  • Start Weblogic. You should see the text highlighted in the image above on your console too.
JBoss 4.0.5 GA :
  • Open the run.bat file present in the bin directory of JBoss installation
  • Look for the following text :
         rem JPDA options. Uncomment and modify as appropriate to enable remote debugging.

         rem set JAVA_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,address=8787,server=y,suspend=y %JAVA_OPTS%
  • If you find it, then uncomment the set JAVA_OPTS by removing the rem keyword.
  • If you do not find the text, add the following text :
         set JAVA_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,address=1044,server=y,suspend=y %JAVA_OPTS%
  • The address=1044 tells the application server to connect the debugger over 1044 port. In case the port is in user, change it to some other port like 7002 or 9001 whichever is free.
  • The suspend=n tells the application server to continue with the startup and execution even if no debugger is attached.
  • If the value of suspend attribute is changed to y i.e. suspend=y, then the application server's JVM starts in suspended mode and stays suspended till some debugger gets attached to it

  • Start JBoss. You should see the text as shown in the image

Websphere 6.1.0.13:
  • Open the admin console of Websphere. This is generally https://machine-name:9043/ibm/console
  • Login using correct credentials
  • On the left navigation pane, open Servers --> Application Servers. On the right pane, click on your server name that needs to be started up in debug mode.
  • This takes you to the Configuration tab. Scroll to the botton to find a section for Additional Properties. Click on the Debugging Services link.
  • Change the JVM debug port to 1044.
  • Append the following text to the JVM debug arguments:
         -Xdebug -Xnoagent -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=1044
  • The address=1044 tells the application server to connect the debugger over 1044 port. In case the port is in user, change it to some other port like 7002 or 9001 whichever is free
  • The suspend=n tells the application server to continue with the startup and execution even if no debugger is attached.
  • If the value of suspend attribute is changed to y i.e. suspend=y, then the application server's JVM starts in suspended mode and stays suspended till some debugger gets attached to it.
  • Click on OK button.
  • Restart the Websphere instance


Eclipse 3.3.0 :
  • Start one of the application server (Weblogic/Websphere/JBOSS)
  • Launch eclipse. Change to the Debug perspective. Open the Debug Dialog.
  • Select the Remote Java Application and Click on New Launch configuration (the button on the top left)
  • Give suitable name for this debug app ( I have given it same as application server name i.e. JBOSS)
  • Select the project which you want to debug. This could be your EJB, Webservice, Servlet etc
  • Give the hostname as the machine name where the application server is running. The port number should match the value of the address attribute set in the start up script off application server (we have provided the address in the above sections)

  • Click on apply. Then click on Debug. You should see multiple Java threads listed in the Debug perspective.


Now you can insert break points in the project and debug the J2EE components deployed in application server.

Happy debugging !