Affichage des articles dont le libellé est Weblogic. Afficher tous les articles
Affichage des articles dont le libellé est Weblogic. Afficher tous les articles

mercredi 9 décembre 2009

Memo : Weblogic JDBC Persistent Store

Script to use for table creation of a JDBC persistent store :

CREATE TABLE TABLE_NAME (
id int not null primary key,
type int not null,
handle int not null,
record blob not null
);

The table name is computed with the prefix name defined at persistent store configuration with the appended string WLSTORE. So for exemple if prefix name is MYAPPLI, the table name will be MYAPPLIWLSTORE.

The command line to create the table could be :

java utils.Schema url JDBC_driver [options] DDL_file


options could be :
  • -u for database connection username
  • -p for the user password
  • -s for the database SID


You will find hereunder is an example of the installation command line :

java utils.Schema jdbc:weblogic:oracle:DEMO weblogic.jdbc.oci.Driver -u user1 -p password1 -verbose createTab.ddl

Command line to use the Jconsole to connect on Weblogic 10.3.1

This is the command line that is usefull for the connection of the jconsole on a Weblogic server 10.3.1

%JAVA_HOME%\bin\jconsole -J-Djava.class.path=%JAVA_HOME%\lib\jconsole.jar;%JAVA_HOME%\lib\tools.jar;%BEA_HOME%\server\lib\wljmxclient.jar -J-Djmx.remote.protocole.provider.pkgs=weblogic.management.remote -J-Dcom.sun.management.jmxremote

where JAVA_HOME references the jdk and BEA_HOME the installation directory of the weblogic server

mercredi 18 mars 2009

Dynamic web service invocation

The main goal of this section is to show you how to invoke a web service using the dynamic web service invocation api (DII).

There is two ways to invoke the web service dynamically :
  • Using the Jax-Rpc's dynamic call api (jax.xml.rpc.Call)
  • Using the Jax-Ws dynamic Dispatch api

First of all, you will find here under the code of a simple web service we will use in the sample :

package test;

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

@Stateless
@WebService(name="DynamicWs",
serviceName="DynamicWsService",
portName="DynamicWsPort",
targetNamespace="http://test"
)
public class DynamicWs {
@WebMethod(operationName="hello")
@WebResult(name="result",
targetNamespace="http://test")
public String hello( @WebParam(name="name", targetNamespace="http://test") String name)
{
return "hello " + name;
}
}

This sample exposes the hello method which receives a string in parameter and returns a string in response to the client.


Dynamic invocation using jax.xml.rpc.Call api

The client code for dynamic method invocation using the rpc Call api :

package client;

import java.rmi.RemoteException;

import javax.xml.namespace.QName;
import javax.xml.rpc.Call;
import javax.xml.rpc.ParameterMode;
import javax.xml.rpc.Service;
import javax.xml.rpc.ServiceException;
import javax.xml.rpc.ServiceFactory;
import javax.xml.rpc.encoding.XMLType;

import org.junit.Test;


public class TestDynamicWs {
private static final String END_POINT = "http://localhost:8080/DynamicWsService/DynamicWs";
private static final String TNS = "http://test";

@Test
public void testDynamicInvocation() throws ServiceException, RemoteException
{
Service service = ServiceFactory.newInstance().createService (
new QName( TNS, "DynamicWsService")
);
Call call = service.createCall ();
call.setTargetEndpointAddress ( END_POINT);
// Set call properties
call.setProperty(Call.SOAPACTION_USE_PROPERTY, new Boolean(true));
call.setProperty(Call.SOAPACTION_URI_PROPERTY, "");
call.setProperty(Call.ENCODINGSTYLE_URI_PROPERTY , "");
call.setProperty(Call.OPERATION_STYLE_PROPERTY, "rpc");

// Set the name of the operation to call
call.setOperationName( new QName( TNS, "hello"));
// Define and add the parameter value
call.addParameter(
"name", // parameter name
XMLType.XSD_STRING, // parameter XML type QName
String.class, // parameter Java type class
ParameterMode.IN); // parameter mode

// Define the type of the result returned by the method
call.setReturnType(XMLType.XSD_STRING);

// Call the method with parameters
String response = (String) call.invoke(new String[] {"franck"});

System.out.println ("Response is '" + response + "'");
}
}


