X

A Common Ant Build File for Metro-Based Services and Clients

Guest Author

by Harold Carr

Metro is a high performance, extensible, easy to use web service stack.
It combines the JAX-WS reference implementation with
Web Services Interoperability Technologies (WSIT),
formerly known as Project Tango. WSIT includes features that enable advanced web service interoperability with
the .NET Windows Communication Foundation (WCF), a set of technologies for building and running connected systems.
Metro is bundled as part of the GlassFish Server,
and runs in other containers such as Tomcat.

An earlier Tech Tip,

Testing Interoperability Between Metro and .NET
, described an interoperability test in which a Metro-based client
communicates with a WCF-based service. In using Metro-based services for these tests, I sometimes start with
a Web Service Definition Language (WSDL) file. Sometimes I build Metro-based services from Java classes. Sometimes
I deploy the services to containers. Sometimes I don't deploy to a container. To simplify the task of using Metro-based
services for these tests, I have a common ant build.xml file. The file handles all these combinations
of web service sources and deployment approaches. This Tech Tip describes the contents and operation of the
build.xml file.

A .tar file, CommonAntBuild.tar, accompanies this tip. The tar file contains the
code and build files referenced in the tip. Note that the directory structure of the code uses symbolic links
that work on UNIX and LINUX platforms. The tip assumes that you are running on a UNIX or LINUX platform.
If you are running on a Windows platform, you will need to use other techniques for sharing common files -- these techniques
are not covered in this tip.

Top-Level Targets

To get started, let's see what the build.xml file advertises by entering the
following commands. Note that the commands in this tip assume that you have
connected to the root directory of the source code associated with this tip.



   cd examples/from-java-container
ant




In response, you should see the following build file targets:



   help:
[echo] service-from-wsdl-container : Builds, wars, deploys service
[echo] service-from-java-container : Builds, wars, deploys service
[echo] service-from-wsdl-publish : Builds, wars, publishes service
[echo] service-from-java-publish : Builds, wars, publishes service
[echo] service-undeploy : Undeploys the service WAR from the container
[echo] client : Builds and runs the client
[echo] clean : Delete all generated code




Each of the targets that begin with service-from- either builds a service from a WSDL description
or from a Java class annotated with @WebService. It then either creates a WAR file and deploys the war
file to a container or it invokes the Endpoint.publish() method to publish the web service endpoint using
the HTTP server built into the Java platform.

General Project Layout

Here is the general layout of a project that uses the common build file.



   cd examples/from-java-container
ls -alF
-rw-r--r-- 1 carr staff 74 May 27 09:21 .README
lrwxr-xr-x 1 carr staff 5 May 27 09:00 build-common@ -> ../..
-rw-r--r-- 1 carr staff 436 May 27 09:06 build.properties
lrwxr-xr-x 1 carr staff 29 May 27 09:00 build.xml@ -> build-common/build-common.xml
drwxr-xr-x 4 carr staff 136 May 27 09:07 etc/
drwxr-xr-x 3 carr staff 102 May 27 08:44 src/




To avoid duplicating files, I use symbolic links. The common build file is located in the root directory.
It is named build-common.xml and linked in the current directory as build.xml.
The following lines in the file import property files:



   <project basedir="." default="help" name="tango/code/build-common">
<property file="build.properties"/>
<property file="${tools.properties}"/>
<property file="${container.properties}"/>




The first import is for the build.properties file in the current directory. The first two lines of
the build.properties file define the other two property files to be imported. As shown below, the
two other files to be imported are tools.properties and tomcat-container.properties:



   tools.properties=build-common/tools.properties
container.properties=build-common/tomcat-container.properties




The tools.properties file defines settings for the wsgen and wsimport
commands. The wsgen command generates Java API for XML Web Services (JAX-WS) portable artifacts for
a web service. The wsimport command generates JAX-WS portable artifacts for a web service client.
Here are the contents of the tools.properties file:



   # WSGEN options
