Sunday, December 10, 2006

Simple Webservice on JBoss using Axis

Download the latest stable version of Axis from Axis Releases

1) Deploying Axis web application on JBoss:

I used Axis 1.4 on JBoss-4.0.4 GCA. Assuming you downloaded Axis to D: , copy the "axis" folder present in D:\Axis-1.4\webapps folder to %JBOSS_HOME%\server\default\deploy folder. The "axis" folder that you copied is nothing but an web application (containing of some servlets and jsps provided by Axis). In JBoss for a web application to be deployed, it has to be named *.war, so rename the "axis" folder in %JBOSS_HOME%\server\default\deploy to axis.war. At this point, the axis web application is ready to be deployed on JBoss. But before starting JBoss, delete the commons-logging-x.x.x.jar and log4j-x.x.x.jar present in %JBOSS_HOME%\server\default\deploy\axis.war\WEB-INF\lib directory, since JBoss maintains its own versions of these jars and packaging these jars in your application will throw exceptions of JBoss startup. After you have deleted these jars, start the JBoss server using the run.bat present in %JBOSS_HOME%\bin folder.


2) Verify Axis web application is deployed and running:

After JBoss has started successfully, go to the following URL to check whether axis is deployed properly:

http://localhost:8080/axis

If Axis was deployed successfully, you will see a welcome page which will have a few links, one of them named "List" which can be used to view the already deployed webservices. Axis by default comes with a few sample webservices which you can see on clicking the "List" link.


3) Writing our own webservice:

3a - The Java classes/interfaces:

Lets create a simple webservice which will echo a message to the user who passes his name. We will have an interface named HelloWorldService:


package org.myapp.service;

public interface HelloWorldService {

public String sayHelloTo(String userName);

}


And here's the implementing class:


package org.myapp.service;

public class HelloWorldServiceImpl implements HelloWorldService {

public String sayHelloTo(String userName) {

//message
String hello = "Hello " + userName + ". You are being watched";

System.out.println(hello);

//return the message
return hello;
}

}



Do you see anything special in this class or the interface? No, you wont. So how do you convert these into webservices? Read on...


3b - Create a wsdl file:

Now that we have the interface and the class ready, lets create a wsdl file out of these java classes. Axis comes with utilities which allow you to create the wsdl file from a java file. I wrote a small ant target which will do this for me:


<!-- Sets classpath for build -->
<path id="classpath">
<pathelement path="${java.class.path}" />
<fileset dir="${lib}">
<include name="**/*.jar" />
</fileset>
<pathelement path="${classes.dir}" />
</path>

<taskdef name="axis-java2wsdl" classname="org.apache.axis.tools.ant.wsdl.Java2WsdlAntTask" >
<classpath refid="classpath"/>
</taskdef>

<target name="generateWSDL" description="Generates wsdl files from the java service interfaces">
<mkdir dir="${wsdl.dir}"/>
<axis-java2wsdl classpathref="classpath"
output="${wsdl.dir}/HelloWorld.wsdl"
location="http://localhost:8080/axis/services/HelloWorld"
namespace="http://jaikiran.com"
classname="org.myapp.service.HelloWorldService">

<mapping namespace="http://jaikiran.com" package="org.myapp.service"/>

</axis-java2wsdl>
</target>


Note: You can find the entire build.xml, that i used, at the end of this article.

You will require the org.apache.axis.tools.ant.wsdl.Java2WsdlAntTask class which is provided by axis to be in the classpath of Ant. This class comes bundled in the axis-ant.jar which i have included in the classpath of this task using the classpathref.

In this target you specify parameters like:
location - this is where the webservice will be deployed.
namespace - the namespace for your webservice
classname - the fully qualified name of the interface which you wrote in step 3a, above.
and also a mapping between the webservice namespace and your application packages.

This target will generate the wsdl file named HelloWorld.wsdl in the directory which you specified in the 'output' parameter of the target.

3c - Create the deploy.wsdd, undeploy.wsdd and the stubs:

Now that you have created a wsdl for your webservice, lets go ahead and create the deploy.wsdd (used for deploying a webservice on the server), undeploy.wsdd (used for undeploying a webservice from the server) and the required stubs for invoking the webservice from a java client.
Again, Axis provides utilities for creating these files. Axis accepts the wsdl file, which we created in step 3b as an input to create these files. Here's the Ant target which does this for us:


<taskdef name="axis-wsdl2java" classname="org.apache.axis.tools.ant.wsdl.Wsdl2javaAntTask" >
<classpath refid="classpath"/>
</taskdef>

<target name="generateWSDD" description="Generates wsdd files from the wsdl files">
<mkdir dir="${wsdd.dir}"/>
<axis-wsdl2java
output="${wsdd.dir}"
deployscope="Application"
serverside="true"
url="${wsdl.dir}\HelloWorld.wsdl">
</axis-wsdl2java>
</target>


This single target creates the deploy.wsdd, undeploy.wsdd and the stubs. As already mentioned, this target takes the wsdl file as an input which we have specified in the 'url' parameter of the axis-wsdl2java target. The files will be created in the directory mentioned in the 'output' parameter of this target. The deploy.wsdd file that gets generated will contain something like:


<!-- Use this file to deploy some handlers/chains and services -->
<!-- Two ways to do this: -->
<!-- java org.apache.axis.client.AdminClient deploy.wsdd -->
<!-- after the axis server is running -->
<!-- or -->
<!-- java org.apache.axis.utils.Admin client|server deploy.wsdd -->
<!-- from the same directory that the Axis engine runs -->

<deployment
xmlns="http://xml.apache.org/axis/wsdd/"
xmlns:java="http://xml.apache.org/axis/wsdd/providers/java">

<!-- Services from HelloWorldServiceService WSDL service -->

