I have hard times using maven to generate my client. So Please refer to Creating a web-service client directly from the source for the first part of my question.
To keep it simple and short, I want to go from here (a file in src/main/java) :
package com.example.maven.jaxws.helloservice;
import javax.jws.WebService;
@WebService
public class Hello {
public String sayHello(String param) {
; return "Hello " + param;
}
}
to there :
/**
* This class was generated by the JAX-WS RI.
* JAX-WS RI 2.1.7-b01-
* Generated source version: 2.1
*
*/
@WebServiceClient(name = "HelloService", targetNamespace = "http://helloservice.jaxws.maven.example.com/", wsdlLocation = "http://localhost:8080/test/")
public class HelloService
extends Service
{
private final static URL HELLOSERVICE_WSDL_LOCATION;
private final static Logger logger = Logger.getLogger(com.example.wsimport.HelloService.class.getName());
...etc
using only 1 pom.xml file.
Please note the wsdlLocation set on the end.
The pom.xml file will probably use both maven-jaxws-plugin wsgen AND wsimport with some tricky configuration to achieve this.
Assuming, you're not going to try to use the generated stubs in the same project that you're doing this in (which would create circular dependencies and be a bad idea...) then, yes, you can do something like this.
The config is not all that tricky, in fact you'd kind of guessed it in your question but here goes:
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>jaxws-maven-plugin</artifactId>
<executions>
<execution>
<id>generate-wsdl</id>
<phase>process-classes</phase>
<goals>
<goal>wsgen</goal>
</goals>
<configuration>
<sei><!-- fully qualified class name goes here --></sei>
<genWsdl>true</genWsdl>
</configuration>
</execution>
<execution>
<id>generate-stubs</id>
<phase>process-classes</phase>
<goals>
<goal>wsimport</goal>
</goals>
<configuration>
<wsdlDirectory>target/jaxws/wsgen/wsdl</wsdlDirectory>
<wsdlFiles>
<wsdlFile><!-- class name goes here -->Service.wsdl</wsdlFile>
</wsdlFiles>
<!-- *** you need the next line to set the wsdlLocation in the generated stubs *** -->
<wsdlLocation>http://localhost:8080/test</wsdlLocation>
</configuration>
</execution>
</executions>
</plugin>
Update - to package up the generated code into a jar I would use the maven-jar-plugin like so:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<executions>
<execution>
<id>package-wsclient-jars</id>
<phase>package</phase>
<goals>
<goal>jar</goal>
</goals>
<configuration>
<classesDirectory>target/jaxws/<!-- rest of the path here, can't remember it right now --></classesDirectory>
<classifier>wsclient</classifier>
</configuration>
</execution>
</executions>
</plugin>
I've quickly pasted this from our config but our usage is a little different (as we zip up the wsdl files not the generated client but I believe this will get you pretty close). You'll probably have to setup the version for the maven-jar-plugin if you're not already using it - 2.3.1 seems to be the latest.
I have succeeded in the same process. The aim is to create a web service proxy JAR for the web services in our application.
Web services
We have three web services in our application (currently). They are created by a Maven project that builds a WAR with the service and supporting classes, that contains a sun-jaxws.xml
descriptor as well as web.xml
.
The web-service Maven project is part of a multi-project build so the web-service WAR is one module in an EAR that also has an EJB JAR, user-interface WAR and other JARs (as well as dependencies).
Client proxy JAR
Ideally, I would create the client proxy JAR in another Maven project that depends on the web service WAR project and uses the Maven JAX-WS plugin goals wsgen
followed by wsimport
to do the work.
But I could not get a Maven project to use a WAR as a depedency so that its classes (in WEB-INF/classes
) are added to the class path. I tried the AppFuse Warpath plugin but could not get it to unpack the WAR dependency.
Two artifacts from one Maven project
In the end I had to resort to building and installing multiple artifacts in one Maven project. My findings about wsgen
and wsimport
and the second artifact:
jaxws-maven-plugin
needs its own dependencies for the wsgen
goal if they are outside the current project, otherwise it can’t find them. (Even if verbose
is set true
this goal emits little helpful information.)
- The
wsgen
goal must be called for each service to generate a WSDL.
- The
wsimport
goal is called once for on all WSDLs at once because the services share a number of support classes. (Because all generated classes go into one client proxy package, it is important to have no overlap of class names across the original sources, even if they originate in different packages.)
maven-jar-plugin:jar
and maven-install-plugin:install-file
are called to package and install the client proxy JAR.
Below are key parts of the POM with some comments:
<parent>
<groupId>lighthouse.navigate</groupId>
<artifactId>navigate</artifactId>
<version>3.9.0-SNAPSHOT</version>
</parent>
<artifactId>navigate-webservice</artifactId>
<packaging>war</packaging>
<name>Navigate WebService</name>
<dependencies>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>navigate-util</artifactId>
<version>${project.version}</version>
<scope>provided</scope>
</dependency>
<!-- snip -->
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.jvnet.jax-ws-commons</groupId>
<artifactId>jaxws-maven-plugin</artifactId>
<version>2.2</version>
<executions>
<!-- WSDLs must be generated for each service. -->
<execution>
<id>generate-client-wsdl</id>
<phase>process-classes</phase>
<goals>
<goal>wsgen</goal>
</goals>
<configuration>
<sei>nav.ws.client.ClientWebService</sei>
<genWsdl>true</genWsdl>
</configuration>
</execution>
<execution>
<id>generate-licence-wsdl</id>
<phase>process-classes</phase>
<goals>
<goal>wsgen</goal>
</goals>
<configuration>
<sei>nav.ws.licence.LicenceWebService</sei>
<genWsdl>true</genWsdl>
</configuration>
</execution>
<!-- snip -->
<!-- Single generation of client proxy because WSDLs share classes. -->
<execution>
<id>generate-proxies</id>
<phase>process-classes</phase>
<goals>
<goal>wsimport</goal>
</goals>
<configuration>
<wsdlDirectory>target/generated-sources/wsdl</wsdlDirectory>
<destDir>target/wsgen/classes</destDir>
<packageName>nav.ws.proxy</packageName>
<xnocompile>false</xnocompile>
</configuration>
</execution>
</executions>
<!--
NB: wsgen needs its own dependencies declared so it can find
classes outside this project.
-->
<dependencies>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>navigate-util</artifactId>
<version>${project.version}</version>
</dependency>
<!-- snip -->
</dependencies>
</plugin>
<!-- Package client proxy JAR as secondary artifact. -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>2.3.2</version>
<executions>
<execution>
<id>package-wsclient</id>
<phase>package</phase>
<goals>
<goal>jar</goal>
</goals>
<configuration>
<classesDirectory>target/wsgen/classes</classesDirectory>
<finalName>navigate-wsclient-${project.version}</finalName>
</configuration>
</execution>
</executions>
</plugin>
<!-- Install client proxy JAR as secondary artifact. -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-install-plugin</artifactId>
<version>2.4</version>
<executions>
<execution>
<id>install-wsclient</id>
<phase>install</phase>
<goals>
<goal>install-file</goal>
</goals>
<configuration>
<file>target/navigate-wsclient-${project.version}.jar</file>
<groupId>${project.groupId}</groupId>
<artifactId>navigate-wsclient</artifactId>
<version>${project.version}</version>
<packaging>jar</packaging>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
Running wsgen then wsimport then wsgen then wsimport will achieve what you need, but you maven will not let you build a module twice. To get around this, you can run maven as an ant exec task, which builds mvn install in the current directory, attached to the validate phase of the build cycle. This will do a wsimport then a wsgen, then when the build continues past the validate cycle it will rerun wsimport (now on the newly created wsgen) and then wsgen again. This lets you have a webservice and a webclient that uses that service built in the same maven module.
Put the following plugin declaration in your maven pom.xml build file:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-antrun-plugin</artifactId>
<version>1.7</version>
<dependencies>
<dependency>
<groupId>ant-contrib</groupId>
<artifactId>ant-contrib</artifactId>
<version>1.0b3</version>
<exclusions>
<exclusion>
<groupId>ant</groupId>
<artifactId>ant</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
<executions>
<execution>
<phase>validate</phase>
<configuration>
<tasks>
<taskdef resource="net/sf/antcontrib/antlib.xml"
classpathref="maven.plugin.classpath" />
<if>
<equals arg1="${antrunned}" arg2="yes"/>
<then>
<echo message="Good job."/>
</then>
<else>
<exec dir="${basedir}" executable="mvn" failonerror="true">
<arg value="install"/>
<arg value="-Dantrunned=yes"/>
</exec>
</else>
</if>
</tasks>
</configuration>
<goals>
<goal>run</goal>
</goals>
</execution>
</executions>
</plugin>