wsgen.verbose=true
wsgen.keep=true
# WSIMPORT options
wsimport.debug=false
wsimport.verbose=false
wsimport.keep=true
wsimport.extension=
wsimport.xendorsed=true




You can either globally control settings for all the code that uses this property file or you can define a property
file specifically for a particular project.

The tomcat-container.properties file sets up ant constants that are used to build and deploy a service
in a Tomcat container. Here are the contents of the tomcat-container.properties file:



   catalina.home=/usr/local/hc/java/apache/apache-tomcat-5.5.26
lib.home=${catalina.home}/shared/lib
lib.endorsed=${catalina.home}/common/endorsed
lib.glassfishv3=${catalina.home}
deploy.dir=${catalina.home}/webapps




Note that if you use Tomcat with the common build file, you need to have Metro already installed into Tomcat.

There are two other properties files of interest in the root directory: glassfish-container.properties
and no-container.properties. The glassfish-container.properties file sets up ant constants
that are used to build and deploy a service in a GlassFish container. Here are the contents of the
glassfish-container.properties file:



   #as.home=/Applications/NetBeans/glassfish-v2.1
as.home=C:/ProgramFiles/glassfish-v2.1
lib.home=${as.home}/lib"
lib.endorsed=${as.home}/lib/endorsed"
lib.glassfishv3=${as.home}/modules
domain=domain1
deploy.dir=${as.home}/domains/${domain}/autodeploy




Here are the contents of the no-container.properties file:



   metro.home=/Users/carr/ws/wsit/wsit/dist/image/metro/lib
lib.home=${metro.home}
lib.endorsed=${metro.home}
lib.glassfishv3=${metro.home}
deploy.dir=/tmp




The no-container.properties file points directly to the image created when building Metro.
This allows users to test the latest bits. In general, most people will not want to download Metro source, but they will want
to try building a service without deploying it to a container, that is, simply publishing the service endpoint using
the Endpoint.publish() method. In that case, use the GlassFish or Tomcat settings to get the Metro code --
the deploy.dir assignment will be ignored.

Building a Service From Java Classes and Deploying To a Container

Let's take a closer look at what's in the build.xml file and see how it's used. Let's begin by
examining the project that builds a Metro web service from a Java class and deploys the service to a container.

First change to the from-java-container directory:



   cd examples/from-java-container




The .README file in this directory shows the ant tasks necessary to build and deploy the service,
to run the client, to do cleanup, and to undeploy the service. Here are the contents of the .README file:



   ant service-from-java-container
ant client
ant clean
ant service-undeploy




Building and Deploying the Service

The first ant task in the .README file runs the service-from-java-container target.
Here is the definition of that target in the build.xml file:



   <target name="service-from-java-container"

depends="clean, setup, setup-war, service-compile, service-wsgen, service-war, service-deploy"/>




The service-from-java-container target initially runs the following targets:

  • clean - Removes any previously generated and/or compiled code.
  • setup - Creates a build/classes directory.
  • setup-war - Creates a build/war directory.
  • service-compile - Compiles the pertinent Java classes.
  • service-wsgen - Runs the wsgen command to generate portable artifacts for
    the web service.
  • service-war - Creates a WAR file for the compiled and generated code.
  • service-deploy - Deploys the WAR file to the deploy directory of the container.

Here is what the service-compile target looks like:



    <target name="service-compile" depends="setup">
<javac
fork="true"
srcdir="${basedir}/src"
destdir="${build.classes.home}"
includes="\*\*/service/\*\*,\*\*/common/\*\*">
<classpath refid="wsit.classpath"/>
</javac>
</target>




The service-compile target runs the javac compiler on all Java classes found in directories
named service and common (note that common is not used in this example).
A Java class annotated with @WebService is found and compiled (not shown).

Here is what the service-wsgen target looks like:



    <target name="service-wsgen" depends="setup">
