Packaging WSDL clients in JARs with Maven and cxf-codegen-plugin

Xavi López picture Xavi López · Sep 3, 2014 · Viewed 8.1k times · Source

I've got a maven project that will consume a number of webservices. The application will be packaged as a WAR. So far the clients' code has been generated with cxf-codegen-plugin, in the generate-sources phase. By default, generated sources are placed into target/generated-sources/cxf, and after compile, they are compiled and mixed up with the application classes in target/classes. Both the generated and application classes can share the first level packages.

I'd like each of the clients to be packaged in its own JAR inside WEB-INF/lib in the WAR file. I found out about -clientjar, but it only generates the .jar files and places them into target/generated-sources/cxf, and the JARs also end up in target/classes along with the compiled classes, which is pointless.

I suppose the compile plugin at some point is compiling the generated sources into target/classes, and possibly another phase is also moving the JARs there. Would it be possible to have Maven avoid compiling those generated sources (or even have cxf-codegen-plugin generate no sources at all, only the JARs), and compile the application classes against the JARs generated by CXF?

I know it would be possible to achieve this by defining a multimodule project with a jar packaging module for each webservice, but I'd like to avoid this option. There can be a large number of webservices and it would not be suitable to maintain an independent module for each one. With -clientjar I'm already forced to define a <wsdlOption> for each WSDL in order to provide the JAR name for each WSDL (it's not possible to let cxf-codegen-plugin just scan src/main/resources/wsdl or <wsdlRoot>).

Of course the client JARs could be generated outside Maven and installed to a local repository, and be defined as dependencies in the project, but I'd like to know if it's possible to do this in a single Maven build.

With assemblies I'd probably sort out how to place the JAR files generated by -clientjar into WEB-INF/lib but there would still be an issue with the generated classes inside the WAR.

I don't have a deep knowledge of the Maven build lifecycle and its possibilities, any suggestions or pointers are very much welcome.

Answer

Xavi L&#243;pez picture Xavi López · Sep 12, 2014

This is the approach I took. It isn't exactly what I wanted, there's still lots of manual work to do with every WSDL file and is far from the solution I had in mind (having Maven automatically create individual JAR artifacts for every wsdl in the project and using them as dependency).

Specifically, for each WSDL, this approach needs :

  1. Creating a directory containing the WSDL file and a pom.xml containing a distinct artifact name
  2. Adding that directory to the top-level aggregator POM.
  3. Adding a dependency to the WS client JAR artifact in the webapp's POM.

I ended up creating an aggregator (multimodule) Maven project, having one module for each WebService Client, that will produce a JAR artifact with the generated WS client classes (following Maven's convention of one artifact per POM).

For convenience, the plugin that will take care of the WebService client classes generation is defined only once in the main pom.xml, and this POM is the parent of the WS client generation POMs. It is important to note the difference between aggregator and parent POMs. Both concepts are used together in this approach.

There's also a module for the main webapp. Its pom.xml specifies dependencies for each of the WS client JAR artifacts.

This is the outline of the project's directory tree:

|   pom.xml
+-- WSClient1
|   |   WebService1.wsdl
|   |   pom.xml
+-- WSClientN
|   |   WebServiceN.wsdl
|   |   pom.xml
\---MyWebapp
    |   src
    |   pom.xml

The aggregator POM:

<?xml version="1.0" encoding="UTF-8"?>
<project xsi...>
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.example.myproject</groupId>
    <artifactId>parent</artifactId>
    <version>1.0.0</version>
    <packaging>pom</packaging>
    <name>Aggregator POM</name>
    <properties>
        <!-- CXF version used for cxf-codegen-plugin -->
        <cxf.version>2.7.10</cxf.version>
    </properties>

    <modules>
            <!-- WS Client Modules -->
            <module>WSClient1</module>
            <module>WSClientN</module>
            <!-- WAR Module -->
            <module>MyWebapp</module>
    </modules>

    <!-- Project configuration every child POM will inherit -->
    <build>
        <plugins>
            <plugin>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <source>1.6</source>
                    <target>1.6</target>
                </configuration>
            </plugin>

            <plugin>
                <groupId>org.apache.cxf</groupId>
                <artifactId>cxf-codegen-plugin</artifactId>
                <version>${cxf.version}</version>
                <executions>
                    <execution>
                        <id>generate-sources</id>
                        <phase>generate-sources</phase>
                        <configuration>
                            <!-- WSDL files will be at each project's root level -->
                            <wsdlRoot>.</wsdlRoot>
                        </configuration>
                        <goals>
                            <goal>wsdl2java</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
</project>

Each one of the WS client POMs is extremely simple, just an artifact name and specify the parent:

<?xml version="1.0" encoding="UTF-8"?>
<project xsi...>
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.example.myproject</groupId>
    <artifactId>wsclient1</artifactId>
    <packaging>jar</packaging>
    <version>1.0.0</version>
    <name>WebService1 client</name>

    <parent>
      <groupId>com.example.myproject</groupId>
      <artifactId>parent</artifactId>
      <version>1.0.0</version>
    </parent>
</project>

The webapp's POM doesn't need to be a child of the parent POM because it won't be using the cxf-codegen-plugin, and includes <dependency>s for each one of the WS client artifacts:

 <dependency>
     <groupId>com.example.myproject</groupId>
     <artifactId>wsclient1</artifactId>
     <version>1.0.0</version>
 </dependency>
 <dependency>
     <groupId>com.example.myproject</groupId>
     <artifactId>wsclientN</artifactId>
     <version>1.0.0</version>
 </dependency>