Thursday, March 20, 2014

Writing JAX-WS SoapHandler

SoapHandler(SMH) is a interceptor for JAXWS webservies. It will intercept in bound and out bound messages. It is a couterpart of GenericHandler for the JAXRPC webservices. There are two types of handlers for JAXWS webservices. One is LogicalHandler and other is SoapHandler. LogicalHandler is used to write some logic on in bound or out bound messages. Lets focus now on writing SoapHandler. Lets try to do it in simple steps. 1. Write a handler by implementing interface SoapHandler. SoapHandler is a Generic interface, which takes in any class which "IS A" SOAPMessageContext. If you want your custom features in SOAPMessageContext, then implement you own. Else, use SOAPMessageContext.
 
public class MySoapHandler implements SOAPHandler{

    @Override
    public boolean handleMessage(SOAPMessageContext context) {
 // TODO Auto-generated method stub
 return false;
    }

    @Override
    public boolean handleFault(SOAPMessageContext context) {
 // TODO Auto-generated method stub
 return false;
    }

    @Override
    public void close(MessageContext context) {
 // TODO Auto-generated method stub
 
    }

    @Override
    public Set getHeaders() {
 // TODO Auto-generated method stub
 return null;
    }

}

2. Once you implement the class, you will have to implement a number of methods. Prominent method being 2.1 handleMessage(SOAPMessageContext context) - This is the method used to get the in bound or out bound messages and process it. 2.2 handleFault(SOAPMessageContext context) - Whenever there is a Fault, this message is called and you can write logic to handle the fault. 3. I will log the inbound and out bound messages in handleMessgae and let the flow continue.
 
 
     @Override
    public boolean handleMessage(SOAPMessageContext context) {
 Boolean isOutbound = (Boolean) context.get(MessageContext.MESSAGE_OUTBOUND_PROPERTY);
 if (!isOutbound) {
     System.out.println("In Bound Message ... ");
 }
 else {
     System.out.println("Out Bound Message ... ");
 }

 try {
     context.getMessage().writeTo(System.out);
 }
 catch (SOAPException e) {
     e.printStackTrace();
 }
 catch (IOException e) {
     e.printStackTrace();
 }
 return true;
    }

4. Write a handlerConfig.xml file which will contain the fully qualified class name of the handler we just created. As the name of the file indicates, it is a chain of handlers.
    <handler-chains xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemalocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/javaee_web_services_metadata_handler_2_0.xsd">  
      <handler-chain>  
           <handler>  
                <handler-name>SoapRequestValidator</handler-name>  
                <handler-class>com.example.jws.handler.MySoapHandler</handler-class>  
           </handler>  
 <!-- Can have multiple chains ... -->  
      </handler-chain>  
 </handler-chains>  
5. In the JAXWs webservice, include the handlerChain using the annotation:
@HandlerChain(file = "handlerConfig.xml")
Place the handlerConfig.xml in the same package as the webservice endpoint implementation file.
6. Finally, very important point is to include the handlerConfig.xml in the war which you build. If you don't include the handlerConfig file, then you don't get any error. But the handler will never be called. So, do remember to include the handlerConfig.xml in the war file.
Expect an example project on this shortly in my github account. Simple isn't it :)

Monday, March 3, 2014

Deploy to Tomcat 7 using Maven

Steps to deploy war file in Tomcat 7 through Maven:

1. tomcat-users.xml: This file is present at conf directory under tomcat folder (apache-tomcat-7.x/conf)

   Add role "manager-script". This is required in tomcat 7 if deployment is done through a script, like maven.
   Create a user by any name and give a password of your choice and assign this user with the "manage-script" role.
 
   Eg:

      <tomcat-users>

<role rolename="manager-script"/>
        <user username="admin" password="admin"  roles="manager-script"/>

      </tomcat-users>

For deploying to tomcat through eclipse, above roles will suffice. However, to manage through tomcat manager UI, additional roles needs to be added the use.

Note that for Tomcat 7 onwards, the roles required to use the manager application were changed from the single manager role to the following four roles. You will need to assign the role(s) required for the functionality you wish to access.

manager-gui - allows access to the HTML GUI and the status pages
manager-script - allows access to the text interface and the status pages
manager-jmx - allows access to the JMX proxy and the status pages
manager-status - allows access to the status pages only


 2. Setup the tomcat server in maven's settings.xml, under servers.
   
 <server>
<id>TomcatServer</id>
<username>admin</username>
<password>admin</password>
      </server>

 3. In the pom.xml, add the below plugin:

            <plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>tomcat-maven-plugin</artifactId>
<configuration>
   <!-- Manager path in tomcat 7 is different than other versions of tomcat. -->
<url>http://localhost:8080/manager/text</url>
<!-- This is the server ID, given in the settings.xml -->
<server>TomcatServer</server>
<path>/exampleApplication</path>
</configuration>
</plugin>

Note that tomcat 7 has a different manager path than other versions of tomcat.

  4. Use command: mvn tomcat:deploy to deploy war file and command: mvn tomcat:redeploy to redeploy the web application. 

Monday, January 20, 2014

Using same Embedded object more than once in the same entity

If more than once we have to use the same Embedded object, then we have to use @Attributeoverrides and @AttributeOverride annotations to give override the column names of the embedded objects. Otherwise, we get a error like this : "org.Hibernate.MappingException: Repeated column in mapping entry:" for all the columns of the Embedded objects that have been repeated.