<wsgen
verbose="${wsgen.verbose}"
keep="${wsgen.keep}"
destdir="${build.classes.home}"
sei="${service.wsgen.sei}"

>
<classpath path="${build.classes.home}"/>
</wsgen>
</target>




The service-wsgen target invokes the wsgen command to generate server-side artifacts, such as
JAXB marshaling code, for the web service. The target uses wsgen settings from the tools.properties
and service.wsgen.sei variables defined in the build.properties file of the current directory.
For example, here is the service.wsgen.sei setting in the build.properties file in the
from-java-container directory:



    service.wsgen.sei=fromjava.service.AddNumbersImpl




The service.wsgen.sei setting is the service endpoint interface (SEI) for the web service, that is,
a Java class that contains a @WebService annotation.

Here are the contents of the service-war target:



    <target name="service-war">

<war warfile="${service.war.file}" webxml="etc/web.xml">

<webinf dir="${etc.dir}" includes="sun-jaxws.xml"/>

<zipfileset

dir="${etc.dir}"

includes="\*.wsdl, \*.xsd"

prefix="WEB-INF/wsdl"/>

<classes dir="${build.classes.home}"/>

</war>
</target>




The service-war target creates a WAR file that contains the compiled and generated code. It also includes
\*.xml files (and \*.wsdl and \*.xsd files - not used in this example). The name of
the WAR file is defined in build.properties of the current directory, as follows:



    service.war.filename=wsit-enabled-fromjava.war




Here is what the service-deploy target looks like:



    <target name="service-deploy">

<copy file="${service.war.file}" todir="${deploy.dir}"/>
</target>




The service-deploy target copies the WAR file to the deploy directory of the container.

Creating the Client

The second ant task in the from-java-container .README file runs the client target.
The client target builds the client and then runs it. Building the client runs the
client-wsimport, client-compile, and client-cp-config targets in the
build.xml file.

Here is the definition of the client-wsimport target:



   <target name="client-wsimport" depends="setup">
<wsimport
debug="${wsimport.debug}"
verbose="${wsimport.verbose}"
keep="${wsimport.keep}"
extension="${wsimport.extension}"
xendorsed="${wsimport.xendorsed}"
destdir="${build.classes.home}"
wsdl="${client.wsimport.wsdl}"

package="${client.wsimport.package}"

>
<binding dir="${etc.dir}" includes="${client.wsimport.binding}"/>
</wsimport>
</target>




The client-wsimport target invokes the wsimport command. The target picks up
client.wsimport.wsdl, client.wsimport.package, and
client.wsimport.binding settings in the build.properties file in the current directory
(client.wsimport.binding is not used in this example).
Here are the settings in the build.properties file:



   address=http://localhost:8080/wsit-enabled-fromjava/addnumbers
client.wsimport.wsdl=${address}?wsdl
client.wsimport.package=fromwsdl.client




The target action retrieves the WSDL file of the deployed service and places the generated code in the
fromwsdl.client package. You can implement finer-grained control of JAXB bindings by supplying
a client.wsimport.binding file.

Here is the definition of the client-compile target:



   <target name="client-compile" depends="setup">
<javac
fork="true"
srcdir="${basedir}/src"
destdir="${build.classes.home}"
includes="\*\*/client/\*\*,\*\*/common/\*\*">
<classpath>
<path refid="wsit.classpath"/>
<pathelement path="${client.compile.classpath.extra}"/>
</classpath>
</javac>
</target>




The client-compile target runs the javac compiler on all the Java source code for the project that
it finds in the client and
common
directories under src.

Here is the definition of the client-cp-config target:



   <target name="client-cp-config" depends="setup">
<copy todir="${build.classes.home}">
<fileset dir="${etc.dir}" includes="client-\*.xml"/>
</copy>
</target>




The client-cp-config target copies any files to the build directory.
This is useful for configuration files such as those used in JAX-WS handler configuration (not used in this example).

Here is the target that runs the client:



   <target name="client-run">
