SOAP web services are often used in commercial software. If we plan to use existing SOAP web service, we should receive a WSDL file which defines the contract between the web service and its clients. This contract defines at least: the methods provided by the web service, arguments of each methods and their types, exception specification for methods and definitions of additional XSD types.
JDK provides wsimport executable which can generate Java source code files based on the information provided in the WSDL file. In practice we use a build tool to do it automatically. In this post I would like to show how we can import WSDL file in Maven project.
Storing WSDL file
After receiving WSDL file we should put it in a location that is accessible by Maven. Usually WSDL file is placed under src/main/resources folder or one of its subdirectories.
In our case the situation will be more complicated because we got two files. One is the WSDL file periodictableaccess.wsdl that we want to import. The second is the XSD file periodictablebase.xsd which is imported inside WSDL file like this:
<xsd:import schemaLocation="periodictablebase.xsd" namespace="http://www.example.org/periodictablebase"></xsd:import>
With such import the WSDL file and its XSD file should be placed in the same directory. We will choose src/main/resources/wsdl directory.
Configuring JAX-WS Maven plugin
To import the WSDL file we have to put the following plugin definition in pom.xml file:
<plugin> <groupId>org.codehaus.mojo</groupId> <artifactId>jaxws-maven-plugin</artifactId> <version>2.4.1</version> <executions> <execution> <id>periodictableaccessws</id> <goals> <goal>wsimport</goal> </goals> <configuration> <wsdlDirectory>src/main/resources/wsdl</wsdlDirectory> <wsdlFiles> <wsdlFile>periodictableaccess.wsdl</wsdlFile> </wsdlFiles> <packageName>com.example.periodictable.ws.access</packageName> <vmArgs> <vmArg>-Djavax.xml.accessExternalSchema=all</vmArg> </vmArgs> </configuration> </execution> </executions> </plugin>
Let’s go over it step by step. With groupId, artifactId and version we reference the Maven plugin and its version that we want to use. In this case it is jaxws-maven-plugin from Codehaus.
Later we define executions element. This element can contain multiple execution elements – one for each independent wsimport execution. In our case we got only one WSDL file and we execute wsimport only once.
Inside execution element we define unique ID of the execution. Obviously, if you got multiple execution elements, they should have different IDs.
We should also define the goals that we want to use. There are four of these:
- wsimport – generates JAX-WS artifacts used by JAX-WS clients and services using WSDL file
- wsimport-test – same like wsimport but for tests
- wsgen – generates JAX-WS artifacts used by JAX-WS clients and services using service endpoint implementation class
- wsgen-test – same like wsgen but for tests
Finally, we should provide the configuration that should be used to generate JAX-WS artifacts. Element wsdlDirectory defines the directory where the WSDL files are placed and wsdlFiles specifies which exactly WSDL files from this directory should be imported. We can specify more than one WSDL file if needed.
Element packageName defines in which Java package the generated artifacts should be placed. It is advised to put the generated artifacts in its own separate package to prevent name collisions.
Normally, it should be everything but in our case the WSDL file is not standalone and imports XML schema from separate file. Since Java 8 such access is subject to restrictions and may result in following error during build:
schema_reference: Failed to read schema document 'periodictablebase.xsd', because 'file' access is not allowed due to restriction set by the accessExternalSchema property.
To prevent the error we should define javax.xml.accessExternalSchema property. We can do so in different ways. In this case we pass the property definition as an argument to wsimport execution.
Here we used only a small subset of parameters provided by the Maven plugin jaxws-maven-plugin. You can read more about the plugin and its parameters in the official documentation.
Please, notice that we don’t specify the path to XSD file in pom.xml file. It is completely enough that the WSDL file knows the location of XSD file.
Running JAX-WS Maven plugin
When you run build in Maven (e.g. mvn install), you should notice the following plugin execution in the output:
--- jaxws-maven-plugin:2.4.1:wsimport (periodictableaccessws) @ com.example.wsimp --- Processing: file:/home/robert/informatyka/tests/com.example.wsimp/src/main/resources/wsdl/periodictableaccess.wsdl jaxws:wsimport args: [-keep, -s, '/home/robert/informatyka/tests/com.example.wsimp/target/generated-sources/wsimport', -d, '/home/robert/informatyka/tests/com.example.wsimp/target/classes', -encoding, UTF-8, -Xnocompile, -p, com.example.periodictable.ws.access, "file:/home/robert/informatyka/tests/com.example.wsimp/src/main/resources/wsdl/periodictableaccess.wsdl"] parsing WSDL... Generating code...
If you take a close look, you can notice that:
- The generated Java source files (*.java files) are put somewhere under target/generated-sources/wsimport directory.
- The generated Java class files (*.class files) are put under target/classes directory.
JAX-WS Maven plugin is bound to Maven lifecycle phase generate-sources. This phase is run almost at the very beginning of the build to ensure that all generated classes are already here for compile phase.
More information about wsimport can be found in this technote.
Importing multiple WSDL files
If you have many WSDL files to import, there are 2 possibilities. The easiest one is to specify multiple wsdlFile elements inside wsdlFiles in file pom.xml. The generated classes will be put in the same Java package which may result in name conflicts. The better option is to create multiple execution elements inside file pom.xml. This way we can specify separate Java package for each imported WSDL to prevent name conflicts.