Monday, April 12, 2010

JAX-WS Web service with Maven

One of my earlier post Building JAX-WS Webservice was using Ant as build tool for developing web services. With each passing day Maven is gaining more popularity and became preferred build tool for many developers. I used to get emails from several readers about working JAX-WS examples with Maven as build tool. In this post I will try to demonstrate to publish and consume a JAX-WS web service using Maven.

The source code used in this post is available here, which will be referred at several places in this post.

The jaxws-maven-plugin has two goals wsgen and wsimport.

  • wsgen - This reads a service endpoint implementation class and generates all of the portable artifacts for a JAX-WS web service. With newer versions (tested with 2.2) of JAX-WS, execution of this task is not required for publishing webservice
  • wsimport - This tool reads WSDL and generates client side artifacts. We will be using this goal for generating client and consuming a web service.

Publish Web service

For publishing a web service we need to write an interface and its implementation then annotate them with relevant annotations. The JAX-WS libraries should be added as dependency in pom.xml. A sample (partial) build script is shown below-

<dependencies>
    <dependency>
        <groupId>com.sun.xml.ws</groupId>
        <artifactId>jaxws-rt</artifactId>
        <scope>compile</scope>
        <version>2.2</version>
    </dependency>
</dependencies>
. . .
<build>
    <plugins>
        <plugin>
            <groupId>org.mortbay.jetty</groupId>
            <artifactId>maven-jetty-plugin</artifactId>
            <configuration>
                <scanIntervalSeconds>10</scanIntervalSeconds>
                <connectors>
                    <connector implementation="org.mortbay.jetty.nio.SelectChannelConnector">
                        <port>8080</port>
                        <maxIdleTime>60000</maxIdleTime>
                    </connector>
                </connectors>
            </configuration>
        </plugin>
    </plugins>
</build>

We can see in the above Maven script that the wsgen is not required. The JAX-WS implementation will take care of that at runtime. To make testing easier I have used Jetty plugin, which will publish the web service for further consumption by the client in next step. To publish the web service invoke following command in 'webservice' directory of the source code of this post-
mvn -e clean compile jetty:run

Create Client

For creating web service client the wsimport goal of jaxws-maven-plugin will be used. The wsimport goal creates portable client artifacts by parsing a WSDL. In this example the WSDL URL is the one which was published by starting the web service at previous step. The pom.xml of client side application will look like below-

<dependencies>
    <dependency>
        <groupId>com.sun.xml.ws</groupId>
        <artifactId>jaxws-rt</artifactId>
        <scope>compile</scope>
        <version>2.2</version>
    </dependency>
</dependencies>
. . .
<build>
    <plugins>
        <!-- Generate client using WSDL -->
        <plugin>
            <groupId>org.codehaus.mojo</groupId>
            <artifactId>jaxws-maven-plugin</artifactId>
            <configuration>
                <packageName>wsclient</packageName>
                <wsdlUrls>
                    <wsdlUrl>http://localhost:8080/webservice/myService?wsdl</wsdlUrl>
                </wsdlUrls>
            </configuration>
        </plugin>
        <plugin>
            <groupId>org.codehaus.mojo</groupId>
            <artifactId>exec-maven-plugin</artifactId>
            <version>1.1</version>
            <executions>
                <execution>
                    <goals>
                        <goal>java</goal>
                    </goals>
                </execution>
            </executions>
            <configuration>
                <mainClass>client.WsClient</mainClass>
            </configuration>
        </plugin>
    </plubins>
</build>

For the sake of simplicity I have used exec-maven-plugin here, which will run the test class to invoke the web service from build script itself. To generate client and run the test class, trigger the build from 'webservice-client' directorty using following command-
mvn -e clean compile exec:java


The examples shown above were tested with following configuration-
  • Java 1.6.18
  • JAX-WS 2.
  • Maven 2.2.1
  • Windows XP

