jeudi 18 septembre 2008

Begining with WebServices on Glassfish

Welcome to my Java Technologies Tech Tips.
Here you'll get tips on using enterprise Java technologies and APIs, such as those in Java 2 Platform, Enterprise Edition (J2EE) and Java Platform, Enterprise Edition 5 (Java EE 5).

This post covers:
* Developing Web Services using JAX-WS for beginers
* Generating the associated wsdl
* Writing a standalone client

These tips were developed using an open source implementation of Java EE 5 called GlassFish.

Developing a simple Web Service

To build the web service :
* Create a Java project that integrates your application server libraries ( for me the javaee.jar under my glassfish library path)
* Write an endpoint implementation class under your project.
* Compile the endpoint implementation class.
* Optionally generate portable artifacts required for web service execution.
* Package the web service as a WAR or JAR file and deploy it.


Writing the end point

First of all, under my IDE, i create a package where i will write my web service.For my sample, i decide to create the test.example1 package.
Under it i create a class named PeopleManager.java that will publish a sample method with simple type arguments. See the code here under :

package test.example1;

import javax.ejb.Stateless;
import javax.jws.Oneway;
import javax.jws.WebMethod;
import javax.jws.WebParam;
import javax.jws.WebService;

@WebService( name="PeopleManager",
serviceName="PeopleManager",
targetNamespace="http://techtip.com/samples/example1"
)

@Stateless

public class PeopleManager
{
@WebMethod( operationName="createPerson")
@Oneway
public void createPerson ( @WebParam(name="firstName") String firstName,
@WebParam(name="lastName") String lastName,
@WebParam(name="age") int age
)
{
System.out.println ( "Creating person [firstName=" + firstName
+ "; lastName=" + lastName + ";age=" + age + "]"
);
}
}

As you can see, the class is annotated @WebService and specify the name of the service and the namespace to use. The class is also annotated @Stateless but this is not important for the sample, it is just because i would like to deploy the web service through a jar instead of a WAR archive.
The class provide one method annotated @WebMethod and parameters annotated @WebParam

Compile the class