<service name="HelloWorld" provider="java:RPC" style="rpc" use="encoded">
<parameter name="wsdlTargetNamespace" value="http://jaikiran.com"/>
<parameter name="wsdlServiceElement" value="HelloWorldServiceService"/>
<parameter name="wsdlServicePort" value="HelloWorld"/>
<parameter name="className" value="com.jaikiran.HelloWorldSoapBindingImpl"/>
<parameter name="wsdlPortType" value="HelloWorldService"/>
<parameter name="typeMappingVersion" value="1.2"/>
<operation name="sayHelloTo" qname="operNS:sayHelloTo" xmlns:operNS="http://jaikiran.com" returnQName="sayHelloToReturn" returnType="rtns:string" xmlns:rtns="http://www.w3.org/2001/XMLSchema" soapAction="" >
<parameter qname="in0" type="tns:string" xmlns:tns="http://www.w3.org/2001/XMLSchema"/>
</operation>
<parameter name="allowedMethods" value="sayHelloTo"/>
<parameter name="scope" value="Application"/>

</service>
</deployment>


You might notice that during all these steps we never mentioned our implementing class (HelloWorldServiceImpl) in any of the targets. As you can see above in the deploy.wsdd, Axis has created its own implementing class for the HelloWorldService and provided default implementations in that class. In the deploy.wsdd file, it mentioned the implementation class as follows:

<parameter name="className" value="com.jaikiran.HelloWorldSoapBindingImpl"/>

The reason why Axis created this implementing class is that it never knew that we had created our own implementation. The input that we provided to Axis for generating the wsdd files was a wsdl file through which Axis could never have known that we already have a implementation class.

Our next step would be to modify the deploy.wsdd file to mention our implementation class, in place of Axis'. So let's change the 'className' parameter to:

<parameter name="className" value="org.myapp.service.HelloWorldServiceImpl"/>


3d - Deploy the webservice onto the server:

Now we are almost ready to deploy the webservice onto the server. But before doing that, we will have to make the HelloWorldService.class and HelloWorldServiceImpl.class available in the server's classpath so that when we use this webservice, the server wont throw ClassNotFoundException. So let's compile these 2 classes and place the class files in the %JBOSS_HOME%\server\default\deploy\axis.war\WEB-INF\classes folder. Here's the Ant target that i used to compile these classes:


<target name="compile" description="Compiles java source code">
<mkdir dir="${classes.dir}"/>
<javac destdir="${classes.dir}" classpathref="classpath" fork="true" srcdir="${compile.src}" excludes="org/myapp/service/client/**/*.java"/>
</target>


Once this is done, we are ready to deploy the webservice onto the server. Axis again provides a utility to do this. Here's the target for deploying the service onto the server. This target just invokes the org.apache.axis.client.AdminClient java class passing it the deploy.wsdd file as an argument.


<target name="deployWebservice" description="Deploys the webservice onto the server, using the wsdd file and Axis' AdminClient">
<java classname="org.apache.axis.client.AdminClient" classpathref="classpath" fork="true">
<arg value="${wsdd.dir}/com/jaikiran/deploy.wsdd"/>
</java>
</target>


Now that we have deployed the webservice, lets verify whether its deployed successfully. Let's again go to http://localhost:8080/axis . Here again lets click the "List" link to see all the available webservices. This time you will notice that our HelloWorld service is also listed over here. You can view the wsdl of this webservice, by clicking the wsdl link next to it. Our webservice has been deployed at http://localhost:8080/axis/services/HelloWorld as you can see from the wsdl contents. So let's hit this url. You will see a message like:

HelloWorld

Hi there, this is an AXIS service!
Perhaps there will be a form for invoking the service here...


4) Java client for accessing the webservice that we created:

Let's now create a simple java client which accesses the webservice that we just deployed on the server.
You might remember that in step 3c, we even created the stubs required to access the webservice. We will use these stubs in our java client to access the webservice. Here's the simple java client:


package org.myapp.service.client;

import com.jaikiran.HelloWorldService;
import com.jaikiran.HelloWorldServiceServiceLocator;

public class ServiceClient {

/**
* @param args
*/
public static void main(String[] args) {

try {

//Get hold of the webservice using the locator provided by Axis
HelloWorldService helloWorldService = new HelloWorldServiceServiceLocator().getHelloWorld();

//Invoke the method on the webservice
String message = helloWorldService.sayHelloTo("someUserName");

System.out.println("Client received: " + message);

} catch (Exception e) {
e.printStackTrace();
}
}

}


In this class you will notice that we are using HelloWorldServiceServiceLocator and the HelloWorldService which Axis generated for us. Running this client will invoke the webservice deployed on the server.


5) Ant build file

Here's the complete build.xml that i used as part of this example:


<?xml version="1.0" encoding="UTF-8"?>
<project name="MyApp" basedir=".." default="compile">

<property name="conf.dir" location="${basedir}/conf" />
<property name="build.dir" location="${basedir}/build"/>
<property name="classes.dir" location="${build.dir}/classes" />
<property name="compile.src" location="${basedir}/src" />
<property name="lib" location="${basedir}/lib" />
<property name="wsdl.dir" location="${build.dir}/wsdl" />
<property name="wsdd.dir" location="${build.dir}/wsdd" />


<!-- Sets classpath for build -->
<path id="classpath">
<pathelement path="${java.class.path}" />
<fileset dir="${lib}">
<include name="**/*.jar" />
</fileset>
<pathelement path="${classes.dir}" />
</path>



<taskdef name="axis-java2wsdl" classname="org.apache.axis.tools.ant.wsdl.Java2WsdlAntTask" >
<classpath refid="classpath"/>
</taskdef>

<taskdef name="axis-wsdl2java" classname="org.apache.axis.tools.ant.wsdl.Wsdl2javaAntTask" >
<classpath refid="classpath"/>
</taskdef>

<target name="clean-all" description="Cleans(Deletes) the contents of all output directories">
<delete dir="${classes.dir}"/>
<delete dir="${wsdl.dir}"/>
<delete dir="${wsdd.dir}"/>
</target>

<target name="build-all" description="Builds the entire project" depends="clean-all,compile,generateWSDL,generateWSDD,compileStubs,compile-client">