Fist of all, we have to create the service using the javax.xml.rpc.ServiceFactory and instanciate the javax.xml.rpc.Call object.
This Call object will be configured using some properties :
  • Call.SOAPACTION_USE_PROPERTY : this property indicates whether or not SOAPAction is to be used. We set it to TRUE.
  • Call.OPERATION_STYLE_PROPERTY : this property is set to "rpc" to indicates that the operation will be accessed by rpc.
Once configured, we defined the operation to invoke
call.setOperationName( new QName( TNS, "hello"));


and the type of input and output parameters
        // Define and add the parameter value
call.addParameter(
"name", // parameter name
XMLType.XSD_STRING, // parameter XML type QName
String.class, // parameter Java type class
ParameterMode.IN); // parameter mode

// Define the type of the result returned by the method
call.setReturnType(XMLType.XSD_STRING);

The XMLType class contains all QName that defines the type of parameters we could use.

Now, it's time to invoke the remote method using rpc call and encoding
        // Call the method with parameters
String response = (String) call.invoke(new String[] {"franck"});


Remark : Just note that the client never recovered the wsdl file on the server !

The message that has been sent by the client code :

<env:envelope enc="http://schemas.xmlsoap.org/soap/encoding/" env="http://schemas.xmlsoap.org/soap/envelope/" ns0="http://test" xsd="http://www.w3.org/2001/XMLSchema" xsi="http://www.w3.org/2001/XMLSchema-instance">
<env:body>
<ns0:hello>
<name>franck</name>
</ns0:hello>
</env:body>
</env:envelope>


The response received by the client :

<s:envelope s="http://schemas.xmlsoap.org/soap/envelope/">
<s:body>
<helloresponse xmlns="http://test">
<result>hello franck</result>
</helloresponse>
</s:body>
</s:envelope>


Dynamic call using the jax-ws dynamic Dispatch api
In this level we will the Dispatch api of the jax-ws api to create a SOAP envelop and invoke the web service.
You will find here under the source code to invoke the hello method through the Dispatch api :

package client;

import java.net.URL;
import java.util.Iterator;

import javax.xml.namespace.QName;
import javax.xml.soap.MessageFactory;
import javax.xml.soap.SOAPBody;
import javax.xml.soap.SOAPConstants;
import javax.xml.soap.SOAPElement;
import javax.xml.soap.SOAPEnvelope;
import javax.xml.soap.SOAPHeader;
import javax.xml.soap.SOAPMessage;
import javax.xml.soap.SOAPPart;
import javax.xml.soap.Text;
import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.stream.StreamResult;
import javax.xml.ws.Dispatch;
import javax.xml.ws.Service;

import org.junit.Test;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