For example, if Name is a Embedded object in Student Entity, then the mapping should be like this
 @Entity  
 public class Student implements Serializable {  
      @AttributeOverrides({  
                @AttributeOverride(name = "firstName", column = @Column(name = "FATHER_FIRST_NAME")),  
                @AttributeOverride(name = "surName", column = @Column(name = "FATHER_SUR_NAME")),  
                @AttributeOverride(name = "lastName", column = @Column(name = "FATHER_LAST_NAME")),  
                @AttributeOverride(name = "displayName", column = @Column(name = "FATHER_DISPLAY_NAME")) })  
      @Embedded  
      private Name father;  
      @AttributeOverrides({  
                @AttributeOverride(name = "firstName", column = @Column(name = "MOTHER_FIRST_NAME")),  
                @AttributeOverride(name = "surName", column = @Column(name = "MOTHER_SUR_NAME")),  
                @AttributeOverride(name = "lastName", column = @Column(name = "MOTHER_LAST_NAME")),  
                @AttributeOverride(name = "displayName", column = @Column(name = "MOTHER_DISPLAY_NAME")) })  
      @Embedded  
      private Name mother;  
  ...  
 }  
And Embeddable object'r mapping being:
 @Embeddable  
 public class Name implements Serializable {  
      /**  
       * Default generated Serialization ID  
       */  
      private static final long serialVersionUID = -971064645279229579L;  
      @Column(name = "FIRST_NAME")  
      private String firstName;  
      @Column(name = "SUR_NAME")  
      private String surName;  
      @Column(name = "LAST_NAME")  
      private String lastName;  
 ...   
 }  

Configuring log4j with Spring framework


  • Make sure that the commons-logging is there in the classpath of the projet. Including spring-core will automatically include commins-logging with it. I tried for spring-core version 3.1.1.RELEASE
  • First, define the log4j.xml with the desired log4j configuration for the project. Example is given below. Place the file along with log4j.dtd in the resources/config folder of the  project.

 <?xml version="1.0" encoding="UTF-8" ?>  
 <!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">  
 <log4j:configuration xmlns:log4j='http://jakarta.apache.org/log4j/'>  
      <!-- console logger -->  
      <appender name="stdout" class="org.apache.log4j.ConsoleAppender">  
           <layout class="org.apache.log4j.PatternLayout">  
                <param name="ConversionPattern"  
                     value="%-5p [%d{EEE MMM d HH:mm:ss z yyyy:}] [%t] [%X{ProcessId}] [%L] %c : %m%n" />  
           </layout>  
           <filter class="org.apache.log4j.varia.LevelRangeFilter">  
                <param name="LevelMin" value="DEBUG" />  
           </filter>  
      </appender>  
     <!-- Specify the path for the logFile -->  
      <appender name="FILE" class="org.apache.log4j.RollingFileAppender">  
           <param name="File" value="<path of the log file>" />  
           <param name="Threshold" value="DEBUG" />  
           <param name="MaxFileSize" value="10MB" />  
           <param name="MaxBackupIndex" value="10" />  
           <layout class="org.apache.log4j.PatternLayout">  
                <param name="ConversionPattern" value="%d{ISO8601}: [%t] %c, %p, %X{ProcessId}: %m%n" />  
           </layout>  
      </appender>  
      <!-- Place your application's package whose classes needs to be logger enabler at Debug level. -->  
      <logger name="<Your application package>" additivity="false">  
           <level value="DEBUG" />  
           <appender-ref ref="stdout" />  
           <appender-ref ref="FILE" />  
      </logger>  
      <logger name="org.springframework" additivity="false">  
           <level value="INFO" />  
           <appender-ref ref="stdout" />  
           <appender-ref ref="FILE" />  
      </logger>  
      <logger name="org.hibernate" additivity="false">  
           <level value="INFO" />  
           <appender-ref ref="stdout" />  
           <appender-ref ref="FILE" />  
      </logger>  
      <root>  
           <priority value="DEBUG" />  
           <appender-ref ref="stdout" />  
           <appender-ref ref="FILE" />  
      </root>  
 </log4j:configuration>  
  • In the spring configuration, define a bean of type 'org.springframework.beans.factory.config.MethodInvokingFactoryBean' like this:
 
   <bean id="log4jInitialization"  
           class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">  
           <property name="targetClass" value="org.springframework.util.Log4jConfigurer" />  
           <property name="targetMethod" value="initLogging" />  
           <property name="arguments">  
                <list>  
                     <value>classpath:config/log4j.xml</value>  
                </list>  
           </property>  
      </bean>  
          The above bean is for the class org.springframework.beans.factory.config.MethodInvokingFactoryBean, which takes in 3 arguments. First the targetClass, which in this case is Spring's Log4jConfigurer class and the second argument will be the method "initLogging" of class Log4jConfigurer, which will be called with the third arguments passed in as the third argument.
We will pass the location of the log4j.xml.

During runtime, MethodInvokingFactoryBean will invoke the method "initLogging" of class Log4jConfigurer with the arguments passes to it through property "arguments"

  • Class in which the logging should be used should have a declaration like this.   org.apache.log4j.Logger logger = org.apache.log4j.Logger.getLogger(clazz);
  • Level of logging can be set in the log4j.xml.