</target>

<target name="compile" description="Compiles java source code">
<mkdir dir="${classes.dir}"/>
<javac destdir="${classes.dir}" classpathref="classpath" fork="true" srcdir="${compile.src}" excludes="org/myapp/service/client/**/*.java"/>
</target>

<target name="compile-client" description="Compiles the webservice client" depends="compile">
<mkdir dir="${classes.dir}"/>
<javac destdir="${classes.dir}" classpathref="classpath" fork="true" srcdir="${compile.src}" includes="org/myapp/service/client/**/*.java"/>
</target>

<target name="generateWSDL" description="Generates wsdl files from the java service interfaces">
<mkdir dir="${wsdl.dir}"/>
<axis-java2wsdl classpathref="classpath"
output="${wsdl.dir}/HelloWorld.wsdl"
location="http://localhost:8080/axis/services/HelloWorld"
namespace="http://jaikiran.com"
classname="org.myapp.service.HelloWorldService">
<mapping namespace="http://jaikiran.com" package="org.myapp.service"/>
</axis-java2wsdl>
</target>

<target name="generateWSDD" description="Generates wsdd files from the wsdl files">
<mkdir dir="${wsdd.dir}"/>
<axis-wsdl2java
output="${wsdd.dir}"
deployscope="Application"
serverside="true"
url="${wsdl.dir}\HelloWorld.wsdl">
</axis-wsdl2java>
</target>

<target name="compileStubs" description="Compiles the java classes(stubs) generated by Axis using the wsdl2java task">
<mkdir dir="${classes.dir}"/>
<javac destdir="${classes.dir}" classpathref="classpath" fork="true" srcdir="${wsdd.dir}"/>
</target>

<target name="deployWebservice" description="Deploys the webservice onto the server, using the wsdd file and Axis' AdminClient">
<java classname="org.apache.axis.client.AdminClient" classpathref="classpath" fork="true">
<arg value="${wsdd.dir}/com/jaikiran/deploy.wsdd"/>
</java>
</target>

<target name="undeployWebservice" description="Undeploys webservice using the wsdd file and the Axis' AdminClient">
<java classname="org.apache.axis.client.AdminClient" classpathref="classpath" fork="true">
<arg value="${wsdd.dir}/com/jaikiran/undeploy.wsdd"/>
</java>
</target>

<target name="runWebserviceClient" description="Executes the java client which access the webservice">
<java classname="org.myapp.service.client.ServiceClient" classpathref="classpath" fork="true"/>
</target>

</project>



6) The project directory structure

This is how my project's directory structure looks like (just in case you want to create a similar one):


Sample Webservice
|
|
|------------- src
| |
| |---- org
| |--- myapp
| |---- service
| |------- HelloWorldService.java
| |
| |------- HelloWorldServiceImpl.java
| |
| |----- client
| |-------- ServiceClient.java
|
|
|------------- build
| |----- build.xml
|
|
|------------- lib
|
|----- activation.jar
|----- axis.jar
|----- axis-ant.jar
|----- commons-discovery-0.2.jar
|----- commons-logging-1.0.4.jar
|----- jaxrpc.jar
|----- mailapi_1_3_1.jar
|----- saaj.jar
|----- wsdl4j-1.5.1.jar

Tuesday, October 31, 2006

How to package an application into a ear file

This is for those who want to know the ideal and most common way an application is packaged in the form of an ear file. Here's how the application structure will look like:

myApp.ear
|
|--------- META-INF
| |
| |---------- application.xml
|
|--------- myEJB.jar
| |
| |--------- META-INF
| | |
| | |-------- ejb-jar.xml
| |
| |------ org
| | |----- myApp
| | |------- ejb
| |-------- *.class
|
|---------- myWeb.war
| |
| |----- WEB-INF
| |------ web.xml
| |
| |------ jsp
| |---- *.jsp
|
|---------- commonUtil.jar
|
|--------- org
|----- myApp
|------ common
|----- *.class


The application.xml present in the META-INF folder of the ear will contain the following:


<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE application PUBLIC "-//Sun Microsystems, Inc.//DTD J2EE Application 1.3//EN" "http://java.sun.com/dtd/application_1_3.dtd">
<application>
<display-name>helloworld</display-name>

<module>
<java>commonUtil.jar</java>
</module>

<module>
<web>
<web-uri>myWeb.war</web-uri>
<context-root>/myWeb</context-root>
</web>
</module>

<module>
<ejb>myEJB.jar</ejb>
</module>

</application>


Thursday, August 24, 2006

inverse attribute in Hibernate - What does it mean?



This is the best explanation, that i have seen till date, about Hibernate's "inverse" attribute:
Meaning of "inverse" in Hibernate

Evict collection from Hibernate second level cache


Hibernate allows persistent objects to be cached in its second level cache(The first level cache in Hibernate is the Session object which is ON by default). Applications can switch on the second level cache. When a object is being retrieved by the application through Hibernate, Hibernate first checks in its Session cache and then the Second level cache to see if the object has be retrieved already. If it finds it either the Session cache or the Second level cache, it will NOT fire a query to the database.
While configuring second level cache, the object can be cached and also the collections contained in the object can be cached. Have a look at the following example:

<hibernate-mapping default-lazy="false" >
<class name="org.myapp.ho.Parent" table="Parent">
<b><cache usage="read-only" /> </b>
<id name="id" type="Integer" column="ID" />
<set name="myChildren">
<b> <cache usage="read-only"/> </b>
<one-to-many class="org.myapp.ho.Child"/>
</set>
</class>
</hibernate-mapping>



<hibernate-mapping default-lazy="false" >
<class name="org.myapp.ho.Child" table="Child">
<b><cache usage="read-only" /></b>
<id name="id" type="Integer" column="ID" />
</class>
</hibernate-mapping>


