JBoss.orgCommunity Documentation
The JAX-WS tools provided by JBossWS can be used in a variety of ways. First we will look at server-side development strategies, and then proceed to the client.
When developing a Web Service Endpoint (the server-side) you have the option of starting from Java ( bottom-up development ), or from the abstact contract (WSDL) that defines your service ( top-down development ). If this is a new service (no existing contract), the bottom-up approach is the fastest route; you only need to add a few annotations to your classes to get a service up and running. However, if you are developing a service with an already defined contract, it is far simpler to use the top-down approach, since the provided tool will generate the annotated code for you.
Bottom-up use cases:
Exposing an already existing EJB3 bean as a Web Service
Providing a new service, and you want the contract to be generated for you
Top-down use cases:
Replacing the implementation of an existing Web Service, and you can't break compatibility with older clients
Exposing a service that conforms to a contract specified by a third party (e.g. a vender that calls you back using an already defined protocol).
Creating a service that adheres to the XML Schema and WSDL you developed by hand up front
The following JAX-WS command line tools are included in JBossWS:
Command |
Description |
---|---|
wsprovide |
Generates JAX-WS portable artifacts, and provides the abstract contract. Used for bottom-up development. |
wsconsume |
Consumes the abstract contract (WSDL and Schema files), and produces artifacts for both a server and client. Used for top-down and client development |
The bottom-up strategy involves developing the Java code for your service, and then annotating it using JAX-WS annotations. These annotations can be used to customize the contract that is generated for your service. For example, you can change the operation name to map to anything you like. However, all of the annotations have sensible defaults, so only the @WebService annotation is required.
This can be as simple as creating a single class:
package echo; @javax.jws.WebService public class Echo { public String echo(String input) { return input; } }
A JSE or EJB3 deployment can be built using this class, and it is the only Java code needed to deploy on JBossWS. The WSDL, and all other Java artifacts called "wrapper classes" will be generated for you at deploy time. This actually goes beyond the JAX-WS specification, which requires that wrapper classes be generated using an offline tool. The reason for this requirement is purely a vender implementation problem, and since we do not believe in burdening a developer with a bunch of additional steps, we generate these as well. However, if you want your deployment to be portable to other application servers, you will unfortunately need to use a tool and add the generated classes to your deployment.
This is the primary purpose of the wsprovide tool, to generate portable JAX-WS artifacts. Additionally, it can be used to "provide" the abstract contract (WSDL file) for your service. This can be obtained by invoking wsprovide using the "-w" option:
$ javac -d . Echo.java $ wsprovide -w echo.Echo Generating WSDL: EchoService.wsdl Writing Classes: echo/jaxws/Echo.class echo/jaxws/EchoResponse.class
Inspecting the WSDL reveals a service called EchoService :
<service name='EchoService'> <port binding='tns:EchoBinding' name='EchoPort'> <soap:address location='REPLACE_WITH_ACTUAL_URL'/> </port> </service>
As expected, this service defines one operation, " echo ":
<portType name='Echo'> <operation name='echo' parameterOrder='echo'> <input message='tns:Echo_echo'/> <output message='tns:Echo_echoResponse'/> </operation> </portType>
Remember that when deploying on JBossWS you do not need to run this tool. You only need it for generating portable artifacts and/or the abstract contract for your service.
Let's create a POJO endpoint for deployment on JBoss AS. A simple web.xml needs to be created:
<web-app xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd" version="2.4"> <servlet> <servlet-name>Echo</servlet-name> <servlet-class>echo.Echo</servlet-class> </servlet> <servlet-mapping> <servlet-name>Echo</servlet-name> <url-pattern>/Echo</url-pattern> </servlet-mapping> </web-app>
The web.xml and the single class can now be used to create a war:
$ mkdir -p WEB-INF/classes $ cp -rp echo WEB-INF/classes/ $ cp web.xml WEB-INF $ jar cvf echo.war WEB-INF added manifest adding: WEB-INF/(in = 0) (out= 0)(stored 0%) adding: WEB-INF/classes/(in = 0) (out= 0)(stored 0%) adding: WEB-INF/classes/echo/(in = 0) (out= 0)(stored 0%) adding: WEB-INF/classes/echo/Echo.class(in = 340) (out= 247)(deflated 27%) adding: WEB-INF/web.xml(in = 576) (out= 271)(deflated 52%)
The war can then be deployed to the JBoss Application Server.The war can then be deployed to the JBoss Application Server; this will internally invoke wsprovide, which will generate the WSDL. If deployment was successful, and you are using the default settings, it should be available in the server management console.
For a portable JAX-WS deployment, the wrapper classes generated earlier could be added to the deployment.
The top-down development strategy begins with the abstract contract for the service, which includes the WSDL file and zero or more schema files. The wsconsume tool is then used to consume this contract, and produce annotated Java classes (and optionally sources) that define it.
wsconsume may have problems with symlinks on Unix systems
Using the WSDL file from the bottom-up example, a new Java implementation that adheres to this service can be generated. The "-k" option is passed to wsconsume to preserve the Java source files that are generated, instead of providing just classes:
$ wsconsume -k EchoService.wsdl echo/Echo.java echo/EchoResponse.java echo/EchoService.java echo/Echo_Type.java echo/ObjectFactory.java echo/package-info.java echo/Echo.java echo/EchoResponse.java echo/EchoService.java echo/Echo_Type.java echo/ObjectFactory.java echo/package-info.java
The following table shows the purpose of each generated file:
File |
Purpose |
---|---|
Echo.java |
Service Endpoint Interface |
Echo_Type.java |
Wrapper bean for request message |
EchoResponse.java |
Wrapper bean for response message |
ObjectFactory.java |
JAXB XML Registry |
package-info.java |
Holder for JAXB package annotations |
EchoService.java |
Used only by JAX-WS clients |
Examining the Service Endpoint Interface reveals annotations that are more explicit than in the class written by hand in the bottom-up example, however, these evaluate to the same contract:
@WebService(name = "Echo", targetNamespace = "http://echo/") public interface Echo { @WebMethod @WebResult(targetNamespace = "") @RequestWrapper(localName = "echo", targetNamespace = "http://echo/", className = "echo.Echo_Type") @ResponseWrapper(localName = "echoResponse", targetNamespace = "http://echo/", className = "echo.EchoResponse") public String echo( @WebParam(name = "arg0", targetNamespace = "") String arg0); }
The only missing piece (besides for packaging) is the implementation class, which can now be written, using the above interface.
package echo; @javax.jws.WebService(endpointInterface="echo.Echo") public class EchoImpl implements Echo { public String echo(String arg0) { return arg0; } }
Before going to detail on the client-side it is important to understand the decoupling concept that is central to Web Services. Web Services are not the best fit for internal RPC, even though they can be used in this way. There are much better technologies for this (CORBA, and RMI for example). Web Services were designed specifically for interoperable coarse-grained correspondence. There is no expectation or guarantee that any party participating in a Web Service interaction will be at any particular location, running on any particular OS, or written in any particular programming language. So because of this, it is important to clearly separate client and server implementations. The only thing they should have in common is the abstract contract definition. If, for whatever reason, your software does not adhere to this principal, then you should not be using Web Services. For the above reasons, the recommended methodology for developing a client is to follow the top-down approach , even if the client is running on the same server.
Let's repeat the process of the top-down section, although using the deployed WSDL, instead of the one generated offline by wsprovide . The reason why we do this is just to get the right value for soap:address. This value must be computed at deploy time, since it is based on container configuration specifics. You could of course edit the WSDL file yourself, although you need to ensure that the path is correct.
Offline version:
<service name='EchoService'> <port binding='tns:EchoBinding' name='EchoPort'> <soap:address location='REPLACE_WITH_ACTUAL_URL'/> </port> </service>
Online version:
<service name="EchoService"> <port binding="tns:EchoBinding" name="EchoPort"> <soap:address location="http://localhost.localdomain:8080/echo/Echo"/> </port> </service>
Using the online deployed version with wsconsume :
$ wsconsume -k http://localhost:8080/echo/Echo?wsdl echo/Echo.java echo/EchoResponse.java echo/EchoService.java echo/Echo_Type.java echo/ObjectFactory.java echo/package-info.java echo/Echo.java echo/EchoResponse.java echo/EchoService.java echo/Echo_Type.java echo/ObjectFactory.java echo/package-info.java
The one class that was not examined in the top-down section, was
EchoService.java
. Notice how it stores the location the WSDL was obtained from.
@WebServiceClient(name = "EchoService", targetNamespace = "http://echo/", wsdlLocation = "http://localhost:8080/echo/Echo?wsdl") public class EchoService extends Service { private final static URL ECHOSERVICE_WSDL_LOCATION; static { URL url = null; try { url = new URL("http://localhost:8080/echo/Echo?wsdl"); } catch (MalformedURLException e) { e.printStackTrace(); } ECHOSERVICE_WSDL_LOCATION = url; } public EchoService(URL wsdlLocation, QName serviceName) { super(wsdlLocation, serviceName); } public EchoService() { super(ECHOSERVICE_WSDL_LOCATION, new QName("http://echo/", "EchoService")); } @WebEndpoint(name = "EchoPort") public Echo getEchoPort() { return (Echo)super.getPort(new QName("http://echo/", "EchoPort"), Echo.class); } }
As you can see, this generated class extends the main client entry point in JAX-WS,
javax.xml.ws.Service
. While you can use
Service
directly, this is far simpler since it provides the configuration info for you. The only method we really care about is the
getEchoPort()
method, which returns an instance of our Service Endpoint Interface. Any WS operation can then be called by just invoking a method on the returned interface.
It's not recommended to refer to a remote WSDL URL in a production application. This causes network I/O every time you instantiate the Service Object. Instead, use the tool on a saved local copy, or use the URL version of the constructor to provide a new WSDL location.
All that is left to do, is write and compile the client:
import echo.*; public class EchoClient { public static void main(String args[]) { if (args.length != 1) { System.err.println("usage: EchoClient <message>"); System.exit(1); } EchoService service = new EchoService(); Echo echo = service.getEchoPort(); System.out.println("Server said: " + echo.echo(args0)); } }
It is easy to change the endpoint address of your operation at runtime, setting the ENDPOINT_ADDRESS_PROPERTY as shown below:
EchoService service = new EchoService(); Echo echo = service.getEchoPort(); /* Set NEW Endpoint Location */ String endpointURL = "http://NEW_ENDPOINT_URL"; BindingProvider bp = (BindingProvider)echo; bp.getRequestContext().put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY, endpointURL); System.out.println("Server said: " + echo.echo(args0));
An introduction to binding customizations:
The schema for the binding customization files can be found here:
wsconsume is a command line tool and ant task that "consumes" the abstract contract (WSDL file) and produces portable JAX-WS service and client artifacts.
The command line tool has the following usage:
usage: wsconsume [options] <wsdl-url> options: -h, --help Show this help message -b, --binding=<file> One or more JAX-WS or JAXB binding files -k, --keep Keep/Generate Java source -c --catalog=<file> Oasis XML Catalog file for entity resolution -j --clientjar=<name> Create a jar file of the generated artifacts for calling the webservice -p --package=<name> The target package for generated source -w --wsdlLocation=<loc> Value to use for @WebServiceClient.wsdlLocation -o, --output=<directory> The directory to put generated artifacts -s, --source=<directory> The directory to put Java source -t, --target=<2.1|2.2> The JAX-WS specification target -q, --quiet Be somewhat more quiet -v, --verbose Show full exception stack traces -l, --load-consumer Load the consumer and exit (debug utility) -e, --extension Enable SOAP 1.2 binding extension -a, --additionalHeaders Enables processing of implicit SOAP headers -d, --encoding=<charset> The charset encoding to use for generated sources -n, --nocompile Do not compile generated sources
The wsdlLocation is used when creating the Service to be used by clients and will be added to the @WebServiceClient annotation, for an endpoint implementation based on the generated service endpoint interface you will need to manually add the wsdlLocation to the @WebService annotation on your web service implementation and not the service endpoint interface.
Generate artifacts in Java class form only:
wsconsume Example.wsdl
Generate source and class files:
wsconsume -k Example.wsdl
Generate source and class files in a custom directory:
wsconsume -k -o custom Example.wsdl
Generate source and class files in the org.foo package:
wsconsume -k -p org.foo Example.wsdl
Generate source and class files using multiple binding files:
wsconsume -k -b wsdl-binding.xml -b schema1-binding.xml -b schema2-binding.xml
The wsconsume tools is included in the org.jboss.ws.plugins:jaxws-tools- maven- plugin plugin. The plugin has two goals for running the tool, wsconsume and wsconsume-test , which basically do the same during different maven build phases (the former triggers the sources generation during generate-sources phase, the latter during the generate-test-sources one).
The wsconsume plugin has the following parameters:
Attribute |
Description |
Default |
---|---|---|
bindingFiles |
JAXWS or JAXB binding file |
true |
classpathElements |
Each classpathElement provides a library file to be added to classpath |
${project.compileClasspathElements} or ${project.testClasspathElements} |
catalog |
Oasis XML Catalog file for entity resolution |
none |
targetPackage |
The target Java package for generated code. |
generated |
bindingFiles |
One or more JAX-WS or JAXB binding file |
none |
wsdlLocation |
Value to use for @WebServiceClient.wsdlLocation |
generated |
outputDirectory |
The output directory for generated artifacts. |
${project.build.outputDirectory} or ${project.build.testOutputDirectory} |
sourceDirectory |
The output directory for Java source. |
${project.build.directory}/generated-sources/wsconsume |
verbose |
Enables more informational output about command progress. |
false |
wsdls |
The WSDL files or URLs to consume |
n/a |
extension |
Enable SOAP 1.2 binding extension. |
false |
encoding |
The charset encoding to use for generated sources. |
${project.build.sourceEncoding} |
argLine |
An optional additional argline to be used when running in fork mode;
can be used to set endorse dir, enable debugging, etc.
Example
|
none |
fork |
Whether or not to run the generation task in a separate VM. |
false |
target |
A preference for the JAX-WS specification target |
Depends on the underlying stack and endorsed dirs if any |
You can use wsconsume in your own project build simply referencing the jaxws-tools- maven- plugin in the configured plugins in your pom.xml file.
The following example makes the plugin consume the test.wsdl file and generate SEI and wrappers' java sources. The generated sources are then compiled together with the other project classes.
<build> <plugins> <plugin> <groupId>org.jboss.ws.plugins</groupId> <artifactId>jaxws-tools-maven-plugin</artifactId> <version>1.2.0.Final</version> <configuration> <wsdls> <wsdl>${basedir}/test.wsdl</wsdl> </wsdls> </configuration> <executions> <execution> <goals> <goal>wsconsume</goal> </goals> </execution> </executions> </plugin> </plugins> </build>
You can also specify multiple wsdl files, as well as force the target package, enable SOAP 1.2 binding and turn the tool's verbose mode on:
<build> <plugins> <plugin> <groupId>org.jboss.ws.plugins</groupId> <artifactId>jaxws-tools-maven-plugin</artifactId> <version>1.2.0.Final</version> <configuration> <wsdls> <wsdl>${basedir}/test.wsdl</wsdl> <wsdl>${basedir}/test2.wsdl</wsdl> </wsdls> <targetPackage>foo.bar</targetPackage> <extension>true</extension> <verbose>true</verbose> </configuration> <executions> <execution> <goals> <goal>wsconsume</goal> </goals> </execution> </executions> </plugin> </plugins> </build>
Finally, if the wsconsume invocation is required for consuming a wsdl to be used in your testsuite only, you might want to use the wsconsume-test goal as follows:
<build> <plugins> <plugin> <groupId>org.jboss.ws.plugins</groupId> <artifactId>jaxws-tools-maven-plugin</artifactId> <version>1.2.0.Final</version> <configuration> <wsdls> <wsdl>${basedir}/test.wsdl</wsdl> </wsdls> </configuration> <executions> <execution> <goals> <goal>wsconsume-test</goal> </goals> </execution> </executions> </plugin> </plugins> </build>
Plugin stack dependencyThe plugin itself does not have an explicit dependency to a JBossWS stack, as it's meant for being used with implementations of any supported version of the
JBossWS SPI
. So the user is expected to set a dependency in his own
pom.xml
to the desired
JBossWS
stack version. The plugin will rely on the that for using the proper tooling.
<dependencies> <dependency> <groupId>org.jboss.ws.cxf</groupId> <artifactId>jbossws-cxf-client</artifactId> <version>5.1.0.Final</version> </dependency> </dependencies>
Be careful when using this plugin with the Maven War Plugin as that include any project dependency into the generated application war archive. You might want to set
<scope>provided</scope>
for the
JBossWS
stack dependency to avoid that.
Up to version 1.1.2.Final, the artifactId of the plugin was maven-jaxws-tools-plugin .
The wsconsume Ant task ( org.jboss.ws.tools.ant.WSConsumeTask ) has the following attributes:
Attribute |
Description |
Default |
---|---|---|
fork |
Whether or not to run the generation task in a separate VM. |
true |
keep |
Keep/Enable Java source code generation. |
false |
catalog |
Oasis XML Catalog file for entity resolution |
none |
package |
The target Java package for generated code. |
generated |
binding |
A JAX-WS or JAXB binding file |
none |
wsdlLocation |
Value to use for @WebServiceClient.wsdlLocation |
generated |
encoding |
The charset encoding to use for generated sources |
n/a |
destdir |
The output directory for generated artifacts. |
"output" |
sourcedestdir |
The output directory for Java source. |
value of destdir |
target |
The JAX-WS specification target. Allowed values are 2.0, 2.1 and 2.2 |
|
verbose |
Enables more informational output about command progress. |
false |
wsdl |
The WSDL file or URL |
n/a |
extension |
Enable SOAP 1.2 binding extension. |
false |
additionalHeaders |
Enables processing of implicit SOAP headers |
false |
Users also need to put streamBuffer.jar and stax-ex.jar to the classpath of the ant task to generate the appropriate artefacts.
The wsdlLocation is used when creating the Service to be used by clients and will be added to the @WebServiceClient annotation, for an endpoint implementation based on the generated service endpoint interface you will need to manually add the wsdlLocation to the @WebService annotation on your web service implementation and not the service endpoint interface.
Also, the following nested elements are supported:
Element |
Description |
Default |
---|---|---|
binding |
A JAXWS or JAXB binding file |
none |
jvmarg |
Allows setting of custom jvm arguments |
|
Generate JAX-WS source and classes in a separate JVM with separate directories, a custom wsdl location attribute, and a list of binding files from foo.wsdl:
<wsconsume fork="true" verbose="true" destdir="output" sourcedestdir="gen-src" keep="true" wsdllocation="handEdited.wsdl" wsdl="foo.wsdl"> <binding dir="binding-files" includes="*.xml" excludes="bad.xml"/> </wsconsume>
wsprovide is a command line tool, Maven plugin and Ant task that generates portable JAX-WS artifacts for a service endpoint implementation. It also has the option to "provide" the abstract contract for offline usage.
The command line tool has the following usage:
usage: wsprovide [options] <endpoint class name> options: -h, --help Show this help message -k, --keep Keep/Generate Java source -w, --wsdl Enable WSDL file generation -a, --address The generated port soap:address in wsdl -c. --classpath=<path> The classpath that contains the endpoint -o, --output=<directory> The directory to put generated artifacts -r, --resource=<directory> The directory to put resource artifacts -s, --source=<directory> The directory to put Java source -e, --extension Enable SOAP 1.2 binding extension -q, --quiet Be somewhat more quiet -t, --show-traces Show full exception stack traces
Generating wrapper classes for portable artifacts in the "generated" directory:
wsprovide -o generated foo.Endpoint
Generating wrapper classes and WSDL in the "generated" directory
wsprovide -o generated -w foo.Endpoint
Using an endpoint that references other jars
wsprovide -o generated -c application1.jar:application2.jar foo.Endpoint
The wsprovide tools is included in the org.jboss.ws.plugins:jaxws-tools- maven- plugin plugin. The plugin has two goals for running the tool, wsprovide and wsprovide-test , which basically do the same during different Maven build phases (the former triggers the sources generation during process-classes phase, the latter during the process-test-classes one).
The wsprovide plugin has the following parameters:
Attribute |
Description |
Default |
---|---|---|
testClasspathElements |
Each classpathElement provides a library file to be added to classpath |
${project.compileClasspathElements} or ${project.testClasspathElements} |
outputDirectory |
The output directory for generated artifacts. |
${project.build.outputDirectory} or ${project.build.testOutputDirectory} |
resourceDirectory |
The output directory for resource artifacts (WSDL/XSD). |
${project.build.directory}/wsprovide/resources |
sourceDirectory |
The output directory for Java source. |
${project.build.directory}/wsprovide/java |
extension |
Enable SOAP 1.2 binding extension. |
false |
generateWsdl |
Whether or not to generate WSDL. |
false |
verbose |
Enables more informational output about command progress. |
false |
portSoapAddress |
The generated port soap:address in the WSDL |
|
endpointClass |
Service Endpoint Implementation. |
|
You can use wsprovide in your own project build simply referencing the jaxws-tools- maven- plugin in the configured plugins in your pom.xml file.
The following example makes the plugin provide the wsdl file and artifact sources for the specified endpoint class:
<build> <plugins> <plugin> <groupId>org.jboss.ws.plugins</groupId> <artifactId>jaxws-tools-maven-plugin</artifactId> <version>1.2.0.Final</version> <configuration> <verbose>true</verbose> <endpointClass>org.jboss.test.ws.plugins.tools.wsprovide.TestEndpoint</endpointClass> <generateWsdl>true</generateWsdl> </configuration> <executions> <execution> <goals> <goal>wsprovide</goal> </goals> </execution> </executions> </plugin> </plugins> </build>
The following example does the same, but is meant for use in your own testsuite:
<build> <plugins> <plugin> <groupId>org.jboss.ws.plugins</groupId> <artifactId>jaxws-tools-maven-plugin</artifactId> <version>1.2.0.Final</version> <configuration> <verbose>true</verbose> <endpointClass>org.jboss.test.ws.plugins.tools.wsprovide.TestEndpoint2</endpointClass> <generateWsdl>true</generateWsdl> </configuration> <executions> <execution> <goals> <goal>wsprovide-test</goal> </goals> </execution> </executions> </plugin> </plugins> </build>
Plugin stack dependencyThe plugin itself does not have an explicit dependency to a JBossWS stack, as it's meant for being used with implementations of any supported version of the
JBossWS SPI
. So the user is expected to set a dependency in his own
pom.xml
to the desired
JBossWS
stack version. The plugin will rely on the that for using the proper tooling.
<dependencies> <dependency> <groupId>org.jboss.ws.cxf</groupId> <artifactId>jbossws-cxf-client</artifactId> <version>5.1.0.Final</version> </dependency> </dependencies>
Be careful when using this plugin with the Maven War Plugin as that include any project dependency into the generated application war archive. You might want to set
<scope>provided</scope>
for the
JBossWS
stack dependency to avoid that.
Up to version 1.1.2.Final, the artifactId of the plugin was maven-jaxws-tools-plugin .
The wsprovide ant task ( org.jboss.ws.tools.ant.WSProvideTask ) has the following attributes:
Attribute |
Description |
Default |
---|---|---|
fork |
Whether or not to run the generation task in a separate VM. |
true |
keep |
Keep/Enable Java source code generation. |
false |
destdir |
The output directory for generated artifacts. |
"output" |
resourcedestdir |
The output directory for resource artifacts (WSDL/XSD). |
value of destdir |
sourcedestdir |
The output directory for Java source. |
value of destdir |
extension |
Enable SOAP 1.2 binding extension. |
false |
genwsdl |
Whether or not to generate WSDL. |
false |
address |
The generated port soap:address in wsdl. |
|
verbose |
Enables more informational output about command progress. |
false |
sei |
Service Endpoint Implementation. |
|
classpath |
The classpath that contains the service endpoint implementation. |
"." |
Executing wsprovide in verbose mode with separate output directories for source, resources, and classes:
<target name="test-wsproivde" depends="init"> <taskdef name="wsprovide" classname="org.jboss.ws.tools.ant.WSProvideTask"> <classpath refid="core.classpath"/> </taskdef> <wsprovide fork="false" keep="true" destdir="out" resourcedestdir="out-resource" sourcedestdir="out-source" genwsdl="true" verbose="true" sei="org.jboss.test.ws.jaxws.jsr181.soapbinding.DocWrappedServiceImpl"> <classpath> <pathelement path="${tests.output.dir}/classes"/> </classpath> </wsprovide> </target>