Note:- If JAX-WS 2.2 is used with older versions of Java 6 (e.g. Java 1.6.03) then you may encounter errors like shown below-
SEVERE: WSSERVLET11: failed to parse runtime descriptor: java.lang.LinkageError: JAXB 2.1 API is being loaded from the bootstrap classloader, but this RI (from jar:file:/C:/Documents%20and%20Settings/Vinod%20Singh/.m2/repository/com/sun/xml/bind/jaxb-impl/2.2/jaxb-impl-2.2.jar!/com/sun/xml/bind/v2/model/impl/ModelBuilder.class) needs 2.2 API. Use the endorsed directory mechanism to place jaxb-api.jar in the bootstrap classloader. (See http://java.sun.com/j2se/1.6.0/docs/guide/standards/)
java.lang.LinkageError: JAXB 2.1 API is being loaded from the bootstrap classloader, but this RI (from jar:file:/C:/Documents%20and%20Settings/Vinod%20Singh/.m2/repository/com/sun/xml/bind/jaxb-impl/2.2/jaxb-impl-2.2.jar!/com/sun/xml/bind/v2/model/impl/ModelBuilder.class) needs 2.2 API. Use the endorsed directory mechanism to place jaxb-api.jar in the bootstrap classloader. (See http://java.sun.com/j2se/1.6.0/docs/guide/standards/) at com.sun.xml.bind.v2.model.impl.ModelBuilder.(ModelBuilder.java:173)

The reason of these errors is that the earlier versions of Java 6 are using lower versions of JAX-WS while we are trying to use higher version of JAX-WS. To overcome these errors either we need to downgrade the JAX-WS version in our runtime environment or use endorsed directory mechanism.

Hope this will be useful to some of the readers.

Thursday, February 25, 2010

m2eclipse plugin makes M2_REPO variable non modifiable in Eclipse

Yesterday for first time I installed m2eclipse plugin and all of a sudden projects failed to compile. I was using Maven for quite long time hence adding all library dependencies for a project using a variable named M2_REPO, which was pointing towards a location where Maven downloaded jars were stored. After installation of m2eclipse plugin Eclipse was not able to resolve the absolute path of the jars in classpath.



The value of the M2_REPO variable was changed itself to some path other than what I configured. When I tried to fix that, to my surprise I can't edit it any longer. It was marked with this text "M2_REPO (non modifiable)". After poking here and there for a while I come to know that m2eclipse picks the value of M2_REPO variable from $MAVEN_HOME/conf/settings.xml (MAVEN_HOME is the currently selected Maven installation) file using <localRepository> element-

<localRepository>PATH_TO_REPOSITORY</localRepository>
If nothing is specifed in settings.xml then ${user.home}/.m2 directory becomes the value of M2_REPO variable.

Wednesday, February 17, 2010

Remove old images of Linux (Ubuntu) kernel

Development of Linux is quite fast paced and new versions of kernel are released pretty frequently. I have a dual boot desktop with Ubuntu and Windows XP. With each update grub boot menu keeps on growing and I have to make Windows XP default OS as desktop is used by my 8 year son as well. Each version of kernel takes up > 100MB space, though that is not a big worry with huge storage available at much cheaper price.

The unused (older) versions of kernel can be removed safely. Usually I prefer to keep the last version along with "last - 1", to be on safer side so that I have something to fallback upon if something goes wrong with the latest kernel. To remove a version of kernel go to Synaptic Package Manager and search for linux-image and select the ones you want to remove.



You'll get an option to remove completely (Mark for Complete Removal), that will get rid of everything including configuration files used by that package. Before removing kernel make sure to not remove the current version as that will make the system unusable. Use uname -a command to know the currently used version of kernel. Output of this command looks like below on my machine-

vinod@vinod-desktop:~$ uname -a
vinod@vinod-desktop:~$ Linux vinod-desktop 2.6.31-19-generic #56-Ubuntu SMP Thu Jan 28 02:39:34 UTC 2010 x86_64 GNU/Linux

You can also find and remove linux-headers using this simple method.

Monday, January 11, 2010

Share a datasource among several applications in Tomcat using Atomikos

In a previous post I discussed about how to use Atomikos JTA implementation in Tomcat. We had a requirement of sharing a globally defined datasource among several applications deployed in Tomcat container. The datasource lookup from the one application (whichever does the lookup first) succeeds but it always fails from the second application with an error message like-

Another resource already exists with name DATA_SOURCE_NAME - pick a different name

Why? We were using the sample bean factory available at Atomikos' site, which tries to recreate the datasource on each lookup. Within an application one can cache the datasource after initial JNDI lookup call. But what about doing a lookup of same resource from multiple applications?

After poking around for a while in Atomikos source code I found that after creation, datasource instance is cached by the Atomikos. With a minor modification in bean factory as shown below, the cached instance can be retrieved instead of recreating it.
// see if this DataSource is already initialized then return cached object
// if available, else Atomikos will throw an exception
try {
    bean = IntraVmObjectRegistry.getResource(((AbstractDataSourceBean) bean).getUniqueResourceName());
    if (log.isInfoEnabled())
        log.info("Returning cached value of AbstractDataSourceBean (Atomikos): " + bean);

    return bean;
} catch (NameNotFoundException nfe) {
    // OK, it is not available go ahead and create one
}
With this change a globally defined datasource can be used by multiple applications inside a Tomcat container to use Atomikos JTA implementation.