Note that we have used the cache setting at 3 places:
1) The org.myapp.ho.Parent object
2) The "myChildren" collection in the org.myapp.ho.Parent object
3) The org.myapp.ho.Child object
When you configure a collection to be second level cached in Hibernate, it internally maintains a SEPERATE cache for these collection than the one which it uses to cache the parent objects. So in the example above, the “myChildren” will be cached separately than the org.myapp.ho.Parent object.
There might be cases where applications would want to evict objects from the cache. If its the Session cache from which the application has to evict the object then the call to Session.evict will cascade even to collections and will evict the collection from the *Session cache*. However, if the object(and the collections contained in it) have to be evicted from the second level cache, then the application has to *explicitly* call the evictCollection method on the SessionFactory to remove the *collection* contained in the Parent object. The reason behind this is, as already mentioned, the collections are cached separately, than the parent objects, in the second level cache.
So, in our example above, if we have to evict the Parent with id 500 and its collection from the second level cache, then here’s what has to be done:

SessionFactory sf = MyUtil.getSessionFactory();
//this will evict the Parent Object from the second level cache
sf.evict(org.myapp.ho.Parent.class,new Integer(500));
//this will evict the collection from the second level cache for the Parent with id=500
sf.evictCollection(org.myapp.ho.Parent.class.getName() + ".myChildren", new Integer(500));


The first parameter to the evictCollection method is the ‘roleName’ of the collection. The roleName is formed as follows:

roleName = NameOfTheParentClass + "." + NameOfTheCollectionInsideTheParent

The second parameter the evictCollection method is the id of the parent object, to which this collection belongs.

Custom reverse engineering strategy in Hibernate


Hibernate has tools to create mapping files(hbm files) and domain model classes from database schemas(reverse engineering). can be used as part of ant task to do this. Hibernate creates the property names using its default reverse engineering strategy. Hibernate also, provides a way through which the user can specify his own custom reverse engineering strategy through which he can follow his own naming conventions etc…

There is a reversestrategy attribute which can be set to some custom class, which implements org.hibernate.cfg.reveng.ReverseEngineeringStrategy, in the . Here’s an example:

<jdbcconfiguration configurationfile="hibernate.cfg.xml"

packagename="${package.name}"

revengfile="hibernate.reveng.xml"

reversestrategy="org.myapp.hibernate.tool.SampleReverseEngineeringStrategy"/>


The org.myapp.hibernate.tool.SampleReverseEngineeringStrategy is our own custom class which implements org.hibernate.cfg.reveng.ReverseEngineeringStrategy. In this example, our SampleReverseEngineeringStrategy, overrides the columnToPropertyName(TableIdentifier table, String column) method to provide a custom implementation for generating property names out of a column name. Here’s the SampleReverseEngineeringStrategy code:

package org.myapp.hibernate.tool;