public class TestDynamicWs2 {
private static final String TNS = "http://test";
private static final String WSDL = "http://localhost:8080/DynamicWsService/DynamicWs?wsdl";

@Test
public void testDynamicInvocation() throws Exception
{
/* wsdl url you can get from link provided by glassfish server.*/
URL url = new URL( WSDL);

/* service name & port name as mentioned in wsdl, which can also be viewed in glassfish */
QName serviceName = new QName( TNS, "DynamicWsService");
QName portName = new QName( TNS, "DynamicWsPort");
Service service = Service.create(url, serviceName);

/** Create a Dispatch instance from a service.**/
Dispatch dispatch = service.createDispatch( portName,
SOAPMessage.class,
Service.Mode.MESSAGE
);

MessageFactory mf;
try {
mf = MessageFactory.newInstance(SOAPConstants.DEFAULT_SOAP_PROTOCOL);

// Create a message. This example works with the SOAPPART.
SOAPMessage req = mf.createMessage();
SOAPPart part = req.getSOAPPart();

// Obtain the SOAPEnvelope and header and body elements.
SOAPEnvelope env = part.getEnvelope();
SOAPHeader header = env.getHeader();
SOAPBody body = env.getBody();

// Construct the message payload.
SOAPElement operation = body.addChildElement( "hello", "ns", TNS);
SOAPElement value = operation.addChildElement( "name", "ns", TNS);
value.addTextNode( "franck");
req.saveChanges();

// Invoke the service using the soap request
SOAPMessage res = (SOAPMessage)dispatch.invoke(req);

SOAPPart soapPart = res.getSOAPPart();
NodeList list = soapPart.getElementsByTagNameNS (TNS, "result");
// Read the first node
Node node = list.item ( 0);

// Display the result
System.out.println ( "Server say : " + node.getTextContent ());

} catch (Exception ex) {
ex.printStackTrace ();
throw ex;
}

}


First of all, we have to connect to server to retrieve the wsdl file and the port associated to the service :

/* wsdl url you can get from link provided by glassfish server.*/
URL url = new URL( WSDL);

/* service name & port name as mentioned in wsdl, which can also be viewed in glassfish */
QName serviceName = new QName( TNS, "DynamicWsService");
QName portName = new QName( TNS, "DynamicWsPort");
Service service = Service.create(url, serviceName);

The wsdl file will contains the end point definition to find the right service deployed on the right server.

After we create a Dispatch object on the port and indicates that we want to deal with soap message :

/** Create a Dispatch instance from a service.**/
Dispatch dispatch = service.createDispatch( portName,
SOAPMessage.class,
Service.Mode.MESSAGE
);


It is now the time to create a soap message and fill it with invocation parameters :

MessageFactory mf = MessageFactory.newInstance(SOAPConstants.DEFAULT_SOAP_PROTOCOL);

// Create a message. This example works with the SOAPPART.
SOAPMessage req = mf.createMessage();


The api used to set attributs in the soap envelop are very similar to the DOM api used in xml parsing.

SOAPElement operation = body.addChildElement( "hello", "ns", TNS);
SOAPElement value = operation.addChildElement( "name", "ns", TNS);
value.addTextNode( "franck");
req.saveChanges(); // Save the changes made on the soap envelop


Now invoke you service :
            // Invoke the service using the soap request
SOAPMessage res = (SOAPMessage)dispatch.invoke(req);


And retrieve your result :

SOAPPart soapPart = res.getSOAPPart();
NodeList list = soapPart.getElementsByTagNameNS (TNS, "result");
// Parse the node list here...

lundi 6 octobre 2008

How to deal with Pessimistic Locking

There are severals way to perform pessimistic locking :
  1. Using a direct locking mechanism in database (vendor specific)
  2. Using query hints defined statically in named meta data (vendor specific)
  3. Using a Query api that defines the lock programatically (vendor specific)
  4. Using vendor implementation artifacts (vendor specific)
  5. Using the optimistic lock mechanism of JPA
Database locking mechanism
Database lock mechanism is vendor specific but it is very efficient.
For Oracle database, the lock is performed while doing a 'select for update' on a row of a database table.
See here under to get a sample that explains how to lock an entity:

public MyEntity lock( long id)
{
Query query = entityManager.createNativeQuery("select e.* from myentity e where id=?1 for update");
query.setParameter(1, id);
MyEntity found = null;
try {
found = query.getSingleResult();
} catch(EntityNotFoundException e) {} catch(NoResultException e) {}
if(found != null) entityManager.refresh(found);
return found;
}


The lock is acquired until the calling transaction commits or rollbacks.
If a transaction acquires the lock, all others transactions will be blocked on the query.getSingleResult() until the first one exists its transaction.
This mechanism is database specific and is not portable if you consider changing your database provider.

Query hints
Query hints are vendor directives that may be defined statically in named query metadata (annotations or XML).
They are applied at execution time while executing or flushing the query.
See here under to get a sample :


@NamedQuery(name=“MyEntity.loadAndLock”,
query=“SELECT e FROM MyEntity e WHERE e.id = :id”,
hints={
@QueryHint( name=“toplink.pessimistic-lock”, value=“Lock”),
@QueryHint( name=“openjpa.ReadLockLevel”,value=“write”)
}
)

As you can see the toplink.pessimistic-lock hint is dedicated to the TopLink midleware and could not be used in an other provider like Hibernate.
This source code is not portable to an other environment.

Dynamic query hints
Some time you may want to lock dynamically a query using the Query API.
This is more flexible because the lock could be applied on any object and any query without a static definition.

Query query = em.createQuery( “SELECT e FROM MyEntity e WHERE e.id = :id”);
query.setHint( “toplink.pessimistic-lock”,“Lock”);
query.setHint( “openjpa.ReadLockLevel” “write”);
query.setParameter(“id”, objectId)
query.getResultList();



Vendor artifacts
There is an other way to perform a lock using the specific API of the database mapping midleware. For example with Toplink you can write :


public MyEntity loadAndLock( int id)
{
MyEntity e = entityManager.find( MyEntity.class, id);

UnitOfWork uow = (TopLinkEntityManager)entityManager.getUnitOfWork();
uow.refreshAndLockObject(e, LOCK);
return e;
}


you can write also the following source code :

public MyEntity loadAndLock(int id)
{
Query q = entityManager.createQuery( “SELECT e FROM MyEntity e WHERE e.id = :id”);
q.setParameter( “id”, id);
((ObjectLevelReadQuery)((TopLinkQuery)q.getDatabaseQuery())).acquireLocks();
return q.getSingleResult();
}


As you can see, the code is specific to the supplier of the persistence layer and can not in any way be related to a different environment


Lock using JPA feature
At the moment JPA supports only Optimistic Locking but does not support the pessimistic locking mechanism. But if you are familiar with the lock feature of the entity manager you will realize that it is possible to use the traditional lock method to perform a pessimistic locking.

The following source code do almost the same thing as the 'select for update' in oracle database but is portable whatever the environment :

MyEntity entity = entityManager.find( MyEntity.class, id);
entityManager.lock ( entity, LockModeType.WRITE);
entityManager.flush ();


The use of the lock mechanism at entity involves the use of a field-level version on this entity :

@entity
public class Myentity
{
@Id
private Integer id;

@Version
private int version;

...

public int getVersion () {
return version;
}

public void setVersion (int version) {
this.version = version;
}
}


What's happening while locking ?
The first thread that acquires the lock is not blocked but a second one that trys to access the same entity is blocked on the flush() method.
When the first thread commits or rollbacks, the blocked one receive a javax.persistence.OptimisticLockException. This mechanism ensure that the second thread does not take into account data that would have been affected by the first one.
As a result, the source code must be aware that an exception can be lifted and must implement a mechanism to retry.

Note that when the flush() throw an exception, the current transaction is marked for rollback and can not be used for transactional purposes. In this way each retry must be performed using a new transaction.

mardi 23 septembre 2008

How to deal with abstract classes in WS signature

Dealing with concrete classes in web service signature is very easy but what is happening when dealing with abstract classes ? It doesn't work ! Really ? Take a look at this sample...

In the following example, i will try to call a web service method that referes in its signature an abstract class.
I developpe the Person class where i inherit two concrete classes: Employee and Manager. I create after a web service where i declare a methode to create both an Employee or a Manager : public void createPerson( Person aPerson);
If you try this without annotation (except for @WebService, @WebMethod and @WebParam) you will have an exception some time when generating the wsdl file, some time when you try to use it from a client.

To make them work, you need to help the ws generator with binding annotation (JAXB annototion). I give you the solution :

Defining your object model
for the abstract class

package test.example5.model;

import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlType;

@XmlType( name="Person")
public abstract class Person {
@XmlAttribute(name="firstName", required=true)
private String firstName;
@XmlAttribute(name="lastName", required=true)
private String lastName;

public Person() {}

public String getFirstName () {
return firstName;
}

public void setFirstName (String firstName) {
this.firstName = firstName;
}

public String getLastName () {
return lastName;
}

public void setLastName (String lastName) {
this.lastName = lastName;
}
}


For the concrete one :

package test.example5.model;

import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;

@XmlRootElement( name="Employee")
@XmlType( name="Employee")
public class Employee extends Person {
@XmlAttribute( name="managerName", required= false)
private String managerName;

public Employee()
{
super();
}

public String getManagerName () {
return managerName;
}

public void setManagerName (String managerName) {
this.managerName = managerName;
}
}


package test.example5.model;

import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;

@XmlRootElement( name="Manager")
@XmlType( name="Manager")
public class Manager extends Person {
@XmlAttribute( name="teamName", required=false)
private String teamName;

public Manager()
{
super();
}

public String getTeamName () {
return teamName;
}

public void setTeamName (String teamName) {
this.teamName = teamName;
}
}


Now you need to create yourself an object factory that will be used to generate all these classes

package test.example5.model;

import javax.xml.bind.JAXBElement;
import javax.xml.bind.annotation.XmlElementDecl;
import javax.xml.bind.annotation.XmlRegistry;
import javax.xml.namespace.QName;

@XmlRegistry
public class ObjectFactory {
//--------------------------------------------------------------------------
// Static members
//--------------------------------------------------------------------------

private final static QName _Person_QNAME = new QName("http://model.example5.test/", "Person");
private final static QName _Employee_QNAME = new QName("http://model.example5.test/", "Employee");
private final static QName _Manager_QNAME = new QName("http://model.example5.test/", "Manager");

//-------------------------------------------------------------------------
// Constructor
//-------------------------------------------------------------------------

/**
* Create a new ObjectFactory that can be used to create new instances
*
*/
public ObjectFactory() {
}

/**
* Create an instance of {@link JAXBElement }{@code <}{@link Person}{@code >}}
*
*/
@XmlElementDecl(namespace = "http://model.example5.test/", name = "Person")
public JAXBElement createPerson(Person value) {
return new JAXBElement(_Person_QNAME, Person.class, null, value);
}

/**
* Create an instance of {@link Employee }
*
*/
public Employee createEmployee() {
return new Employee();
}


/**
* Create an instance of {@link JAXBElement }{@code <}{@link Employee}{@code >}}
*
*/
@XmlElementDecl(namespace = "http://model.example5.test/", name = "Employee")
public JAXBElement createEmployee(Employee value) {
return new JAXBElement(_Employee_QNAME, Employee.class, null, value);
}

/**
* Create an instance of {@link Manager }
*
*/
public Manager createManager() {
return new Manager();
}


/**
* Create an instance of {@link JAXBElement }{@code <}{@link Manager}{@code >}}
*
*/
@XmlElementDecl(namespace = "http://model.example5.test/", name = "Manager")
public JAXBElement createManager(Manager value) {
return new JAXBElement(_Manager_QNAME, Manager.class, null, value);
}
}
The @XmlRegistry declares that the class contains the methods use to map your data model object to JAXBElement use to serialize your objects in an XML form. for more information see http://java.sun.com/javase/6/docs/api/javax/xml/bind/annotation/XmlElementDecl.html.

Note also the difference between the factory methods of the abstract classe and one for the concrete classes. in this class, you will see how is manage the binding for all object managed by web services.

Now you need to create a specific class called package-info.java :

@javax.xml.bind.annotation.XmlSchema(namespace = "http://model.example5.test/")
@XmlAccessorType(XmlAccessType.NONE)
package test.example5.model;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;


To resume, in your model package you will have the following classes:
* Person.java
* Employee.java
* Manager.java
* ObjectFactory.java
* package-info.java


Defining your web service
Now, you can code your web service :

package test.example5.service;

import javax.ejb.Stateless;
import javax.jws.Oneway;
import javax.jws.WebMethod;
import javax.jws.WebService;
import javax.xml.bind.annotation.XmlSeeAlso;

import test.example5.model.Employee;
import test.example5.model.Manager;
import test.example5.model.Person;

@WebService( name="TestExample5", serviceName="TestExample5", targetNamespace="http://techtip.com/samples/example5")
@XmlSeeAlso({
test.example5.model.ObjectFactory.class
})

@Stateless
public class TestExample5 {

@WebMethod( operationName="createPerson")
@Oneway
public void createPerson( Person person)
{
if( person instanceof Employee)
{
System.out.println( "Creating Employee = [firstName=" + person.getFirstName ()
+ ";lastName=" + person.getLastName ()
+ ";managerName=" + ((Employee)person).getManagerName () + "]"
);
} else if( person instanceof Manager)
{
System.out.println( "Creating Manager = [firstName=" + person.getFirstName ()
+ ";lastName=" + person.getLastName ()
+ ";teamName=" + ((Manager)person).getTeamName () + "]"
);
} else {
System.out.println ( "Person not managed");
}
}

}

The @XmlSeeAlso annotation is very important, it provides the class that is in change of the binding of the objects manage in web service signature (the ObjectFactory of your data model).
If you have several packages because you have several data model, you can declare all the Object factory in the @XmlSeeAlso.

So use wsgen to generate the wsdl and associated files, you will have :
* TestExample5.wsdl
* TestExample5_schema1.xsd
* TestExample5_schema2.xsd

This is the content of the TestExample5.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-hudson-182-RC1. -->
<definitions targetNamespace="http://techtip.com/samples/example5" name="TestExample5" xmlns="http://schemas.xmlsoap.org/wsdl/" xmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy" xmlns:tns="http://techtip.com/samples/example5" 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="TestExample5PortBinding_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/example5" schemaLocation="TestExample5_schema1.xsd"/>
</xsd:schema>
<xsd:schema>
<xsd:import namespace="http://model.example5.test/" schemaLocation="TestExample5_schema2.xsd"/>
</xsd:schema>
</types>
<message name="createPerson">
<part name="parameters" element="tns:createPerson"/>
</message>
<portType name="TestExample5">
<operation name="createPerson">
<input message="tns:createPerson"/>
</operation>
</portType>
<binding name="TestExample5PortBinding" type="tns:TestExample5">
<soap:binding transport="http://schemas.xmlsoap.org/soap/http" style="document"/>
<operation name="createPerson">
<wsp:PolicyReference URI="#TestExample5PortBinding_createPerson_WSAT_Policy"/>
<soap:operation soapAction=""/>
<input>
<soap:body use="literal"/>
</input>
</operation>
</binding>
<service name="TestExample5">
<port name="TestExample5Port" binding="tns:TestExample5PortBinding">
<soap:address location="REPLACE_WITH_ACTUAL_URL"/>
</port>
</service>
</definitions>


For the TestExample5_schema1.xsd:

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

<xs:import namespace="http://model.example5.test/" schemaLocation="TestExample5_schema2.xsd"/>

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

<xs:complexType name="createPerson">
<xs:sequence>
<xs:element name="arg0" type="ns1:Person" minOccurs="0"/>
</xs:sequence>
</xs:complexType>
</xs:schema>


For the TestExample5_schema2.xsd:

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

<xs:element name="Employee" nillable="true" type="tns:Employee"/>

<xs:element name="Manager" nillable="true" type="tns:Manager"/>

<xs:element name="Person" nillable="true" type="tns:Person"/>

<xs:complexType name="Person" abstract="true">
<xs:sequence/>
<xs:attribute name="firstName" type="xs:string" use="required"/>
<xs:attribute name="lastName" type="xs:string" use="required"/>
</xs:complexType>

<xs:complexType name="Employee">
<xs:complexContent>
<xs:extension base="tns:Person">
<xs:sequence/>
<xs:attribute name="managerName" type="xs:string"/>
</xs:extension>
</xs:complexContent>
</xs:complexType>

<xs:complexType name="Manager">
<xs:complexContent>
<xs:extension base="tns:Person">
<xs:sequence/>
<xs:attribute name="teamName" type="xs:string"/>
</xs:extension>
</xs:complexContent>
</xs:complexType>
</xs:schema>


You will see that with all the binding annotations, wsgen generates the Person, Employee and Manager types used by our web service.

The WS client
This is the client code that tests Employee and Manager creation :

package test.example5;

import java.net.MalformedURLException;
import java.net.URL;

import javax.xml.namespace.QName;

import org.junit.BeforeClass;
import org.junit.Test;

import test.example5.generated.Employee;
import test.example5.generated.Manager;


public class TestExample5 {
private static test.example5.generated.TestExample5 testManager;
private static final String HOST_NAME = "localhost";
private static final String PORT_NUMBER = "8081";
private static final String TARGET_NAMESPACE = "http://techtip.com/samples/example5";

@BeforeClass
public static void beforeClass () throws MalformedURLException
{
String wsdlUrl = "http://" + HOST_NAME + ":" + PORT_NUMBER + "/TestExample5/TestExample5?wsdl";
URL wsdlLocation = new URL( wsdlUrl);
QName serviceName = new QName(
TARGET_NAMESPACE,
"TestExample5"
);

test.example5.generated.TestExample5_Service service = new test.example5.generated.TestExample5_Service(
wsdlLocation, serviceName
);

testManager = service.getTestExample5Port ();

}

@Test
public void testCreatePerson() throws Exception {
Employee employee = new Employee();
employee.setFirstName ( "Franck");
employee.setLastName ( "Mosse");
employee.setManagerName ( "BigBoss");

testManager.createPerson ( employee);

Manager manager = new Manager();
manager.setFirstName ( "Martin");
manager.setLastName ( "Dupond");
manager.setTeamName ( "IT");

testManager.createPerson ( manager);
}

}

How to use a wsdl file locally from a client code

From a WS client point of view, it would be interesting to retrieve a wsdl file locally instead of calling a remote web server. This method garantee that if the server add some new feature in the WS port you use, you do not have to rebuild your client code.

To do so, you need to generate your stub specifiying a local wsdl file. In this way, the path to access the wsdl file is relative to the path of the package where you generate your stubs.

For example with wsimport you can ask it to generate the stub in the test.example.generated package ( wsimport … -p test.example.generated …) and referes locally your wsdl file (if wsdl files stored in /wsd => wsimport … -p test.example.generated –wsdllocation ../../../wsdl/mywsdlfile.xml).

What is the impact on you client code ?
For example if you try to use a web service called MyHelloWorld and if you generate your stubs in the test.example.generated package, your client code will look like :

test.example.generated.MyHelloWorldService service = new test.example.generated.MyHelloWorldService();
test.example.generated.MyHelloWorld helloPort = service.getMyHelloWorldPort();
BindingProvider provider = (BindingProvider) helloPort;
provider.getRequestContext().put(
BindingProvider.ENDPOINT_ADDRESS_PROPERTY,
"http://localhost:8080/MyHelloWorld/MyHelloWorld"
);


How to package your client
To package you client jar, you need to add :
  • the stubs generated by the wsimport utility
  • the wsdl files and associated xsd,
  • and your client code.
That's all...