<java fork="true" classname="${client.main}">
<jvmarg value="${client.http.dump.do}"/>
<classpath>
<path refid="wsit.classpath"/>
<pathelement location="${build.classes.home}"/>
<pathelement location="${etc.dir}"/>
</classpath>
<arg value="${client.main.arg1}"/>
<arg value="${client.main.arg2}"/>
</java>
</target>




The client-cp-config target defines the main class and optionally defines a switch to control
whether Metro dumps the HTTP messages that are sent and received. It also optionally passes values to the main class.
The target uses the following settings from the build.properties file:



   client.main=fromjava.client.AddNumbersClient
#client.http.dump=true




The following ant tasks do final clean up by deleting the build directory and undeploying the
service from the container.



   ant clean
ant service-undeploy




Building a Service From a WSDL File and Deploying To a Container

Now let's examine the project that builds a Metro web service from a WSDL file and deploys the service to a container.

Change to the from-wsdl-container directory:



   cd examples/from-wsdl-container




The ant tasks for building a service from WSDL are similar to those for building a service from
a Java class. The difference is that the service-wsimport target runs before the service-compile
target, and the service-wsgen target does not run at all.

The service-wsimport target generates code from a WSDL file similarly to the way the client-wsimport
target does on the client-side in building a service from a Java class. However, in the service-wsimport case,
the WSDL file is located in the file system as defined in the build.properties file, rather than accessed
from the network. Here are the settings in the build.properties file in the from-wsdl-container
directory:



   service.wsimport.wsdl=etc/AddNumbers.wsdl
service.wsimport.package=fromwsdl.service




The .README file in the from-wsdl-container directory shows the ant tasks to build a service from
a WSDL file and deploy it to a container. With the exception of the initial task, the tasks are the same as for building
a service from a Java class and deploying it to a container. The client tasks are exactly the same because the client
should know nothing about the implementation of the web service.

Here are the contents of the .README file:



   ant service-from-wsdl-container
ant client
ant clean
ant service-undeploy




Building a Service From a Java Class and Publishing It

So far we've examined projects that build a web service and deploy it to a container. Now let's examine projects
that build a service and publish the web service endpoint.

Change to the from-java-publish directory:



   cd examples/from-java-publish




The .README file shows the ant tasks to build and publish the service. Here is the initial task:



   ant service-from-java-publish &




The & at the and of the command is the way to run a UNIX or LINUX process in the background.
(Running the process in the background allows me to use the same shell for further commands. However, spawning
another shell works well too.)

Here is the definition of the service-from-java-publish target in the build.xml file:



   <target name="service-from-java-publish"

depends="clean, setup, service-compile, service-wsgen, service-cp-config, service-publish"/>




As is the case for building a service from a Java class and deploying it to a container, the
service-compile target compiles the user-written source code for the web service.
The service-wsgen target then runs the wsgen command on the SEI.
The service-cp-config target copies service-\*.xml files from the
etc directory. Those files are useful for configuration files such as JAX-WS handlers.
However, they are not used here.

The service-publish target is where this project differs from service-from-java-container.
Here is the definition of the service-publish target:



   <target name="service-publish">
<java fork="true" classname="${service.main}">
<jvmarg value="${service.http.dump.do}"/>
<classpath>
<path refid="wsit.classpath"/>
<pathelement location="${build.classes.home}"/>
</classpath>
<arg value="${service.main.arg1}"/>
<arg value="${service.main.arg2}"/>
</java>
</target>





The service-publish target runs service.main and passes it jvmargs and
main args. The values for these variables are specified in the build.properties file in the
from-java-publish directory as follows:



   address=http://localhost:8888/echo
service.wsgen.sei=foo.service.Echo
service.main=common.GenericPublisher
service.main.arg1=${address}
service.main.arg2=${service.wsgen.sei}