import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.hibernate.cfg.reveng.DelegatingReverseEngineeringStrategy;
import org.hibernate.cfg.reveng.ReverseEngineeringStrategy;
import org.hibernate.cfg.reveng.TableIdentifier;
/**
*
* @author Jaikiran Pai
*
*/
public class SampleReverseEngineeringStrategy extends DelegatingReverseEngineeringStrategy {
/**
* Constructor
*
* @param delegate {@link org.hibernate.cfg.reveng.ReverseEngineeringStrategy}
*/
public SampleReverseEngineeringStrategy(ReverseEngineeringStrategy delegate) {
super(delegate);
}

/**
* Changes the default behaviour of naming the property.

* Does the following replacements(not neccessarily in the order) and returns the resulting
* {@link String} as property name:
* - Converts the first letter of the column to uppercase
* - Converts the letters following a ‘_’ character to uppercase in the column
* - Removes any underscores present from column
*
* @see org.hibernate.cfg.reveng.DelegatingReverseEngineeringStrategy
* @see org.hibernate.cfg.reveng.ReverseEngineeringStrategy
*
* @param table {@link TableIdentifier}
* @param column
* @return Returns the propert name after converting it appropriately
*/
public String columnToPropertyName(TableIdentifier table, String column) {

String replacedColumn = replaceFirstLetterToUpperCase(column);

replacedColumn = removeUnderScoresAndConvertNextLetterToUpperCase(replacedColumn);

if (anyReplacementsMadeToOriginalColumnName(column,replacedColumn)) {

return replacedColumn;

}

/*
* Let DelegatingReverseEngineeringStrategy handle this
*/
return super.columnToPropertyName(table, column);
}

/**
*
* Returns true if the originalString and replacedString are NOT equal
* (meaning there was some replacement done to the original column name). Else returns false.
*
* @param originalString The original column name
* @param replacedString The column name after doing necessary replacements
* @return Returns true if the originalString and replacedString are NOT equal
* (meaning there was some replacement done to the original column name). Else returns false.
*
* @throws {@link NullPointerException} if originalString is null.
*/
protected boolean anyReplacementsMadeToOriginalColumnName(String originalString, String replacedString) {
if (originalString.equals(replacedString)) {
return false;

}

return true;
}

/**
* Converts the first letter of the input to uppercase and
* returns the resultant {@link String}.
*
* Ex: If the input is startDate then the resulting {@link String}
* after replacement will be StartDate
*
* @param input The {@link String} whose contents have to be replaced
* @return Returns a {@link String} after doing the appropriate replacements
*/
protected String replaceFirstLetterToUpperCase(String input) {

/*
* The pattern to match a String starting with lower case
*/
final String startsWithLowerCasePattern = "^[a-z]";

Pattern patternForReplacingLowerCase = Pattern.compile(startsWithLowerCasePattern);
Matcher regexMatcher = patternForReplacingLowerCase.matcher(input);

/*
* This will hold the replaced contents
*/
StringBuffer replacedContents = new StringBuffer();

/*
* Check whether the first letter starts with lowercase.
* If yes, change it to uppercase, else pass on the control to
* DelegatingReverseEngineeringStrategy
*
*/
if (regexMatcher.find()) {

String firstCharacter = regexMatcher.group();
/*
* Convert it to uppercase
*/
regexMatcher.appendReplacement(replacedContents,firstCharacter.toUpperCase());
regexMatcher.appendTail(replacedContents);
regexMatcher.reset();

/*
* Return the replaced contents
*/
return replacedContents.toString();

}

//no replacements to do, just return the original input
return input;

}

/**
* Converts the letters following a ‘_’ character to uppercase and also removes
* the ‘_’ character from the input and returns the resulting {@link String}.
* Carries out a 2 pass strategy to do the replacements. During the first pass,
* replaces all the letters that immidiately follow a ‘_’ to uppercase.
* Ex: If the input is _start_Date__today_ then after the first pass of replacement, the
* resultant string will be _Start_Date__Today_
*
* This replaced {@link String} is then passed ahead for second pass (if no replacements were
* done during first pass, then the original {@link String} is passed). During the second pass
* the underscores are removed.
* Ex: If the input is _start_Date__today_ then after BOTH the passes the
* resultant string will be StartDateToday
*
* @param input The {@link String} whose contents have to be replaced
* @return Returns a {@link String} after doing the appropriate replacements
*/
protected String removeUnderScoresAndConvertNextLetterToUpperCase(String input) {

/*
* The pattern which matches a String that starts with a letter immidiately after
* a ‘_’ character
*/
final String stringFollowingUnderScore = "[.]*_[a-zA-Z]+";

Pattern patternForReplacingLowerCase = Pattern.compile(stringFollowingUnderScore);
Matcher regexMatcher = patternForReplacingLowerCase.matcher(input);
/*
* This will hold the replaced contents
*/
StringBuffer replacedContents = new StringBuffer();

boolean foundAnyMatch = false;

while (regexMatcher.find()) {
foundAnyMatch = true;
String matchedString = regexMatcher.group();
/*
* The character immidiately following the underscore
* Example:
* If matchedString is _tMn then originalCharAfterUnderScore will be the
* character t
*/

char originalCharAfterUnderScore = matchedString.charAt(1);
/*
* Convert the character to uppercase
*/

String replacedCharAfterUnderScore = String.valueOf(originalCharAfterUnderScore).toUpperCase();

/*
* Now place this replaced character back into the matchedString
*/

String replacement = matchedString.replace(originalCharAfterUnderScore,replacedCharAfterUnderScore.charAt(0));

/*
* Append this to the replacedColumn, which will be returned back to the user
*/
regexMatcher.appendReplacement(replacedContents,replacement);

} //end of while

regexMatcher.appendTail(replacedContents);
regexMatcher.reset();

/*
* Now the input string has been replaced to contain uppercase letters after the underscore.
* Ex: If input string was "_start_Date_today" then at this point after the above processing,
* the replaced string will be "_Start_Date_Today"
* The only thing that remains now is to remove the underscores from the input string.
* The following statements do this part.
*
*/

if (foundAnyMatch) {
return removeUnderScores(replacedContents.toString());

} else {
return removeUnderScores(input);

}

}

/**
* Removes any underscores present from input and returns the
* resulting {@link String}
* Ex: If the input is _start_Date__today_ then the resulting {@link String}
* after replacement will be startDatetoday
*
* @param input The {@link String} whose contents have to be replaced
* @return Returns a {@link String} after doing the appropriate replacements
*/
protected String removeUnderScores(String input) {

/*
* Pattern for matching underscores
*/
Pattern patternForUnderScore = Pattern.compile("[.]*_[.]*");

Matcher regexMatcher = patternForUnderScore.matcher(input);
/*
* This will hold the return value
*/
StringBuffer returnVal = new StringBuffer();
boolean foundAnyMatch = false;

while (regexMatcher.find()) {
foundAnyMatch = true;

String matchedString = regexMatcher.group();

/*
* Remove the underscore
*/
regexMatcher.appendReplacement(returnVal,"");

}

regexMatcher.appendTail(returnVal);
regexMatcher.reset();

/*
* If any match was found(and replaced) then return the replaced string.
* Else return the original input.
*/
if (foundAnyMatch) {
return returnVal.toString();

}
return input;

}

}


In the example above, the columnToPropertyName method is overridden to do the following:

- Creates property names that start with a Capital case(By default, Hibernate creates property names in camel-case)
- Converts the letter, that follows a ‘_’ (underscore character) to uppercase in the property name
- Removes any underscores in the property name

Ex: If the column name is start_Date_today, then the resulting property name after using the SampleReverseEngineeringStrategy would be StartDateToday.

Here’s a documentation from Hibernate about Controlling Reverse Engineering

Convert exception stacktrace to String object


Usually whenever a exception occurs, we use the method printStackTrace() on the exception object to display the stacktrace. However, if the stacktrace has to be stored into a String object then the following piece of code will prove handy:

/**
* Creates and returns a {@link java.lang.String} from t’s stacktrace
* @param t Throwable whose stack trace is required
* @return String representing the stack trace of the exception
*/
public String getStackTrace(Throwable t) {
StringWriter stringWritter = new StringWriter();
PrintWriter printWritter = new PrintWriter(stringWritter, true);
t.printStackTrace(printWritter);
printWritter.flush();
stringWritter.flush();

return stringWritter.toString();
}

Lookup an EJB from a different application, deployed on the same server, in JBoss


Question:
I have a web application through which i want to access an EJB which is deployed as a separate appliaction on the same server. How do i do it?
Answer:
In the web.xml of your war(the web application through which you want to access the EJB), have the following entry:


<ejb-ref>

<ejb-ref-name>GiveAnNameByWhichYouWouldLikeToReferTheBeanInYourWebApp</ejb-ref-name>
<ejb-ref-type>session</ejb-ref-type>
<home>packageName.ClassNameOfTheHomeObjectOfTheBeanYouWantToRefer</home>
<remote>packageName.ClassNameOfTheRemoteObjectOfTheBeanYouWantToRefer</remote>
</ejb-ref>


In the jboss-web.xml of your war, have the following entry:


