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.

17 Comments:

Anonymous said...

how to soft-code the wsdlUrl in wsimport goal?

Anonymous said...

This can be passed as environment variable and later used in pom.xml

<wsdlUrl>${env.WSDL_URL}</wsdlUrl>

Steffan said...

Good example, thank you for this! I think I have spotted 2 minor errors:

(1) The lines including the word legacy should be removed from both pom.xml files. That particular repository at dev.java.net uses the default (Maven 2) repository format. I confirmed this by rebuilding after deleting my local repository.

(2) In the webservice project, the file src/main/webapp/WEB-INF/classes/handlers.xml ought to be in src/main/resources instead.

Anonymous said...

@Steffan,

You are right on both points.

Anonymous said...

Do you know if it will work with Java 1.5_06? Thanks.

Anonymous said...

Yes, it should work.

Anonymous said...

I'm using Eclipse and I wanted to make the generated source to be included in the src/main/java directory. Do you know how I can do that?

Steffan said...

@Anonymous:


Note the convention that Maven regards "src" as read only. All output is placed in "target" - This applies to generated source code too. I suggest using m2eclipse, an Eclipse plugin which allows importing of Maven projects. In this case, it recognises the generated source code tree placed in "target" and creates an extra source folder to point to it.

You can use m2eclipse for Vinod's example, but there are some corrections needed for this to work properly. Here's the full procedure for installing m2eclipse and using Vinod's example in Eclipse:

(1) Close Eclipse if it is running.
(2) Unzip Vinod's project zip file.
(3) In both projects, delete folder .settings, and files .classpath and .project
(4) Move file webservice/src/main/webapp/WEB-INF/classes/handlers.xml to webservice/src/main/resources
(5) Delete the now empty folder webservice/src/main/webapp/WEB-INF/classes
(6) In both pom.xml files, delete lines including the word legacy
(7) In webservice/pom.xml, after the line <artifactId>maven-jetty-plugin</artifactId>, insert the line <version>6.1.25</version>
(8) Start Eclipse
(9) Follow these instructions to install m2eclipse Core : http://m2eclipse.sonatype.org/installing-m2eclipse.html
(10) Also follow the instructions on that page to install m2eclipse Extras. You only need the extra Maven Integration for WTP (Optional)
(11) Restart Eclipse when prompted
(12) Click menu item File > Import
(13) Select Maven > Existing Maven Projects
(14) For Root Directory, navigate to webservice folder (don't import webservice-client yet as that would fail)
(15) Click Finish to import the webservice project
(16) In Package Explorer, right click on webservice project and select Run As > Maven build...
(17) In the Edit Configuration dialog that appears, for the item Goals, enter the value compile jetty:run
(18) Click Run to immediately run this configuration. Maven will compile the webservice project, then fire up a Jetty instance and serve the project. Leave Jetty running for now, as this is needed to build the webservice-client project.
(19) Click menu item File > Import
(20) Select Maven > Existing Maven Projects
(21) For Root Directory, navigate to webservice-client folder
(22) Click Finish to import the webservice-client project. An error marker will be displayed against the webservice-client project. This is normal because wsimport has not been run, so not all the source code is there yet.
(23) In Package Explorer, right click on webservice-client project, and select Maven > Update Project Configuration. This actually invokes a Maven build, causing wsimport to be run, fetching the WSDL from the running web service. m2eclipse will also recognise the added source root target/jaxws/wsimport/java, and adds a source folder to reflect this, as can be seen in Package Explorer. The error marker should now be gone, as all necessary source code is now present.
(24) To run the client, in Package Explorer expand the nodes webservice-client > src/main/java > client, right click on WsClient.java and select Run As > Java Application. Observe the Console output for both the client (WsClient) and server (webservice).
(25) To finish, click Terminate on the webservice Console to stop Jetty.

1B said...

I am new to maven2 and creation of war to wsdl. I would like to ask if i am using glassfish bundle tools with eclipse. How do i compile, create war and generate wsdl using this maven2 which is embedded in the system

Anonymous said...

hello sir,
Iam new to web services... I would like to know if there is any method where in when I provide the WSDL url in the HTML page,it would create another HTML page dynamically which would contain all the services declared in the WSDL. Once we click the required services it should ask for the inputs and the hit the specified services..

Christian said...

HiVinod!

You made my day! :-) Thanx a lot for this article and thanx to Steffan for his adaptations to Eclipse!

Christian

Abhinav Chand Mittal said...

Hi Sir
I read your post about maven and webservices, this is not quite relevant to me in my issue but the issue endorsed directory mechanism is what i am facing.
Well, i am working on Oracle IPM(Imaging and Process management)product and while configuring ODC(Oracle Document Capture) commit profile i am facing same error.
Failed to automatically connect to I/PM systems and retrieve data.Please correct the login settings and reconnect
Error:Failed to login.Exception:java.lang.LinkageError:JAXB 2.0 API is being loaded from the bootstrap classloader, but this RI(from jar:file:/C:/Program%20Files/OracleDocument%20Capture/OracleIPM11g/lib/oracle.webservices.standalone.client.jar!/com/sun/xml/bind/v2/model/impl/ModelBuilder.class) needs 2.1 API.Use the endorsed directory mechanism to place jaxb-api.jar in the bootstrap classloader(See:http://download.oracle.com/javase/1.5.0/docs/guide/standards/)
Kindly help me if you have any information about this, i have used directory mechanism but still its not working.
Regards
Abhinav(amittal@systime.net)[Please contact me on my mail]

online calculator said...

It's very great post. This is really helpful for me.Thanks for sharing it.

Anonymous said...

Hi Vinoth,

Nice information I've found here. How do I install jaxws-maven-plugin?

I am using m2eclipse, tried to install all the extras, from the below page. Does these extras contain jaxws-maven-plugin?.

http://m2eclipse.sonatype.org/sites/m2e-extras

Thanks a lot.

Steffan said...

@Anonymous:

There is no need to manually "install" jaxws-maven-plugin (nor any other Maven plugin) as Maven automatically downloads and uses any plugins mentioned in the plugins section of the POM. Just follow the instructions I gave in a post above and all should work.

The purpose of m2eclipse is to add functionality to Eclipse for working with Maven projects, such as creating POMs. m2e-extras further enhances this to cope with more Maven project types such as .war files. Note that you can work with Maven projects without Eclipse, as Maven is a command line tool. So there is no relation between specific Maven plugins (such as jaxws-maven-plugin) and the m2eclipse tool.

Cheers,
Steffan

john said...

Hi Steffan,

i have a same issue as u mentioned for the solution for using m2eclipse.

In my project i want to create client artifacts using wsImport (maven plugin).For this reason i created a maven client project and in the pom i given the wsimport plugin parameters.
When i do maven install it is creating source class files under target folder.But i need them to be under src/main/java...
Im using RAD...Could you please help me out to solve this issue?

Steffan said...

@john:

This is working by design, there is no issue here. As I mentioned above, the src directory of a Maven project should be regarded as read-only during a Maven build. All output goes to target directory, and that includes Java source code generated during the build. I strongly discourage attempting to write output to the src directory as you will have an unstable project (I know this from experience).

Eclipse and Maven both support the concept of multiple source root directories. m2eclipse is also smart enough to know when a new source root is introduced during a build, and this is precisely what happens during step (23) in my post above.