Notice that the service.main class is GenericPublisher. Most of the code to publish an endpoint
is the same for the various projects in this example. Because of this, I use a GenericPublisher
to do the publishing. I share this class, using symbolic links, will all the projects that publish. The GenericPublisher
is given the address at which to publish the service and the SEI to be published. The class uses reflection to instantiate the
SEI.

Here are the contents of the GenericPublisher class:



   package common;
import javax.xml.ws.Endpoint;
public class GenericPublisher {
public static void main(String[] av) {
final String address = av[0];
final String serviceImplementationBeanClass = av[1];
try {
final Object serviceImplementationBean =
Class.forName(serviceImplementationBeanClass).newInstance();
Endpoint.publish(address, serviceImplementationBean);
} catch (Throwable t) {
t.printStackTrace();
}
}
}




The steps to run the client are identical to those in the other projects in this example.

Building a Service From a WSDL File and Publishing It

Change to the from-java-publish directory:



   cd examples/from-wsdl-publish




The .README file shows the ant tasks to build and publish the service. Here is the initial task:



   ant service-from-wsdl-publish &




Here is the definition of the service-from-wsdl-publish target in the build.xml file:



   <target name="service-from-wsdl-publish"

depends="clean, setup, service-wsimport, service-compile, service-cp-config, service-publish"/>




The first steps in this process are similar to the service-from-java-container. The main differences are
the last steps. Instead of creating a WAR file and deploying it in a container, this process publishes the endpoint
using the GenericPublisher, as specified in the following build.properties file settings:



   tools.properties=build-common/tools.properties
container.properties=build-common/no-container.properties
service.wsimport.wsdl=etc/AddNumbers.wsdl
service.wsimport.package=fromwsdl.service
address=http://localhost:8080/fromwsdl/addnumbers
service.main=common.GenericPublisher
service.main.arg1=${address}
service.main.arg2=fromwsdl.service.AddNumbersImpl
client.wsimport.wsdl=${address}?wsdl
client.wsimport.package=fromwsdl.client
client.main=fromwsdl.client.AddNumbersClient
client.http.dump=true




Again, the client steps are identical.

Summary

This Tech Tip showed a build.xml file that can be used with Metro to create a web service from
a Java class or from a WSDL file and deploy the service in a web container or standalone using the
Endpoint.publish method.

It is also possible to create dynamic clients using the JAX-WS
Dispatch API
and dynamic services using the JAX-WS
Provider API.
Much of the ant build file described in this tip should work for those cases, but I have not tested it or extended it.

I also use NetBeans in combination with
GlassFish to create and deploy Metro-based services
and clients. But it is often useful to have some test cases handy that can be used quickly from the command
line in different configurations. And that's the value of the ant build file covered in this tip.

Further Reading

For more information, see the following resources:


About the Author

Harold Carr is the engineering lead for enterprise web services interoperability at Sun Microsystems. Previous to this role,
Harold was responsible for RMI-IIOP load-balancing and fail-over in the Sun Java System Application Server. He designed the
core architecture used in Sun's CORBA ORB and in the JAX-RPC 2.0 reference implementation and the scalable socket communications
architecture used in SJSAS HTTP and IIOP remoting. Harold helped write the OMG Portable Object Adapter specification and was
chairperson of the OMG Portable Interceptor specification. Previous to Sun, he did distributed computing research at
Hewlett-Packard Research Laboratories and Schlumberger Research Laboratories, was Chief Architect of Visual Lisp technology
at Autodesk, and was a logic simulation consultant for Cirrus Logic. He holds a Ph.D., in Computer Science from the University
of Utah. Read Harold Carr's blog.

Join the discussion

Comments ( 2 )
  • Payday Loan Thursday, August 13, 2009

    Great work , thanks, but it all looks real complicated at this time of the day :-))


  • externe festplatte Tuesday, November 10, 2009

    Hi,

    I first time get to know about the metro and its benefits.I find it very useful for the web-developers.


Please enter your name.Please provide a valid email address.Please enter a comment.CAPTCHA challenge response provided was incorrect. Please try again.