<ejb-ref>
<!--The ejb-ref-name should be same as the one given in the web.xml above -->
<ejb-ref-name>GiveANameByWhichYouWouldLikeToReferTheBeanInYourWebApp</ejb-ref-name>
<!--Example:somecontext/somejndiName.YouWillFindThisJndiNameInTheJboss.xmlOfTheEJB-->
<jndi-name>TheJndiNametoWhichTheBeanIsBound</jndi-name>
</ejb-ref>


For more info, have a look at the dtds of web.xml (http://java.sun.com/dtd/web-app_2_3.dtd) and jboss-web.xml(http://www.jboss.org/j2ee/dtd)
In your code, do the lookup as:

 
Context ic = new InitialContext();
Object home=ic.lookup("java:comp/env/TheNameThatYouHadGivenInEjb-ref-nameTagOfJbossWeb.xml");


Here’s an example:

web.xml:

<ejb-ref>
<ejb-ref-name>MyTestBean</ejb-ref-name>
<ejb-ref-type>session</ejb-ref-type>
<home>com.test.ejb.MyBeanHome</home>
<remote>com.test.ejb.MyBeanRemote</remote>
</ejb-ref>



jboss-web.xml:

<ejb-ref>
<ejb-ref-name>MyTestBean</ejb-ref-name>
<jndi-name>myejb/test/MyTestBean</jndi-name>
</ejb-ref>


Lookup code:

Context ic = new InitialContext();
Object ejbHome = ic.lookup("java:comp/env/MyTestBean");

How to create a Queue/Topic in JBoss?


Queues/Topics are known as administered objects and in jboss they can be deployed through ServiceDotXml files. Let’s name our file, myapp-destination-service.xml. The contents of the file should look like:


<?xml version="1.0" encoding="UTF-8"?>

<server>
<!--My queue, named: myAppQueue -->
<mbean code="org.jboss.mq.server.jmx.Queue"
name="jboss.mq.destination:service=Queue,name=myAppQueue">
<depends optional-attribute-name="DestinationManager">jboss.mq:service=DestinationManager</depends>
</mbean>
<!-- My topic, named: myAppTopic -->
<mbean code="org.jboss.mq.server.jmx.Topic"
name="jboss.mq.destination:service=Topic,name=myAppTopic">
<depends optional-attribute-name="DestinationManager">jboss.mq:service=DestinationManager</depends>
</mbean>

</server>




Place this file in your %JBOSS_HOME%/server/default/deploy directory and start the server.
(Note: If you are using the ‘all’ configuration or the ‘minimal’ configuration then you will have to place this file in the %JBOSS_HOME%/server/all/deploy or %JBOSS_HOME%/server/minimal/deploy directory, as appropriate).

The above queue/topic will be bound to the jndi name: queue/myAppQueue and topic/myAppTopic respectively.

Note:

1) The above file is meant for JBoss-3.2.3. Though there might be slight changes to later versions of JBoss, the basic configuration mechanism remains the same.

2) JBoss also comes with preconfigured queues/topics. These configurations can be found in the jbossmq-destinations-service.xml file which is present in %JBOSS_HOME%/server/default/deploy/jms directory


Update:

For JBoss AS 6.x see this FAQ for more details on how to configure JMS queue/topics

How to enable Garbage Collection (GC) logs


To enable GC logs, the -Xloggc:logFileName option will have to be passed when java command is being executed. Additionally if the detailed log of the GC is required, then an additional -XX:+PrintGCDetails option will have to be passed.

Example: java -Xloggc:D:/log/myLogFile.log -XX:+PrintGCDetails myProg 


Wednesday, July 12, 2006

Create your own logging level in log4j


If you need to add your own logging level in log4j, then you can do it as follows. You will have to create your own class which will extend from Level. Here's a sample code for the same:

MyTraceLevel.java:

package org.myapp.log;

import org.apache.log4j.Level;
/**
* My own {@link org.apache.log4j.Level} for logging.
*
* @author Jaikiran Pai
*
*/
public class MyTraceLevel extends Level {

/**
* Value of my trace level. This value is lesser than
* {@link org.apache.log4j.Priority#DEBUG_INT}
* and higher than {@link org.apache.log4j.Level#TRACE_INT}
*/
public static final int MY_TRACE_INT = DEBUG_INT - 10;

/**
* {@link Level} representing my log level
*/
public static final Level MY_TRACE = new MyTraceLevel(MY_TRACE_INT,"MY_TRACE",7);

/**
* Constructor
*
* @param arg0
* @param arg1
* @param arg2
*/
protected MyTraceLevel(int arg0, String arg1, int arg2) {
super(arg0, arg1, arg2);

}

/**
* Checks whether sArg is "MY_TRACE" level. If yes then returns
* {@link MyTraceLevel#MY_TRACE}, else calls
* {@link MyTraceLevel#toLevel(String, Level)} passing it
* {@link Level#DEBUG} as the defaultLevel.
*
* @see Level#toLevel(java.lang.String)
* @see Level#toLevel(java.lang.String, org.apache.log4j.Level)
*
*/
public static Level toLevel(String sArg) {
if (sArg != null && sArg.toUpperCase().equals("MY_TRACE")) {
return MY_TRACE;
}
return (Level) toLevel(sArg, Level.DEBUG);
}

/**
* Checks whether val is {@link MyTraceLevel#MY_TRACE_INT}.
* If yes then returns {@link MyTraceLevel#MY_TRACE}, else calls
* {@link MyTraceLevel#toLevel(int, Level)} passing it {@link Level#DEBUG}
* as the defaultLevel
*
* @see Level#toLevel(int)
* @see Level#toLevel(int, org.apache.log4j.Level)
*
*/
public static Level toLevel(int val) {
if (val == MY_TRACE_INT) {
return MY_TRACE;
}
return (Level) toLevel(val, Level.DEBUG);
}

/**
* Checks whether val is {@link MyTraceLevel#MY_TRACE_INT}.
* If yes then returns {@link MyTraceLevel#MY_TRACE},
* else calls {@link Level#toLevel(int, org.apache.log4j.Level)}
*
* @see Level#toLevel(int, org.apache.log4j.Level)
*/
public static Level toLevel(int val, Level defaultLevel) {
if (val == MY_TRACE_INT) {
return MY_TRACE;
}
return Level.toLevel(val,defaultLevel);
}

/**
* Checks whether sArg is "MY_TRACE" level.
* If yes then returns {@link MyTraceLevel#MY_TRACE}, else calls
* {@link Level#toLevel(java.lang.String, org.apache.log4j.Level)}
*
* @see Level#toLevel(java.lang.String, org.apache.log4j.Level)
*/
public static Level toLevel(String sArg, Level defaultLevel) {
if(sArg != null && sArg.toUpperCase().equals("MY_TRACE")) {
return MY_TRACE;
}
return Level.toLevel(sArg,defaultLevel);
}
}