For that, you can use your IDE build functionnality or for the purist the javac command line (don't forget to reference the javaee.jar in your classpath).

Build your jar

Once your class is compiled, build a jar that contains the class using your favourite IDE or for the purist using the jar command line.

Deploy your jar
Start your application server using the asadmin start-domain command line and use the graphical interface to depoy your archive.
If all is ok, you can see your jar deployed under the item 'Applications/EJB Modules' of the interface and also the PeopleManager Web Service under the item 'Web Services'.
The wsdl of the service could be accessed using your navigator at http://localhost:8080/PeopleManager/PeopleManager?wsdl
The xsd could be reached at
http://localhost:8080/PeopleManager/PeopleManager?xsd=1

Building the wsdl manually
Some times it is necessary to build the wsdl in order to provide the service decription to a client program. In this way the client classes could be generated without connection to the server hosting the web service.
To generate these artifacts you can use the wsgen program privided with your jdk or your application server.
The wsgen.bat file is located in the GLASSFISH_HOME/bin directory.
As it is not easy to build the wsdl manually, you can use the following ant script that is very usefull :

<project name="ws-gen" default="j2wsdl" basedir=".">
<property name="appserver.home" value="D:\glassfish-v2ur1" />
<property name="project.home" value="D:\Documents and Settings\fmosse\workspace\TestWebService" />
<property name="build.home" value="${project.home}\classes" />
<property name="wsdl.home" value="${project.home}\dist\wsdl" />
<property name="srcgen.home" value="${project.home}\dist\srcgen\server" />

<!-- setup Metro tooltime classpath -->
<path id="tool.cp">
<pathelement location="${appserver.home}/lib/webservices-tools.jar" />
</path>

<path id="jee.cp">
<pathelement location="${appserver.home}/lib/toplink-essentials.jar" />
<pathelement location="${appserver.home}/lib/javaee.jar" />
</path>

<!-- setup build classpath -->
<path id="build.cp">
<pathelement location="${build.home}" />
</path>

<path id="module.cp">
<dirset dir="../">
<include name="**/bin"/>
</dirset>
</path>

<!-- Setup Wsgen ant task. You would use this task in Java to WSDL case to generate a WSDL or wrapper classes. -->
<taskdef name="wsgen" classname="com.sun.tools.ws.ant.WsGen">
<classpath refid="tool.cp" />
</taskdef>

<target name="setup">
<mkdir dir="${build.home}"/>
<mkdir dir="${wsdl.home}"/>
<mkdir dir="${srcgen.home}"/>
</target>

<target name="j2wsdl" depends="setup">
<wsgen sei="test.example1.PeopleManager"
genwsdl="true"
resourcedestdir="${wsdl.home}"
sourcedestdir="${srcgen.home}"
destdir="${build.home}"
extension="true"
keep="false"
verbose="true">
<classpath refid="build.cp"/>
<classpath refid="module.cp"/>
<classpath refid="tool.cp"/>
<classpath refid="jee.cp"/>
</wsgen>
</target>

<target name="clean">
<delete dir="${build.home}" />
</target>

</project>


The appserver.home variable contains the location of the application server ( for me "D:\glassfish-v2ur1").
The project.home variable contains the location of your project
The build.home variable contains the directory where are generated your classes (for me "${project.home}\classes")
The wsdl.home contains the location of the wsdl files generated by wsgen (for me "${project.home}\dist\wsdl")
The srcgen.home variable contains the directory where wsgen can write its java classes (
${project.home}\dist\srcgen\server)

You can use also the command line :
wsgen -classpath "D:\Documents and Settings\fmosse\workspace\TestWebService\classes;D:\glassfish-
v2ur1\lib\webservices-tools.jar;D:\glassfish-v2ur1\lib\toplink-essentials.jar;D:\glassfish-v2ur1\lib\javaee.jar" -d "D:\
Documents and Settings\fmosse\workspace\TestWebService\classes" -extension -wsdl -r "D:\Documents and Settings\fmosse\wo
rkspace\TestWebService\dist\wsdl" -s "D:\Documents and Settings\fmosse\workspace\TestWebService\dist\srcgen\server" -ver
bose test.example1.PeopleManager

Two files are generated by wsgen under ${project.home}\dist\wsdl directory :
* PeopleManager.wsdl
* PoepleManager_schema1.xsd


PeopleManager.wsdl

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<!-- Generated by JAX-WS RI at http://jax-ws.dev.java.net. RI's version is JAX-WS RI 2.1.2_01-hudson-189-. -->
<definitions targetNamespace="http://techtip.com/samples/example1" name="PeopleManager" xmlns="http://schemas.xmlsoap.org/wsdl/" xmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy" xmlns:tns="http://techtip.com/samples/example1" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
<wsp:UsingPolicy/>
<wsp:Policy wsu:Id="PeopleManagerPortBinding_createPerson_WSAT_Policy">
<wsp:ExactlyOne>
<wsp:All>
<ns1:ATAlwaysCapability wsp:Optional="false" xmlns:ns1="http://schemas.xmlsoap.org/ws/2004/10/wsat"/>
<ns2:ATAssertion ns3:Optional="true" wsp:Optional="true" xmlns:ns2="http://schemas.xmlsoap.org/ws/2004/10/wsat" xmlns:ns3="http://schemas.xmlsoap.org/ws/2002/12/policy"/>
</wsp:All>
</wsp:ExactlyOne>
</wsp:Policy>
<types>
<xsd:schema>
<xsd:import namespace="http://techtip.com/samples/example1" schemaLocation="PeopleManager_schema1.xsd"/>
</xsd:schema>
</types>
<message name="createPerson">
<part name="parameters" element="tns:createPerson"/>
</message>
<portType name="PeopleManager">
<operation name="createPerson">
<input message="tns:createPerson"/>
</operation>
</portType>
<binding name="PeopleManagerPortBinding" type="tns:PeopleManager">
<soap:binding transport="http://schemas.xmlsoap.org/soap/http" style="document"/>
<operation name="createPerson">
<wsp:PolicyReference URI="#PeopleManagerPortBinding_createPerson_WSAT_Policy"/>
<soap:operation soapAction=""/>
<input>
<soap:body use="literal"/>
</input>
</operation>
</binding>
<service name="PeopleManager">
<port name="PeopleManagerPort" binding="tns:PeopleManagerPortBinding">
<soap:address location="REPLACE_WITH_ACTUAL_URL"/>
</port>
</service>
</definitions>





PoepleManager_schema1.xsd

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<xs:schema version="1.0" targetNamespace="http://techtip.com/samples/example1" xmlns:tns="http://techtip.com/samples/example1" xmlns:xs="http://www.w3.org/2001/XMLSchema">

<xs:element name="createPerson" type="tns:createPerson"/>

<xs:complexType name="createPerson">
<xs:sequence>
<xs:element name="firstName" type="xs:string" minOccurs="0"/>
<xs:element name="lastName" type="xs:string" minOccurs="0"/>
<xs:element name="age" type="xs:int"/>
</xs:sequence>
</xs:complexType>
</xs:schema>