Now, you will have to configure your log4j.xml:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/"debug="false">
<!-- ================================= -->
<!-- Preserve messages in a local file -->
<!-- ================================= -->
<!-- A size based rolling appender -->
<appender name="FILE" class="org.apache.log4j.FileAppender">
<param name="File" value="D:/log/myLogFile.log"/>
<param name="Append" value="false"/>
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern" value="%d{ISO8601} %-5p [%c] %m%n"/>
</layout>
</appender>
<!-- ============================== -->
<!-- Append messages to the console -->
<!-- ============================== -->
<appender name="CONSOLE" class="org.apache.log4j.ConsoleAppender">
<param name="Target" value="System.out"/>
<param name="Threshold" value="INFO"/>
<layout class="org.apache.log4j.PatternLayout">
<!-- The default pattern: Date Priority [Category] Message\n -->
<param name="ConversionPattern" value="%d{ISO8601} %-5p [%c{1}] %m%n"/>
</layout>
</appender>
<!-- ================ -->
<!-- Limit categories -->
<!-- ================ -->
<category name="org.myapp">
<priority value="MY_TRACE" class="org.myapp.log.MyTraceLevel" />
<appender-ref ref="FILE"/>
</category>
<!-- ======================= -->
<!-- Setup the Root category -->
<!-- ======================= -->
<root>
<appender-ref ref="CONSOLE"/>
</root>
</log4j:configuration>


Here's a test program which can be used for testing whether the new log level that you introduced is being identified or not:

TestMyLogLevel.java:

package org.myapp.core;

import org.apache.log4j.Level;
import org.apache.log4j.Logger;
import org.myapp.log.MyTraceLevel;

/**
* Tests whether the new log level {@link org.myapp.log.MyTraceLevel#MY_TRACE}
* is working
*
* @author Jaikiran Pai
*
*/
public class TestMyLogLevel {

/**
* Writes a log message with {@link org.myapp.log.MyTraceLevel#MY_TRACE}
* and another message with {@link Level#DEBUG}
*
* @param args
*/
public static void main(String[] args) {
Logger logger = Logger.getLogger(TestMyLogLevel.class);
logger.log(MyTraceLevel.MY_TRACE,"I am MY_TRACE log");
logger.log(Level.DEBUG ,"I am a debug message");
}
}


Finally, here's the log file that got generated:

2006-07-12 13:45:40,633 MY_TRACE [org.myapp.core.TestMyLogLevel] I am MY_TRACE log
2006-07-12 13:45:40,633 DEBUG [org.myapp.core.TestMyLogLevel] I am a debug message

Points to note:

- The int value that you specify for your log level is important. Here i have defined "MY_TRACE" log level is to be higher than the DEBUG level but lower than the TRACE level provided by log4j. So whenever you have set a priority level to DEBUG on the category(in your log4j.xml file), the MY_TRACE level logs will *NOT* make it to the log file.

Wednesday, July 05, 2006

I get "log4j:WARN No appenders could be found for logger" message

While using log4j in your application, sometimes you might encounter the following message:

log4j:WARN No appenders could be found for logger(somePackageName.someClassName).
log4j:WARN Please initialize the log4j system properly.


The reason why you see this message is that your log4j configuration file(i.e. log4j.xml or log4j.properties) is NOT found in the classpath. Placing the log4j configuration file in the applications classpath should solve the issue. If you want to find out how log4j tries to configure itself, have a look at my earlier post :

Know how log4j tries to configure itself



Tuesday, July 04, 2006

Know how log4j tries to configure itself

Large number of applications use log4j for logging. Sometimes you may encounter cases where you sense that log4j is using some other configurations, other than the one that you expected it to use. You can debug the same by switching on the debug flag on log4j. Here's how you can do it:

Add -Dlog4j.debug to the command line. log4j will output info to std. out. telling you how it tries to configure itself.


Accessing a secure EJB through a standalone java client

To access a secure resource(may be a EJB), from a standalone client, you need to do a JAAS login. Here's an simple example which shows how to implement the same. But before going to the example, here's the reason why we need to do a login. Consider a secured EJB "MyTestEJB", deployed on a app server. The normal proceedure that you follow in a web-application to lookup the EJB and invoke a method on the same is as follows:

Context context = new InitialContext();
//lookup the home object
Object lookupObj = context.lookup("MyTestEJBHomeJndiName");
MyTestEJBHome home = (MyTestEJBHome) PortableRemoteObject.narrow(lookupObj, MyTestEJBHome.class);
//create the bean object from the home object
MyTestEJB myBean = home.create();
//invoke the method on the bean
myBean.someMethod();


In the steps above, when the method create is called on the home object, the app server internally checks whether the user who is doing this operation, is authenticated and authorised to do the same. If not, it will throw a SecurityException. The above statements will usually work in a web-application where usually you have a login page to carry out the login process.

Now, consider the case with a standalone java client which has just got a main method and which needs to invoke the secure bean. The application server will have no knowledge about which user is trying to do the operations on the bean(is he authenticated or authorised?). This is the reason why the standalone client needs to do a JAAS login, to let the application server know which user is trying to do the operation on the bean. Now lets look at the code for doing the same. Here we have a standalone java client with a main() method:


package myapp;

import javax.ejb.CreateException;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.rmi.PortableRemoteObject;
import javax.security.auth.login.LoginContext;
import javax.security.auth.login.LoginException;
import java.io.Serializable;
import java.rmi.RemoteException;

/**
*
* Stand-alone client invoking a method on a secure EJB
*
*/
public final class SomeStandAloneClient {


/**
* Default constructor
*
*/
public SomeStandAloneClient() {

}

/**
* Main method
*
* @param args Command line arguments
*/
public static void main(String[] args) {

//obtain the username and password which are passed as part of command line arguments
String userName = args[0];
String password = args[1];

System.out.println("Logging in user: " + userName);

/*
* The name of the file which will contain the login configurations
*/
final String authFile = "someFilename.someExtension";

/*
* Set the filename above, as part of system property, so that while doing a login,
* this file will be used to check the login configurations
*/
System.setProperty("java.security.auth.login.config", authFile);

/*
* During the login process(i.e. when the login() method on the LoginContext is called),
* the control will be transferred to a CallbackHandler. The CallbackHandler will be
* responsible for populating the Callback object with the username and password, which
* will be later on used by the login process
*
* The "MyCallbackHandler" is your own class and you can give any name to it. MyCallbackHandler
* expects the username and password to be passed through its constructor, but this is NOT
* mandatory when you are writing your own callback handler.
*
*
*/
MyCallbackHandler handler = new MyCallbackHandler(userName,password);

try {

/*
* Create a login context. Here, as the first parameter, you will specify which
* configuration(mentioned in the "authFile" above) will be used. Here we are specifying
* "someXYZLogin" as the configuration to be used. Note: This has to match the configuration
* specified in the someFilename.someExtension authFile above.
* The login context expects a CallbackHandler as the second parameter. Here we are specifying
* the instance of MyCallbackHandler created earlier. The "handle()" method of this handler
* will be called during the login process.
*/
LoginContext lc = new LoginContext("someXYZLogin",handler);

/*
* Do the login
*/
lc.login();

System.out.println("Successfully logged in user: " + userName);


} catch (LoginException le) {

System.out.println("Login failed");
le.printStackTrace();
return;
}

try{

/*
* Now that the user has logged in, invoke the method on the EJB
*/

Context context = new InitialContext();

//lookup the home object
Object lookupObj = context.lookup("MyTestEJBHomeJndiName");
MyTestEJBHome home = (MyTestEJBHome) PortableRemoteObject.narrow(lookupObj, MyTestEJBHome.class);

//create the bean object from the home object
MyTestEJB myBean = home.create();

//invoke the method on the bean
myBean.someMethod();


} catch (RemoteException re) {

System.out.println("Remote exception: ");
re.printStackTrace();
return;

} catch (NamingException ne) {

System.out.println("NamingException: ");
ne.printStackTrace();
return;

} catch (CreateException ce) {

System.out.println("CreateException: ");
ce.printStackTrace();
return;
}


} //end of main()

} //end of SomeStandAloneClient

Now the CallbackHandler:

package myapp;

import java.io.IOException;

import javax.security.auth.callback.Callback;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.callback.NameCallback;
import javax.security.auth.callback.PasswordCallback;
import javax.security.auth.callback.UnsupportedCallbackException;

/**
*
* CallbackHandler which will be invoked by the login module during the login
* process of the client. This is a simple CallbackHandler which sets the username
* and password, which will be later used by the Login module for authorizing the
* subject. This class only handles NameCallback and PasswordCallback. It throws
* an UnsupportedCallbackException, if the Callback is other than the two mentioned
* above.
* The username and password are provided as input to this class through its constructor.
*
*
*/
public class MyCallbackHandler implements CallbackHandler {

/**
* Username which will be set in the NameCallback, when NameCallback is handled
*/
private String username;

/**
* Password which will be set in the PasswordCallback, when PasswordCallback is handled
*/
private String password;

/**
* Constructor
* @param username The username
* @param password The password
*/
public MyCallbackHandler(String username, String password) {
this.username = username;
this.password = password;
}

/**
* @param callbacks Instances of Callbacks
* @throws IOException IOException
* @throws UnsupportedCallbackException If Callback is other than NameCallback or PasswordCallback
*/
public void handle(Callback callbacks[]) throws IOException, UnsupportedCallbackException {

for(int i = 0; i < callbacks.length; i++) {
if(callbacks[i] instanceof NameCallback) {
NameCallback nc = (NameCallback)callbacks[i];
nc.setName(username);
} else if(callbacks[i] instanceof PasswordCallback) {
PasswordCallback pc = (PasswordCallback)callbacks[i];
pc.setPassword(password.toCharArray());
} else {
throw new UnsupportedCallbackException(callbacks[i], "Unrecognized Callback");
}

}
}
}

The file containing the login configuration(in our case "someFilename.someExtension"):

someXYZLogin{
org.jboss.security.ClientLoginModule required;
};

Remember, the someXYZLogin configuration name is the same that i provided to the constructor of the LoginContext. The contents of the above file let the LoginContext know, which class will be actually responsible for doing the login. In this case, since i am using jboss, we have specified "org.jboss.security.ClientLoginModule" as the class.

Please make sure that the login configuration file is under the same directory from where you will be running your client.

The command to run your client is the same that you would use to run a normal java class:


java SomeStandAloneClient someUsername somePassword

The someUsername and somePassword will be the arguments that you will pass to the main() method.

This is just a simple example. Internally, there are a lot of things, that go on as part of login. The following link has a great explanation about the same(JAAS related things are explained starting from the "An Introduction to JAAS" section in that article). I highly recommend, to go through it atleast once:

Article on Security

Monday, July 03, 2006

Introductory post

Thought of putting in one place, all the technical stuff that i get to learn while working as a software developer in J2EE field.