Thursday July 17, 2008
Configuring the Sun Java System Mobile Enterprise Platform 1.0 for access to a SAP EIS backend
The Sun Java System Mobile Enterprise Platform 1.0 (MEP) is a comprehensive mobility solution that enables offline data access, data synchronization, and secure access to EIS/EAI applications, such as Siebel and SAP. See announcement here for additional info about MEP. This blog will focus on the steps needed to configure the MEP gateway for access to a SAP EIS backend. Our SAP installation was based on SAP ERP 6.0 SR3 release. The configuration requires 7 easy steps listed below. This configuration setup assumes single-tier installation.
Step 1: Download the SAP Libraries and Install into your Application Server
The SAP Java Connector toolkit (SAP JCo) is a middleware component that enables the Sun JCA Adapter for SAP to communicate directly with SAP. This component is required by the SAP BAPI OTD Wizard, which you use when you develop an Enterprise Connector that accesses a Sun JCA Adapter.
- Log in to SAP site (http://service.sap.com/connectors) using your authorized user ID login as a SAP Licensed Customer
- Click SAP Java Connector.
- In the left-hand menu, click Tools and Services.
- Download the SAP JCo archive file for your operating system.
-
Unzip the archive file into a directory of your choice. Archives are either as(.tgz or .zip) files:
- For UNIX Tar File GZipped '.tgz': 'gunzip' followed by 'tar -xvf'
- After you unzip the archive, you will find a JAR file named sapjco.jar. You will also find some library file(s) with '.so' or '.dll' extension based on your platform.
-
Copy all these files into the Application Server lib directory where your MEP gateway is installed. This directory is AS_HOME/lib, where AS_HOME is the directory where you installed the Sun Java Mobile Enterprise Platform 1.0:
- On Solaris/Linux: cp sapjco.jar *.so $AS_HOME/lib
- On Windows: cp sapjco.jar *.dll %AS_HOME%/lib
Step 2: If you are using a 64–bit JDK, add a JVM setting
NOTE: If you are not using a 64-bit JVM you can skip this step.
- Login to GlassFish admin console (i.e. http://hostname:4848) (where hostname is either the name of the MEP host or localhost). The admin password is the one you specified for the MEP domain during installation.
- In the tree view, select the Application Server node.
- Click the JVM Settings tab, then click the JVM Options sub-tab.
- Click Add JVM Option.
- In the Value field, type -d64.
- Click Save.
- Stop and Restart the Application Server for these changes to take affect
Step 3: Configure a Connector Connection Pool for SAP
In order to configure a SAP Connection, using the SAP Resource Adapter for SAP, a Connector connection pool and a Connector resource needs to be created in the application server. This and the following step show how to do this:
- Login to GlassFish admin console.
- Click on Resources->Connectors->Connector Connection Pools->New
Fill in the connector connection pool Name and select the SAP Resource Adapter as shown in screen shot below and than click Next:
The next screen we accept all the default settings and click Finish to create this connector connection pool:
Step 4: Configure a Connector Resource for the SAP pool above
- Click on Resources->Connectors->Connector Resources->New
Fill in the connector resource JNDI Name and Pool Name as shown in screen shot below and than click OK
Step 5: Configure CAPS connection for SAP
The following SAP Client Connection Settings are necessary for SAP configuration. All other parameters you can accept the defaults. The installation parameters which are specific to your SAP installation are marked 'Yes' in the 3rd column of the table. You will need to obtain the proper values for these installation parameters from your SAP administrator responsible for your SAP installation.
Name Value Installation Parameters
Client Connection Mode Manual No
Application Server Hostname mep-2 Yes
System Number 00 Yes
Client Number 001 Yes
User mep Yes
Password meppass Yes
Language EN Yes
System ID QE1 Yes
To configure these settings do:
- Click on CAPS->Connector Connection Pools->sappool
Step 6: Create a connector Tab for SAP within the MEP gateway admin console and configure proper settings
For the MEP 1.0 release the only pre-configured connectors are MusicDb and Siebel. There is full SAP support within the product but it must be turned on. Follow the procedure below to configure the MEP gateway for the SAP connector:
- Login to MEP gateway admin console(http://hostname:8080/sync/admin). The default user name is admin and password is syncpass.
- Click on Connectors Tab
- In the Local JCR Repositories panel enter SAP in the Name field and click Add
- Click on SAP connector Tab just added to configure property settings
- After adding correct property settings click Save
The following screen shot shows the configuration of the connector tab. The following values partially cutout from screen shot are supplied here:
- Class/JNDI Name -> com.sun.mep.connector.jcr.DbRepository
- business-object-provider -> com.sun.mep.connector.sapeway.customer.CustomerProvider
Step 7: Register a User for SAP to test out your SAP configuration
- Click on Users->Create User Tab within MEP gateway admin console
- Add an Email/User/Password for your SAP User and make sure you choose SAP Enterprise Connector
- Click Register
If SAP is configured correctly your sap user should be created without errors. See the following screen shot for details:
That is all there is to configure MEP gateway for SAP.
Posted at 10:49AM Jul 17, 2008 by artfish in Personal | Comments[1]
Using JAX-WS 2.1 Addressing Features
This techtip will focus on using new features in the Maintenance Release of JAX-WS 2.1 for WS-Addressing support based on the Web Services Addressing 1.0 Core and Web Services Addressing 1.0 SOAP Binding specifications. In this example the server will highlight the use of the javax.xml.ws.soap.Addressing annotation for enabling addressing on the server-side and the use of the javax.xml.ws.Action and javax.xml.ws.FaultAction annotations for explicit association of WS-Addressing Action message addressing properties with input, output and fault messages. The client will highlight the use of javax.xml.ws.WebServiceFeature and javax.xml.ws.soap.AddressingFeature for enabling addressing on the client-side. Both client and server will also show how to obtain an EnpointReference (EPR) for the Endpoint.
The example code below was developed and tested using a GlassFish implementation.
Developing a Web Service to use the new Web Service Addressing features in JAX-WS 2.1
Below is an example of a web service implementation class which uses the Web Service Addressing features. To write a portable endpoint with this version of JAX-WS, an endpoint MUST explicitly specify what WS-Addressing Actions are to be used via the Action and FaultAction annotations. After the W3C WG on WS-Addressing has specified how the use of WS-Addressing is specified in the WSDL, and what the default value must be for Action headers, a future version of JAX-WS will remove these requirements. The annotation @Addressing indicates that this service wants to enable WS-Addressing. The required property is explicitly set to specify that WS-Addressing headers MUST be present on incoming messages. At runtime, WS-Addressing headers MUST be consumed by the receiver and produced by the sender. The Action and FaultAction annotations explicitly associate the WS-Addressing Action message addressing properties on the input and output messages on the addNumbersExplicitInputOutputAction method and on the input, output and fault messages on the addNumbersExplicitFaultAction method. A W3CEnpointReference is obtained from the WebServiceContext via the getEndpointReference() method call as shown in the getEPR() method call.
The server-side implementation also contains a handler ServerSOAPHandler that just dumps out the SOAPMessage from the handleMessage and handleFault methods to show the WS-Addressing header information.
Server-Side Implementation Code Example
AddNumbersImpl.java
/*
* Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
*/
package action.server;
import javax.annotation.Resource;
import javax.jws.WebService;
import javax.xml.ws.WebServiceContext;
import javax.xml.ws.Action;
import javax.xml.ws.FaultAction;
import javax.xml.ws.BindingType;
import javax.jws.HandlerChain;
import javax.xml.ws.soap.Addressing;
import javax.xml.ws.soap.SOAPBinding;
import javax.xml.ws.wsaddressing.W3CEndpointReference;
@WebService(
name="AddNumbers",
portName="AddNumbersPort",
targetNamespace="http://addnumbers.org",
serviceName="AddNumbersService"
)
@HandlerChain(name="", file="server-handler.xml")
@BindingType(value=SOAPBinding.SOAP11HTTP_BINDING)
@Addressing(enabled=true, required=true)
public class AddNumbersImpl {
@Resource
WebServiceContext wsc;
public W3CEndpointReference getEPR() {
return (W3CEndpointReference) wsc.getEndpointReference();
}
@Action(
input="http://addnumbers.org/input",
output="http://addnumbers.org/output")
public int addNumbersExplicitInputOutputAction(int number1, int number2) throws AddNumbersException {
return addNumbers(number1, number2);
}
@Action(
input="input",
output="output",
fault={
@FaultAction(className=AddNumbersException.class, value="http://addnumbers.org/fault/addnumbers")
})
public int addNumbersExplicitFaultAction(int number1, int number2) throws AddNumbersException {
return addNumbers(number1, number2);
}
private int addNumbers(int number1, int number2) throws AddNumbersException {
if (number1 < 0 || number2 < 0) {
throw new AddNumbersException("Negative numbers can't be added!",
"Numbers: " + number1 + ", " + number2);
}
return number1 + number2;
}
}
ServerSOAPHandler.java
/*
* Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
*/
package action.server;
import javax.xml.ws.handler.soap.SOAPMessageContext;
import javax.xml.ws.handler.soap.SOAPHandler;
import javax.xml.ws.handler.MessageContext;
import javax.xml.soap.SOAPMessage;
import javax.xml.soap.SOAPHeader;
import javax.xml.soap.SOAPBody;
import javax.xml.soap.SOAPElement;
import javax.xml.soap.SOAPException;
import javax.xml.soap.Text;
import javax.xml.namespace.QName;
import java.util.Set;
import java.io.ByteArrayOutputStream;
public class ServerSOAPHandler implements SOAPHandler {
private String getMessageEncoding(SOAPMessage msg) throws SOAPException {
String encoding = "utf-8";
if (msg.getProperty(SOAPMessage.CHARACTER_SET_ENCODING) != null) {
encoding = msg.getProperty(SOAPMessage.CHARACTER_SET_ENCODING).toString();
}
return encoding;
}
private void dumpSOAPMessage(SOAPMessage msg) {
if(msg == null) {
System.out.println("SOAP Message is null");
return;
}
System.out.println("");
System.out.println("--------------------");
System.out.println("DUMP OF SOAP MESSAGE");
System.out.println("--------------------");
try {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
msg.writeTo(baos);
System.out.println(baos.toString(getMessageEncoding(msg)));
} catch(Exception e) {
e.printStackTrace();
}
}
public boolean handleMessage(SOAPMessageContext context) {
System.out.println("ServerSOAPHandler.handleMessage");
boolean outbound = (Boolean)context.get(MessageContext.MESSAGE_OUTBOUND_PROPERTY);
if (outbound) {
System.out.println("Direction=outbound");
} else {
System.out.println("Direction=inbound");
}
try {
SOAPMessage msg = ((SOAPMessageContext) context).getMessage();
dumpSOAPMessage(msg);
} catch (Exception e) {
e.printStackTrace();
}
return true;
}
public boolean handleFault(SOAPMessageContext context) {
System.out.println("ServerSOAPHandler.handleFault");
boolean outbound = (Boolean)context.get(MessageContext.MESSAGE_OUTBOUND_PROPERTY);
if (outbound) {
System.out.println("Direction=outbound");
} else {
System.out.println("Direction=inbound");
}
if (!outbound) {
try {
SOAPMessage msg = ((SOAPMessageContext) context).getMessage();
dumpSOAPMessage(msg);
if (context.getMessage().getSOAPBody().getFault() != null) {
String detailName = null;
try {
detailName = context.getMessage().getSOAPBody().getFault().getDetail().getFirstChild().getLocalName();
System.out.println("detailName="+detailName);
} catch(Exception e) {}
}
} catch (SOAPException e) {
e.printStackTrace();
}
}
return true;
}
public Set getHeaders() {
return null;
}
public void close(MessageContext messageContext) {
}
}
Developing a Client to use the new Web Service Addressing features in JAX-WS 2.1
Below is an example of a web service client which uses the Web Service Addressing features. To write a portable client, the client MUST explicitly enable addressing via the use of the AddressingFeature class, and for each invocation, the client MUST explicitly set the BindingProvider.SOAPACTION_URI_PROPERTY to the explicit Action value for the input message. After the W3C WG on WS-Addressing has specified how the use of WS-Addressing is specified in the WSDL, and what the default value must be for Action headers, a future version of JAX-WS will remove these requirements. A W3CEnpointReference is obtained from the BindingProvider via the getEndpointReference() method call as shown in the getEPR() method call.
The client-side implementation also contains a handler ClientSOAPHandler that just dumps out the SOAPMessage from the handleMessage and handleFault methods to show the WS-Addressing header information.
Client-Side Implementation Code Example
Client.java
/*
* Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
*/
package action.client;
import javax.xml.ws.WebServiceRef;
import javax.xml.ws.WebServiceFeature;
import javax.xml.ws.BindingProvider;
import javax.xml.ws.BindingProvider;
import javax.xml.ws.soap.AddressingFeature;
import javax.xml.ws.wsaddressing.W3CEndpointReference;
import java.io.ByteArrayOutputStream;
import javax.xml.transform.stream.StreamResult;
public class Client {
@WebServiceRef
static AddNumbersService service;
private WebServiceFeature[] enabledRequiredwsf = {new AddressingFeature(true, true)};
AddNumbers port;
private AddNumbers getPort()
{
System.out.println("Obtain port from service");
port = service.getPort(AddNumbers.class, enabledRequiredwsf);
return port;
}
private W3CEndpointReference getEPR()
{
if (port == null)
return null;
System.out.println("Obtain EPR from port");
BindingProvider bp = (BindingProvider)port;
W3CEndpointReference epr = (W3CEndpointReference)bp.getEndpointReference();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
epr.writeTo(new StreamResult(baos));
System.out.println(baos.toString());
return epr;
}
private void setSOAPActionURI(Object o, String action) {
BindingProvider bp = (BindingProvider)o;
java.util.Map requestContext=bp.getRequestContext();
if(requestContext == null) {
System.err.println("setSOAPActionURI:getRequestContext() returned null");
} else {
requestContext.put(BindingProvider.SOAPACTION_URI_PROPERTY, action);
}
}
public static void main(String[] args) {
try {
Client client = new Client();
client.getPort();
client.getEPR();
client.callAddNumbersExplicitInputOutputAction();
client.callAddNumbersExplicitFaultAction();
} catch(Exception e) {
e.printStackTrace();
}
}
public void callAddNumbersExplicitInputOutputAction() {
try {
System.out.println("Invoking addNumbersExplicitInputOutputAction operation ...");
setSOAPActionURI(port, "http://addnumbers.org/input");
int result = port.addNumbersExplicitInputOutputAction(10, 10);
System.out.println("result="+result);
} catch(Exception e) {
e.printStackTrace();
}
}
public void callAddNumbersExplicitFaultAction() {
try {
System.out.println("Invoking addNumbersExplicitFaultAction operation ...");
setSOAPActionURI(port, "input");
int result = port.addNumbersExplicitFaultAction(-10, 10);
System.out.println("result="+result);
} catch(Exception e) {
e.printStackTrace();
}
}
}
ClientSOAPHandler.java
/*
* Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
*/
package action.client;
import javax.xml.ws.handler.soap.SOAPMessageContext;
import javax.xml.ws.handler.soap.SOAPHandler;
import javax.xml.ws.handler.MessageContext;
import javax.xml.soap.SOAPMessage;
import javax.xml.soap.SOAPHeader;
import javax.xml.soap.SOAPBody;
import javax.xml.soap.SOAPElement;
import javax.xml.soap.SOAPException;
import javax.xml.soap.Text;
import javax.xml.namespace.QName;
import java.util.Set;
import java.io.ByteArrayOutputStream;
public class ClientSOAPHandler implements SOAPHandler {
private String getMessageEncoding(SOAPMessage msg) throws SOAPException {
String encoding = "utf-8";
if (msg.getProperty(SOAPMessage.CHARACTER_SET_ENCODING) != null) {
encoding = msg.getProperty(SOAPMessage.CHARACTER_SET_ENCODING).toString();
}
return encoding;
}
private void dumpSOAPMessage(SOAPMessage msg) {
if(msg == null) {
System.out.println("SOAP Message is null");
return;
}
System.out.println("");
System.out.println("--------------------");
System.out.println("DUMP OF SOAP MESSAGE");
System.out.println("--------------------");
try {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
msg.writeTo(baos);
System.out.println(baos.toString(getMessageEncoding(msg)));
} catch(Exception e) {
e.printStackTrace();
}
}
public boolean handleMessage(SOAPMessageContext context) {
System.out.println("ClientSOAPHandler.handleMessage");
boolean outbound = (Boolean)context.get(MessageContext.MESSAGE_OUTBOUND_PROPERTY);
if (outbound) {
System.out.println("Direction=outbound");
} else {
System.out.println("Direction=inbound");
}
try {
SOAPMessage msg = ((SOAPMessageContext) context).getMessage();
dumpSOAPMessage(msg);
} catch (Exception e) {
e.printStackTrace();
}
return true;
}
public boolean handleFault(SOAPMessageContext context) {
System.out.println("ClientSOAPHandler.handleFault");
boolean outbound = (Boolean)context.get(MessageContext.MESSAGE_OUTBOUND_PROPERTY);
if (outbound) {
System.out.println("Direction=outbound");
} else {
System.out.println("Direction=inbound");
}
if (!outbound) {
try {
SOAPMessage msg = ((SOAPMessageContext) context).getMessage();
dumpSOAPMessage(msg);
if (context.getMessage().getSOAPBody().getFault() != null) {
String detailName = null;
try {
detailName = context.getMessage().getSOAPBody().getFault().getDetail().getFirstChild().getLocalName();
System.out.println("detailName="+detailName);
} catch(Exception e) {}
}
} catch (SOAPException e) {
e.printStackTrace();
}
}
return true;
}
public Set getHeaders() {
return null;
}
public void close(MessageContext messageContext) {
}
}
Below is the HTTP Request and Response messages captured that shows the WS-Addressing information:
Here is the request/response messages of the addNumbersExplicitInputOutputAction invocation.
Request
POST /J2WDLACTION/jaxws/AddNumbers HTTP/1.1
SOAPAction: "http://addnumbers.org/input"
Content-Type: text/xml; charset=utf-8
Accept: text/xml, multipart/related, text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2
User-Agent: Java/1.5.0_06
Host: localhost:2020
Connection: keep-alive
Content-Length: 788
<?xml version="1.0" ?><S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/"><S:Header><To xmlns="http://www.w3.org/2005/08/addressing">http://localhost:2020/J2WDLACTION/jaxws/AddNumbers</To><Action xmlns="http://www.w3.org/2005/08/addressing">http://addnumbers.org/input</Action><ReplyTo xmlns="http://www.w3.org/2005/08/addressing">
<Address>http://www.w3.org/2005/08/addressing/anonymous</Address>
</ReplyTo><MessageID xmlns="http://www.w3.org/2005/08/addressing">uuid:f0d1d72e-26b8-4fd3-bff4-98fe6b11ebf0</MessageID></S:Header><S:Body><ns3:addNumbersExplicitInputOutputAction xmlns:ns2="http://www.w3.org/2005/08/addressing" xmlns:ns3="http://addnumbers.org"><arg0 xmlns="">10</arg0><arg1 xmlns="">10</arg1></ns3:addNumbersExplicitInputOutputAction></S:Body></S:Envelope>
Response
HTTP/1.1 200 OK
X-Powered-By: Servlet/2.5
Server: Sun Java System Application Server 9.1
Content-Type: text/xml;charset=utf-8
Transfer-Encoding: chunked
Date: Wed, 18 Apr 2007 16:17:00 GMT
2f3
<?xml version="1.0" ?><S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/"><S:Header><To xmlns="http://www.w3.org/2005/08/addressing">http://www.w3.org/2005/08/addressing/anonymous</To><Action xmlns="http://www.w3.org/2005/08/addressing">http://addnumbers.org/output</Action><MessageID xmlns="http://www.w3.org/2005/08/addressing">uuid:ffed49c7-3efb-474c-92ba-a801ee1ae203</MessageID><RelatesTo xmlns="http://www.w3.org/2005/08/addressing">uuid:f0d1d72e-26b8-4fd3-bff4-98fe6b11ebf0</RelatesTo></S:Header><S:Body><ns3:addNumbersExplicitInputOutputActionResponse xmlns:ns2="http://www.w3.org/2005/08/addressing" xmlns:ns3="http://addnumbers.org"><return xmlns="">20</return></ns3:addNumbersExplicitInputOutputActionResponse></S:Body></S:Envelope>
Here is the request/response messages of the addNumbersExplicitFaultAction invocation.
Request
POST /J2WDLACTION/jaxws/AddNumbers HTTP/1.1
SOAPAction: "input"
Content-Type: text/xml; charset=utf-8
Accept: text/xml, multipart/related, text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2
User-Agent: Java/1.5.0_06
Host: localhost:2020
Connection: keep-alive
Content-Length: 755
<?xml version="1.0" ?><S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/"><S:Header><To xmlns="http://www.w3.org/2005/08/addressing">http://localhost:2020/J2WDLACTION/jaxws/AddNumbers</To><Action xmlns="http://www.w3.org/2005/08/addressing">input</Action><ReplyTo xmlns="http://www.w3.org/2005/08/addressing">
<Address>http://www.w3.org/2005/08/addressing/anonymous</Address>
</ReplyTo><MessageID xmlns="http://www.w3.org/2005/08/addressing">uuid:e2a978c0-f727-457d-a35a-88ce66270715</MessageID></S:Header><S:Body><ns3:addNumbersExplicitFaultAction xmlns:ns2="http://www.w3.org/2005/08/addressing" xmlns:ns3="http://addnumbers.org"><arg0 xmlns="">-10</arg0><arg1 xmlns="">10</arg1></ns3:addNumbersExplicitFaultAction></S:Body></S:Envelope>
Response
HTTP/1.1 500 Internal Server Error
X-Powered-By: Servlet/2.5
Server: Sun Java System Application Server 9.1
Content-Type: text/xml;charset=utf-8
Transfer-Encoding: chunked
Date: Wed, 18 Apr 2007 16:17:01 GMT
Connection: close
2000
<?xml version="1.0" ?><S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/"><S:Header><To xmlns="http://www.w3.org/2005/08/addressing">http://www.w3.org/2005/08/addressing/anonymous</To><Action xmlns="http://www.w3.org/2005/08/addressing">http://addnumbers.org/fault/addnumbers</Action><MessageID xmlns="http://www.w3.org/2005/08/addressing">uuid:e92daf6b-7416-4715-b73b-48937a4262c8</MessageID><RelatesTo xmlns="http://www.w3.org/2005/08/addressing">uuid:e2a978c0-f727-457d-a35a-88ce66270715</RelatesTo></S:Header><S:Body><ns2:Fault xmlns:ns2="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns3="http://www.w3.org/2003/05/soap-envelope"><faultcode>ns2:Server</faultcode><faultstring>Negative numbers can't be added!</faultstring><detail><ns3:AddNumbersException xmlns:ns3="http://addnumbers.org" xmlns:ns2="http://www.w3.org/2005/08/addressing"><detail>Numbers: -10, 10</detail><message>Negative numbers can't be added!</message></ns3:AddNumbersException></detail><ns2:Fault></S:Body></S:Envelope>
Running the Sample Code
A sample package accompanies this tip. It provides an example that demonstrates the techniques covered in the tip. The sample package includes the source code for the example, required descriptor files, and build scripts.
Code for the sample after extracting its contents is under:
- src/action
For a working example of this client and server sample code download here.
Steps to install and run the sample:
Step 1. Download GlassFish from the GlassFish Project page that has
JAX-WS 2.1 integrated.
Step 2. Set the following environment variables:
ANT_HOME - path to location of Ant installation. This example uses Ant 1.6.5.
JAVA_HOME - path to location of JDK5.0 installation.
Also, add the Ant location to your PATH environment variable.
Step 3. Download the sample package and extract its contents.
To get started using this tech tip all you have to edit is the
following file:
o techtip/build/common.properties
The main properties to set are the ones below based on your
GlassFish installation properties. You can check any others
that may need tweeking based on your setup.
<!-- Main Properties to Set -->
<!-- Only these need be set -->
<property name="jdk.home" value="/files/java/jdk1.5/jdk1.5"/>
<property name="appserver.home" value="/sun/appserver9.1"/>
<property name="tests.home" value="/tmp/techtip"/>
<property name="webserver.host" value="localhost"/>
<property name="webserver.port" value="8001"/>
<property name="httpproxy.host" value=""/>
<property name="httpproxy.port" value=""/>
Step 4. Start the GlassFish Application Server
Step 5. cd techtip/src/action
Step 6. Type the following to build, deploy and run the sample:
ant
Step 7. Or read it in as a netbeans project by opening project under
techtip/src/action and use netbeans to clean/build/run project.
Posted at 09:28AM May 21, 2007 by artfish in Sun | Comments[3]
Maintaining Sessions using JAX-WS 2.0
This techtip will focus on how to develop a webservice client and server application that maintains session state from one client invocation to the next. This is usefull for webservice application writers that need to develop applications that maintain state across client invocations. A classic example is a shopping cart application.
The example code below was developed and tested using a GlassFish implementation.
Developing a Web Service to maintain session state across client invocations
Step 1
Develop an annotated web service implementation Java class.
Below is a simple example of a web service that maintains the session IDs for all clients connecting to its service. Client session IDs are stored and looked up in a HashSet initialized within its Constructor. In order to determine the session ID of our client the web service needs access to the WebServiceContext which is injected into the endpoint implementation class using the @Resource annotation. Next the endpoint code gets access to the HttpServletRequest object from the MessageContext of the client request in order to determine the session ID for this client invocation. If the session ID exists we know we are in the same session; otherwise this is a new session and the ID is stored in the HashSet appropriately. The web service method printSessionInfo shows the details of this interaction.
/*
* Copyright 2006 Sun Microsystems, Inc. All rights reserved.
* SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
*/
package jaxws.j2w.document.literal.sessionmaintain.server;
import java.util.HashSet;
import java.util.Set;
import javax.annotation.Resource;
import javax.jws.WebMethod;
import javax.jws.WebService;
import javax.servlet.http.HttpSession;
import javax.servlet.http.HttpServletRequest;
import javax.xml.ws.handler.MessageContext;
import javax.xml.ws.WebServiceException;
import javax.xml.ws.WebServiceContext;
@WebService(
name="Test",
portName="TestPort",
serviceName="TestService",
targetNamespace="http://test.org/wsdl"
)
public class TestImpl {
@Resource private WebServiceContext wsc;
private Set clients; // For storing our session ids from clients
private String getClientID() {
HttpServletRequest req = (HttpServletRequest)
wsc.getMessageContext().get(MessageContext.SERVLET_REQUEST);
HttpSession session = req.getSession();
return session.getId();
}
public TestImpl() {
clients = new HashSet();
}
@WebMethod
public String printSessionInfo() {
System.out.println("endpoint invoked, checking session id of client");
String id = getClientID();
System.out.println("** looking up session id: " + id);
boolean idExists = clients.contains(id);
if (idExists) {
System.out.println("** found session id: " + id);
return "Same session, id is " + id;
}
else {
System.out.println("** storing session id: " + id);
clients.add(id);
return "New session, id is " + id;
}
}
}
Step 2
Next the developer compiles the annotated web service class and uses the wsgen tool to perform the Java-to-WSDL mapping to generate the portable artifacts. The wsgen tool has both a command line interface as well as an ant task provided. Our build scripts use the ant task.
Typing the following will compile the annotated server implementation class and invoke wsgen on this class to generate all the portable artifacts:
$ ant compile-server-j2w
compile-server:
[echo] compile-server
[javac] Compiling 1 source file to /tmp/sessionmaintain/classes
do-java2wsdl:
[echo] Invoking WsGen task (Java-to-WSDL mapping)
[wsgen] command line: wsimport /files/java/jdk1.5/jdk1.5.0_06/jre/bin/java -classpath /files/java/jdk1.5/jdk1.5.0_06/lib/tools.jar:/sun/appserver9/lib/javaee.jar:/sun/appserver9/lib/appserv-ws.jar:/tmp/sessionmaintain/classes com.sun.tools.ws.WsGen -d /tmp/sessionmaintain/classes -keep -wsdl -r /tmp/sessionmaintain/src/jaxws/j2w/document/literal/sessionmaintain/wsdl -s /tmp/sessionmaintain/generated -verbose jaxws.j2w.document.literal.sessionmaintain.server.TestImpl
[wsgen] Note: ap round: 1
[wsgen] [ProcessedMethods Class: jaxws.j2w.document.literal.sessionmaintain.server.TestImpl]
[wsgen] [should process method: printSessionInfo hasWebMethods: true ]
[wsgen] [endpointReferencesInterface: false]
[wsgen] [declaring class has WebSevice: true]
[wsgen] [returning: true]
[wsgen] [WrapperGen - method: printSessionInfo()]
[wsgen] [method.getDeclaringType(): jaxws.j2w.document.literal.sessionmaintain.server.TestImpl]
[wsgen] [requestWrapper: jaxws.j2w.document.literal.sessionmaintain.server.jaxws.PrintSessionInfo]
[wsgen] [ProcessedMethods Class: java.lang.Object]
[wsgen] jaxws/j2w/document/literal/sessionmaintain/server/jaxws/PrintSessionInfo.java
[wsgen] jaxws/j2w/document/literal/sessionmaintain/server/jaxws/PrintSessionInfoResponse.java
[wsgen] Note: ap round: 2
[wsgen] [completing model for endpoint: jaxws.j2w.document.literal.sessionmaintain.server.TestImpl]
[wsgen] [ProcessedMethods Class: jaxws.j2w.document.literal.sessionmaintain.server.TestImpl]
[wsgen] [should process method: printSessionInfo hasWebMethods: true ]
[wsgen] [endpointReferencesInterface: false]
[wsgen] [declaring class has WebSevice: true]
[wsgen] [returning: true]
[wsgen] [WebServiceReferenceCollector - method: printSessionInfo()]
[wsgen] [ProcessedMethods Class: java.lang.Object]
compile-server-j2w:
[echo] compile-server-j2w
The following classes were generated:
/tmp/sessionmaintain/classes/jaxws/j2w/document/literal/sessionmaintain/server/jaxws/PrintSessionInfo.class
/tmp/sessionmaintain/classes/jaxws/j2w/document/literal/sessionmaintain/server/jaxws/PrintSessionInfoResponse.class
Step 3
Next the developer packages the web.xml, service implementation class, and all the generated portable artifacts within a WAR file ready for deployment to a web container.
Typing the following will create and package up all these artifacts in a WAR file:
$ ant create-war
Buildfile: build.xml
create-war:
[echo] create-war
[echo] Creating war file /tmp/sessionmaintain/dist/jaxws/j2w/document/literal/sessionmaintain/J2WDLSESSIONMAINTAIN.war
[mkdir] Created dir: /tmp/sessionmaintain/dist/jaxws/j2w/document/literal/sessionmaintain
[war] Building war: /tmp/sessionmaintain/dist/jaxws/j2w/document/literal/sessionmaintain/J2WDLSESSIONMAINTAIN.war
[echo] Created war file /tmp/sessionmaintain/dist/jaxws/j2w/document/literal/sessionmaintain/J2WDLSESSIONMAINTAIN.war
BUILD SUCCESSFUL
Step 4
Deploy the war file to your GlassFish application server.
Typing the following will deploy this war to your GlassFish appserver:
$ ant deploy
Buildfile: build.xml
checkPlatform:
configUnix:
configWindows:
filter.password.file:
[copy] Copying 1 file to /tmp/sessionmaintain/build
configPlatform:
deploy:
[echo] deploy
[echo] Deploying /tmp/sessionmaintain/dist/jaxws/j2w/document/literal/sessionmaintain/J2WDLSESSIONMAINTAIN.war.
[echo] asadmin deploy --user admin --passwordfile /tmp/sessionmaintain/build/password.txt --host localhost --port 4848 --contextroot J2WDLSESSIONMAINTAIN --target server --updload=true
[exec] Command deploy executed successfully.
BUILD SUCCESSFUL
Step 5
Verify web service application is deployed:
In your browser go to URL location of the published WSDL for this server:
This location is as follows:
o http://host:port/J2WDLSESSIONMAINTAIN/jaxws/Test?WSDL
where (host and port) are the settings of your configured GlassFish web server, (J2WDLSESSIONMAINTAIN) is the context root of your web service application, (/jaxws/Test) is the URL alias specified in the web.xml deployment descriptor for deployed endpoint and (?WSDL) is how the published WSDL is accessed.
A WSDL should be displayed if the service is up and running.
Developing a Client to maintain session state across client invocations
Client development always starts from WSDL. The WSDL can be obtained either statically or from a deployed web service. The steps involved:
- Run wsimport to generate client-side artifacts pointing to WSDL
- Implement the client to invoke the web service
- Verify client can access the web service
Step 1
Typing the following will invoke wsimport to import the wsdl and generate all the portable artifacts:
$ ant import-wsdl-client
Buildfile: build.xml
import-wsdl-client:
init:
do-wsdl2java:
[echo] Invoking WsImport task (WSDL-to-Java mapping)
[wsimport] command line: wsimport /files/java/jdk1.5/jdk1.5.0_06/jre/bin/java -classpath /files/java/jdk1.5/jdk1.5.0_06/lib/tools.jar:/sun/appserver9/lib/javaee.jar:/sun/appserver9/lib/appserv-ws.jar:/tmp/sessionmaintain/classes com.sun.tools.ws.WsImport -d /tmp/sessionmaintain/classes -keep -s /tmp/sessionmaintain/generated -verbose wsdl/TestService.wsdl -wsdllocation http://localhost:8001/J2WDLSESSIONMAINTAIN/jaxws/Test?WSDL -b /tmp/sessionmaintain/src/jaxws/j2w/document/literal/sessionmaintain/wsdl/customfile-client.xml -b /tmp/sessionmaintain/src/jaxws/j2w/document/literal/sessionmaintain/wsdl/customfile2-client.xml
[wsimport] jaxws/j2w/document/literal/sessionmaintain/client/ObjectFactory.java
[wsimport] jaxws/j2w/document/literal/sessionmaintain/client/PrintSessionInfo.java
[wsimport] jaxws/j2w/document/literal/sessionmaintain/client/PrintSessionInfoResponse.java
[wsimport] jaxws/j2w/document/literal/sessionmaintain/client/Test.java
[wsimport] jaxws/j2w/document/literal/sessionmaintain/client/TestService.java
[wsimport] jaxws/j2w/document/literal/sessionmaintain/client/package-info.java
[wsimport] jaxws/j2w/document/literal/sessionmaintain/client/ObjectFactory.java
[wsimport] jaxws/j2w/document/literal/sessionmaintain/client/PrintSessionInfo.java
[wsimport] jaxws/j2w/document/literal/sessionmaintain/client/PrintSessionInfoResponse.java
[wsimport] jaxws/j2w/document/literal/sessionmaintain/client/Test.java
[wsimport] jaxws/j2w/document/literal/sessionmaintain/client/TestService.java
[wsimport] jaxws/j2w/document/literal/sessionmaintain/client/package-info.java
BUILD SUCCESSFUL
The following classes were generated:
/tmp/sessionmaintain/classes/jaxws/j2w/document/literal/sessionmaintain/client
/tmp/sessionmaintain/classes/jaxws/j2w/document/literal/sessionmaintain/client/package-info.class
/tmp/sessionmaintain/classes/jaxws/j2w/document/literal/sessionmaintain/client/ObjectFactory.class
/tmp/sessionmaintain/classes/jaxws/j2w/document/literal/sessionmaintain/client/PrintSessionInfoResponse.class
/tmp/sessionmaintain/classes/jaxws/j2w/document/literal/sessionmaintain/client/PrintSessionInfo.class
/tmp/sessionmaintain/classes/jaxws/j2w/document/literal/sessionmaintain/client/Test.class
/tmp/sessionmaintain/classes/jaxws/j2w/document/literal/sessionmaintain/client/TestService.class
Step 2
The developer next implements the client to invoke the web service and compiles it.
Below is a simple example of a web service client that enables our session state to be maintained. Once we get access to the port we get the BindingProvider for the port to get access to the Request context. The client must set the BindingProvider.SESSION_MAINTAIN_PROPERTY to true in order for the session to be maintained. In the method printSessionInfo we print out the session info before this property is set and after to show that the session ID is now being maintained after being set to true.
/*
* Copyright 2006 Sun Microsystems, Inc. All rights reserved.
* SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
*/
package jaxws.j2w.document.literal.sessionmaintain.client;
import javax.xml.ws.WebServiceRef;
import javax.xml.ws.BindingProvider;
import java.util.Map;
public class Client {
@WebServiceRef
static TestService service;
Test port;
private Test getPort()
{
System.out.println("Obtain port from service");
port = service.getPort(Test.class);
return port;
}
public static void main(String[] args) {
try {
Client client = new Client();
client.getPort();
client.printSessionInfo();
} catch(Exception e) {
e.printStackTrace();
}
}
public void printSessionInfo() {
try {
System.out.println("SESSION_MAINTAIN not set all session ids are new");
System.out.println(port.printSessionInfo());
System.out.println(port.printSessionInfo());
System.out.println("Invoking printSessionInfo operation ...");
Map requestContext =
((BindingProvider) port).getRequestContext();
requestContext.put(
BindingProvider.SESSION_MAINTAIN_PROPERTY, Boolean.TRUE);
System.out.println("SESSION_MAINTAIN is set all session ids are same");
System.out.println(port.printSessionInfo());
System.out.println(port.printSessionInfo());
} catch(Exception e) {
e.printStackTrace();
}
}
}
Type the following to compile the client:
$ ant compile-client
Buildfile: build.xml
init:
compile-client:
[echo] compile-client
[javac] Compiling 1 source file to /tmp/sessionmaintain/classes
BUILD SUCCESSFUL
Step 3
Next you can call the runclient task which will deploy the client in the appclient container and run the test.
$ ant runclient
Buildfile: build.xml
checkPlatform:
configUnix:
configWindows:
filter.password.file:
[copy] Copying 1 file to /tmp/sessionmaintain/build
configPlatform:
runclient:
[echo] runclient jaxws.j2w.document.literal.sessionmaintain.client.Client
[exec] Obtain port from service
[exec] SESSION_MAINTAIN not set all session ids are new
[exec] New session, id is 87ced76174257f2a0ed41297882be
[exec] New session, id is 87ced8381c2b3fed91860384a976
[exec] Invoking printSessionInfo operation ...
[exec] SESSION_MAINTAIN is set all session ids are same
[exec] New session, id is 87ced87dbef27438391f7db4c2b5
[exec] Same session, id is 87ced87dbef27438391f7db4c2b5
BUILD SUCCESSFUL
As you can see from the output above before the SESSION_MAINTAIN_PROPERTY was set the session ID was new for each invocation. Once we set SESSION_MAINTAIN_PROPERTY to true only the first invocation is new and subsequent invocations return same session ID.
Running the Sample Code
A sample package accompanies this tip. It provides an example that demonstrates the techniques covered in the tip. The sample package includes the source code for the example, required descriptor files, and build scripts.
Code for the sample after extracting its contents is under:
- sessionmaintain/src/jaxws/j2w/document/literal/sessionmaintain
For a working example of this client and server sample code download here.
Steps to install and run the sample:
Step 1. Download GlassFish from the GlassFish Project page.
Step 2. Set the following environment variables:
ANT_HOME - path to location of Ant installation. This example uses Ant 1.6.5.
JAVA_HOME - path to location of JDK5.0 installation.
Also, add the Ant location to your PATH environment variable.
Step 3. Download the sample package and extract its contents.
To get started using this tech tip all you have to edit is the
following file:
o sessionmaintain/build/common.properties
The main properties to set are the ones below based on your
GlassFish installation properties. You can check any others
that may need tweeking based on your setup.
<!-- Main Properties to Set -->
<!-- Only these need be set -->
<property name="jdk.home" value="/files/java/jdk1.5/jdk1.5.0"/>
<property name="appserver.home" value="/sun/appserver9"/>
<property name="tests.home" value="/tmp/sessionmaintain"/>
<property name="webserver.host" value="localhost"/>
<property name="webserver.port" value="8001"/>
Step 4. Start the GlassFish Application Server
Step 5. cd sessionmaintain/src/jaxws/j2w/document/literal/sessionmaintain
Step 6. Type the following to build, deploy and run the sample:
ant
Posted at 01:53PM Jul 19, 2006 by artfish in Sun | Comments[3]
Logical and Protocol Handler support in JAX-WS 2.0
JAX-WS 2.0 provides a flexible plug-in framework for message processing modules, known as handlers, that may be used to extend the capabilities of a JAX-WS runtime system. An excellent blog has been written that shows how to use and configure Logical and Protcol specific Handlers on both the client and server that reads a message sent from the client to the webservice endpoint and back again. This handler example shows configuring 3 kinds of handlers (service based, port based and protocol based).
Here is the blog written by Stephen DiMilla:
Posted at 09:14AM Jun 01, 2006 by artfish in Sun | Comments[0]
WS-I Attachment Profile 1.0 support in JAX-WS 2.0
JAX-WS 2.0 supports the WS-I Attachments Profile 1.0 Specification. A web service developer can implement Soap With Attachments using the WS-I Attachment Profile 1.0 features in JAX-WS 2.0. A good blog has been written on this which shows a detailed sample application demonstrating these features and the use of the WSDL 1.1 MIME binding.
Here is the blog written by Alan Frechette:
Posted at 08:54AM May 30, 2006 by artfish in Sun | Comments[0]
A Simple HowTo of Web Service and Client Development using JAX-WS 2.0
This techtip comes with sample code and an ant build environment to demonstrate this usage using an actual glassfish implementation.
The code sample for this techtip after extracting its contents is under:
- techtip/src/jaxws/j2w/document/literal/hello
Server Development Steps
Server development in this case starts from Java. The steps involved:
- Develop an annotated web service implementation Java class
- Compile and run wsgen tool to perform Java-to-WSDL mapping on this class
- Package web service implementation code, generated portable artifacts, web deployment descriptor into WAR file for deployment
- Deploy the WAR to your GlassFish application server
- Verify endpoint is accessible by going to URL of published WSDL
Step 1
First a developer begins by writing an annotated Java class to expose as a Web Service. The @WebService tag marks the Java class as implementing a Web Service. The @WebMethod tags indentify the individual methods of the Java class that are exposed externally as Web Service operations.
Below is an example of a simple hello service that has 2 methods: hello and helloOneWay. The method helloOneWay has an additional annotation to mark it as a oneway operation. The @WebService tag defines additional attributes to control the Java-to-WSDL mapping of generated WSDL. The targetNamespace attribute is used as the targetNamespace for the wsdl. The name attribute is used as the name of the wsdl portType element. The portName attribute is used as the name of the wsdl port element. The serviceName attribute is used as the name of the wsdl service element.
package jaxws.j2w.document.literal.hello.server;
import javax.jws.WebService;
import javax.jws.WebMethod;
import javax.jws.Oneway;
@WebService(
name="Hello",
portName="HelloPort",
serviceName="HelloService",
targetNamespace="http://hello.org"
)
public class HelloImpl {
@WebMethod
public String hello(String s) {
System.out.println("hello: " + s);
return s;
}
@WebMethod
@Oneway
public void helloOneWay(String s) {
System.out.println("helloOneWay: " + s);
}
}
Step 2
Next the developer compiles the annotated web service class and uses the wsgen tool to perform the Java-to-WSDL mapping to generate the portable artifacts. The wsgen tool has both a command line interface as well as an ant task provided. Our build scripts use the ant task.
Typing the following will compile the annotated server implementation class and invoke wsgen on this class to generate all the portable artifacts:
$ ant compile-server-j2w
wsgen invocation is as follows:
[wsgen] command line: wsgen /files/java/jdk1.5/jdk1.5.0_04/jre/bin/java -classpath /sun/appserver9/lib/javaee.jar:/sun/appserver9/lib/appserv-ws.jar:/files/techtip/classes com.sun.tools.ws.WsGen -d /files/tmp/techtip/classes -keep -wsdl -r /files/techtip/src/jaxws/j2w/document/literal/hello/wsdl -s /files/techtip/generated -verbose jaxws.j2w.document.literal.hello.server.HelloImpl
The following classes were generated:
/files/techtip/classes/jaxws/j2w/document/literal/hello/server/jaxws/Hello.class
/files/techtip/classes/jaxws/j2w/document/literal/hello/server/jaxws/HelloOneWay.class
/files/techtip/classes/jaxws/j2w/document/literal/hello/server/jaxws/HelloResponse.class
Step 3
Next the developer packages the web.xml, service implementation class, and all the generated portable artifacts within a WAR file ready for deployment to a web container.
Typing the following will create and package up all these artifacts in a WAR file:
$ ant create-war
Step 4
Deploy the war file to your GlassFish application server.
Typing the following will deploy this war to your GlassFish appserver:
$ ant deploy
Step 5
Verify web service application is deployed:
In your browser go to URL location of the published WSDL for this server:
o http://host:port/J2WDLHELLO/jaxws/Hello?WSDL
where host and port are the settings of your configured GlassFish web server host and port settings, J2WDLHELLO is the context root of your web service application, /jaxws/Hello is the URL alias specified in the web.xml deployment descriptor for deployed endpoint and ?WSDL is how the published WSDL is accessed.
A WSDL should be displayed if the service is up and running.
Client Development Steps
Client development always starts from WSDL. The WSDL can be obtained either statically or from a deployed web service. The steps involved:
- Run wsimport to generate client-side artifacts pointing to WSDL
- Implement the client to invoke the web service
- Verify client can access the web service
Typing the following will invoke wsimport to import the wsdl and generate all the portable artifacts:
$ ant import-wsdl-client
The Ant task used invokes wsimport as follows:
[wsimport] command line: wsimport /files/java/jdk1.5/jdk1.5.0_04/jre/bin/java -classpath /sun/appserver9/lib/javaee.jar:/sun/appserver9/lib/appserv-ws.jar:/files/techtip/classes com.sun.tools.ws.WsImport -d /files/techtip/classes -keep -s /files/techtip/generated -verbose http://localhost:8001/J2WDLHELLO/jaxws/Hello?WSDL -p jaxws.j2w.document.literal.hello.client -wsdllocation http://localhost:8001/J2WDLHELLO/jaxws/Hello?WSDL
The following classes were generated:
/files/techtip/classes/jaxws/j2w/document/literal/hello/client/package-info.class
/files/techtip/classes/jaxws/j2w/document/literal/hello/client/Hello.class
/files/techtip/classes/jaxws/j2w/document/literal/hello/client/HelloOneWay.class
/files/techtip/classes/jaxws/j2w/document/literal/hello/client/HelloResponse.class
/files/techtip/classes/jaxws/j2w/document/literal/hello/client/HelloService.class
/files/techtip/classes/jaxws/j2w/document/literal/hello/client/Hello_Type.class
/files/techtip/classes/jaxws/j2w/document/literal/hello/client/ObjectFactory.class
One point to note on this is that the wsdllocation specified is the actual http URL to the deployed web service published WSDL location. See -wsdllocation option above to wsimport command. The generated artifacts like Service Endpoint Interface class and the Service Interface class will have this value in the wsdlLocation attribute on their annotations. This is how the client knows where to contact the web service.
Step 2
The developer next implements the client to invoke the web service. A client must first get a reference to the web service he wants to invoke. This is accomplished by placing a @WebServiceRef annotation in the client code followed by a static reference to the service interface class for the service to be injected.
Upon deployment of this client the @WebServiceRef annotation and service object will be injected with the proper reference to the web service. The location of web service is known based on the wsdlLocation above placed in both the generated Service Endpoint Interface class and Service Interface class generated in step above.
package jaxws.j2w.document.literal.hello.client;
import javax.xml.ws.WebServiceRef;
public class Client {
@WebServiceRef
static HelloService service;
Hello port;
private Hello getPort()
{
System.out.println("Obtain port from service");
port = service.getHelloPort();
return port;
}
public static void main(String[] args) {
try {
Client client = new Client();
client.getPort();
client.sayHello();
client.sayHelloOneWay();
} catch(Exception e) {
e.printStackTrace();
}
}
public void sayHello() {
try {
System.out.println("Invoking hello operation ...");
String s = port.hello("Hello There");
System.out.println(s);
} catch(Exception e) {
e.printStackTrace();
}
}
public void sayHelloOneWay() {
try {
System.out.println("Invoking helloOneWay operation ...");
port.helloOneWay("Hello There - oneway");
} catch(Exception e) {
e.printStackTrace();
}
}
}
Type the following to compile the client:
$ ant compile-client
Step 3
Next you can call the runclient task which will deploy the client in the appclient container and run the test.
$ ant runclient
Buildfile: build.xml
checkPlatform:
configUnix:
configWindows:
filter.password.file:
[copy] Copying 1 file to /files/tmp/techtip/build
configPlatform:
runclient:
[echo] runclient jaxws.j2w.document.literal.hello.client.Client
[exec] Obtain port from service
[exec] Invoking hello operation ...
[exec] Hello There
[exec] Invoking helloOneWay operation ...
Running the Sample(s) Code
A sample package accompanies this tip. It provides an example that demonstrates the techniques covered in the tip. The sample package includes the source code for the example, required descriptor files, and build scripts.
Code for the sample(s) after extracting its contents is under:
- techtip/src/jaxws/j2w/document/literal/hello
For a working example of this client and server sample code download here.
Steps to install and run the sample:
Step 1. Download GlassFish from the GlassFish Project page.
Step 2. Set the following environment variables:
ANT_HOME - path to location of Ant installation. This example uses Ant 1.6.5.
JAVA_HOME - path to location of JDK5.0 installation.
Also, add the Ant location to your PATH environment variable.
Step 3. Download the sample package and extract its contents.
To get started using this tech tip all you have to edit is the
following file:
o techtip/build/common.properties
The main properties to set are the ones below based on your
GlassFish installation properties. You can check any others
that may need tweeking based on your setup.
<!-- Main Properties to Set -->
<!-- Only these need be set -->
<property name="jdk.home" value="/files/java/jdk1.5/jdk1.5.0"/>
<property name="appserver.home" value="/sun/appserver9"/>
<property name="tests.home" value="/files/techtip"/>
<property name="webserver.host" value="localhost"/>
<property name="webserver.port" value="8001"/>
Step 4. Start the GlassFish Application Server
Step 5. cd techtip/src/jaxws/j2w/document/literal/hello
Step 6. Type the following to build, deploy and run the sample:
ant
Posted at 09:28AM May 09, 2006 by artfish in Sun | Comments[5]
Operating at the XML Message Level in JAX-WS 2.0
This techtip will focus on the use of javax.xml.ws.Provider API on the server-side and javax.xml.ws.Dispatch API on the client-side for operating at the XML Message Level for communications between services and service clients.
The example code below was developed and tested using a GlassFish implementation.
Dispatch and Provider support two usage modes:
Message In this mode, applications work directly with protocol-specific message structures. For example, if a SOAP binding is used access is to the SOAPMessage as a whole.
Message Payload In this mode, applications work with the payload of messages rather than the messages themselves. For example, if a SOAP binding is used access is to the contents of the SOAPBody of a SOAPMessage.
Developing a Web Service to operate at the XML Message Level
For a web service to operate in payload mode you would use Provider<Source>. To operate in message mode you would use Provider<SOAPMessage> for the SOAP binding and Provider<DataSource> for the HTTP binding.
Below is an example of a Provider service which operates in message mode using the SOAP binding. The annotation @WebServiceProvider indicates this is a Provider based endpoint implementation. The attributes of this annotation such as portName, serviceName and targetNamespace simply match the corresponding names in the WSDL in this example. The @ServiceMode annotation indicates that the mode of operation is MESSAGE mode. The HelloProvider class implements the required Provider<SOAPMessage> interface to match the SOAP binding and message mode. The service defines its invoke(...) method to take as an input parameter and return value a SOAPMessage object. The Provider receives as input a SOAPMessage request and returns as output a SOAPMessage response.
package jaxws.w2j.document.literal.provider.server;
import javax.xml.ws.WebServiceProvider;
import javax.xml.ws.BindingType;
import javax.xml.ws.ServiceMode;
import javax.xml.ws.Provider;
import javax.xml.soap.SOAPMessage;
import javax.xml.soap.MessageFactory;
import java.io.ByteArrayOutputStream;
import java.io.StringReader;
import javax.xml.transform.stream.StreamSource;
import javax.xml.transform.Source;
@WebServiceProvider(
portName="HelloPort",
serviceName="HelloService",
targetNamespace="http://helloservice.org/wsdl",
wsdlLocation="WEB-INF/wsdl/HelloService.wsdl"
)
@BindingType(value="http://schemas.xmlsoap.org/wsdl/soap/http")
@ServiceMode(value=javax.xml.ws.Service.Mode.MESSAGE)
public class HelloProvider implements Provider<SOAPMessage> {
private static final String helloResponse = "<soapenv:Envelope xmlns:soapenv=\"http://schemas.xmlsoap.org/soap/envelope/\"><soapenv:Body><HelloResponse xmlns=\"http://helloproviderservice.org/types\"><argument>responseBean</argument></HelloResponse></soapenv:Body></soapenv:Envelope>";
public SOAPMessage invoke(SOAPMessage req) {
System.out.println("invoke: Request: " + getSOAPMessageAsString(req));
SOAPMessage res = null;
try {
res = makeSOAPMessage(helloResponse);
} catch (Exception e) {
System.out.println("Exception: occurred " + e);
}
System.out.println("invoke: Response: " + getSOAPMessageAsString(res));
return res;
}
private String getSOAPMessageAsString(SOAPMessage msg)
{
ByteArrayOutputStream baos = null;
String s = null;
try {
baos = new ByteArrayOutputStream();
msg.writeTo(baos);
s = baos.toString();
} catch(Exception e) {
e.printStackTrace();
}
return s;
}
private SOAPMessage makeSOAPMessage(String msg)
{
try {
MessageFactory factory = MessageFactory.newInstance();
SOAPMessage message = factory.createMessage();
message.getSOAPPart().setContent((Source)new StreamSource(new StringReader(msg)));
message.saveChanges();
return message;
}
catch (Exception e) {
return null;
}
}
}
Developing a Client to operate at the XML Message Level
For a client to operate in payload mode you would use Dispatch<Source>. To operate in message mode you would use Dispatch<SOAPMessage> for the SOAP binding and Dispatch<DataSource> for the HTTP binding.
Below is an example of our Dispatch client which operates in message mode using the SOAP binding. The client creates a Dispatch instance from its service object via service.createDispatch(...) and indicates the MESSAGE mode to operate in and the message object to exchange as a SOAPMessage. The client invokes the Provider endpoint at the specified port by calling its invoke method passing it a SOAPMessage request and receiving a SOAPMessage response.
package jaxws.w2j.document.literal.provider.client;
import javax.xml.ws.WebServiceRef;
import javax.xml.ws.Dispatch;
import javax.xml.ws.soap.SOAPBinding;
import javax.xml.soap.SOAPMessage;
import javax.xml.soap.MessageFactory;
import java.io.ByteArrayOutputStream;
import java.io.StringReader;
import javax.xml.transform.stream.StreamSource;
import javax.xml.transform.Source;
import javax.xml.namespace.QName;
public class Client {
@WebServiceRef(
wsdlLocation="http://localhost:8001/W2JDLHELLOPROVIDER/jaxws/Hello?WSDL"
)
static HelloService service;
private static final String URL =
"http://localhost:8080/W2JDLHELLOPROVIDER/jaxws/Hello?WSDL";
private final String NAMESPACEURI = "http://helloservice.org/wsdl";
private static final String SERVICE_NAME = "HelloService";
private static final String PORT_NAME = "HelloPort";
private QName SERVICE_QNAME = new QName(NAMESPACEURI, SERVICE_NAME);
private QName PORT_QNAME = new QName(NAMESPACEURI, PORT_NAME);
private static String helloRequest = "<soapenv:Envelope xmlns:soapenv=\"http://schemas.xmlsoap.org/soap/envelope/\"><soapenv:Body><HelloRequest xmlns=\"http://helloservice.org/types\"><argument>sendBean</argument></HelloRequest></soapenv:Body></soapenv:Envelope>";
Dispatch<SOAPMessage> dispatchMsg;
public static void main(String[] args) {
Client client = new Client();
client.doInvoke();
}
public void doInvoke() {
System.out.println("doInvoke");
try {
dispatchMsg = service.createDispatch(PORT_QNAME, SOAPMessage.class, javax.xml.ws.Service.Mode.MESSAGE);
SOAPMessage reqMsg = makeSOAPMessage(helloRequest);
System.out.println("SOAPMessage request: " + getSOAPMessageAsString(reqMsg));
SOAPMessage resMsg = dispatchMsg.invoke(reqMsg);
System.out.println("SOAPPMessage response: " + getSOAPMessageAsString(resMsg));
} catch (Exception e) {
e.printStackTrace();
}
}
private String getSOAPMessageAsString(SOAPMessage msg)
{
ByteArrayOutputStream baos = null;
String s = null;
try {
baos = new ByteArrayOutputStream();
msg.writeTo(baos);
s = baos.toString();
} catch(Exception e) {
e.printStackTrace();
}
return s;
}
private SOAPMessage makeSOAPMessage(String msg)
{
try {
MessageFactory factory = MessageFactory.newInstance();
SOAPMessage message = factory.createMessage();
message.getSOAPPart().setContent((Source) new StreamSource(new StringReader(msg)));
message.saveChanges();
return message;
}
catch (Exception e) {
return null;
}
}
}
For a working example of this client and server sample code download here.
Posted at 02:46PM Feb 16, 2006 by artfish in Sun | Comments[12]
Client Side Asynchronous Mappings in JAX-WS 2.0
This techtip will focus on the support for client side asynchronous operations.
The example code below was developed and tested using a GlassFish implementation.
Client side asynchronous operation mapping is enabled or disabled via use of the jaxws:enableAsyncMapping binding declaration used within a binding customization file.
The following standard interfaces are used in asynchronous operation mapping:
javax.xml.ws.Response A generic interface that is used to group the results of a method invocation with the response context. Response extends Future<T> to provide asynchronous result polling capabilities.
javax.xml.ws.AsyncHandler A generic interface that clients implement to receive results in an asynchronous callback.
Each wsdl:operation is mapped to two additional asynchronous methods in the corresponding service endpoint interface:
Polling method A polling method returns a typed Response<ResponseBean> that may be polled using methods inherited from Future
Callback Method A callback method takes an additional final parameter that is an instance of a typed AsyncHandler<ResponseBean> and returns a wildcard Future that may be polled to determine when the operation has completed. The object returned from Future.get() has no standard type. Client code should not attempt to cast the object to any particular type as this will result in non-portable behavior.
Developing Client Code with Asynchronous Mapping Support
Client development always starts from WSDL. The WSDL can be obtained either statically or from a deployed web service. The steps involved:
1. Run wsimport to generate portable artifacts pointing to WSDL.
2. Implement the client to invoke the web service.
3. Verify client access via synchronous and asynchronous calls.
Step 1
Run wsimport to generate portable artifacts needed by the client.
Below is a snippet of the WSDL for the HelloService used to generate our client code.
<code>
<message name="hello">
<part name="parameters" element="ns2:HelloRequestBean"/>
</message>
<message name="helloResponse">
<part name="result" element="ns2:HelloResponseBean"/>
</message>
<portType name="Hello">
<operation name="hello">
<input message="tns:hello"/>
<output message="tns:helloResponse"/>
</operation>
</portType>
</code>
The wsdl:operation named hello has 1 input parameter HelloRequestBean and a return parameter of HelloResponseBean.
In our client example we use a binding customization file passed on to wsimport to customize the package name for both wsdl and schema elements and to enable asynchronous operation mappings:
<code>
<package name="jaxws.w2j.document.literal.async.client"/>
<enableAsyncMapping>true</enableAsyncMapping>
</code>
Here is the full customization file for our client:
customfile-client.xml
<code>
<?xml version="1.0" encoding="UTF-8"?>
<bindings wsdlLocation="HelloService.wsdl" xmlns="http://java.sun.com/xml/ns/jaxws">
<bindings node="ns1:definitions" xmlns:ns1="http://schemas.xmlsoap.org/wsdl/">
<package name="jaxws.w2j.document.literal.async.client"/>
<enableAsyncMapping>true</enableAsyncMapping>
</bindings>
<bindings node="ns1:definitions/ns1:types/xs:schema[@targetNamespace='http://helloservice.org/types']"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:ns1="http//schemas.xmlsoap.org/wsdl"/>
<ns2:schemaBindings xmlns:ns2="http://java.sun.com/xml/ns/jaxb">
<ns2:package name="jaxws.w2j.document.literal.async.client"/>
</ns2:schemaBindings>
</bindings>
</bindings>
</code>
With asynchronous mappings enabled the client will generate 1 method for synchronous call and 2 additional methods for asynchronous calls. In this example the wsdl:operation hello will generate the following method calls which will be handled by the portable client runtime:
<code>
public HelloResponseBean hello(HelloRequestBean);
public Response<HelloResponseBean> helloAsync(HelloRequestBean);
public Future<?> helloAsync(HelloRequestBean, AsyncHandler<HelloResponseBean>);
</code>
The following ant target invokes wsimport to import the wsdl and generate the portable artifacts:
$ ant import-wsdl-client
The binding customization file above is passed as an argument to wscompile. The wsdllocation option passed in is the actual http URL to the deployed web service published WSDL location. The generated artifacts like Service Endpoint Interface and Service Interface classes will have this value in the wsdlLocation attribute on their annotations. This is how the client knows where to contact the web service The wsdllocation URL used for our published web service endpoint is:
<code>
http://localhost:8001/W2JDLASYNC/jaxws/Hello?WSDL
</code>
Step 2
The developer next implements the client to invoke the web service. A client must first get a reference to the web service it wants to invoke. This is accomplished by placing a '@WebServiceRef' annotation in the client code followed by a static reference to the service interface class for the service to be injected within a JavaEE container.
Upon deployment of this client the '@WebServiceRef' annotation and service object will be injected with the proper reference to the web service. The location of web service is known based on the wsdlLocation above placed in both the generated Service Endpoint Interface and Service Interface classes generated in step above.
For asynchronous case with polling all we need to do is invoke the asynchronous method and poll the Response object until response is ready. Here is sample snippet of code:
<code>
HelloRequestBean helloReq = of.createHelloRequestBean();
helloReq.setString(reqStr);
System.out.println("Calling remote method helloAsync with polling ...");
Response<HelloResponseBean> response = port.helloAsync(helloReq);
System.out.println("Polling and waiting for data ...");
while (!response.isDone())
;
HelloResponseBean helloRes = response.get();
</code>
For the asynchronous with CallbackHandler case we define in our client an Asynchronous Callback Handler which implements javax.xml.ws.AsyncHandler interface namely 'HelloCallbackHandler'. This handler is passed as a parameter when calling the asynchronous method:
<code>
Public Future<?> helloAsync(HelloRequestBean, AsyncHandler<HelloResponseBean>);
</code>
The CallbackHandler will receive the response data asynchronously when the web service invocation is complete. The client can check the Future<?> object for notification when the operation is done before accessing the data. Here is a sample snippet of code:
<code>
HelloRequestBean helloReq = of.createHelloRequestBean();
helloReq.setString(reqStr);
HelloCallbackHandler handler = new HelloCallbackHandler();
System.out.println("Calling remote method helloAsync with CallbackHandler ...");
Future<?> response = port.helloAsync(helloReq, handler);
System.out.println("Waiting for CallbackHandler to complete ...");
while (!response.isDone())
;
HelloResponseBean helloRes = handler.getResponse();
</code>
Below is the full client code which invokes the web service operation 'hello' via the 3 different methods:
Client.java
<code>
package javax.w2j.document.literal.async.client;
import javax.xml.ws.WebServiceRef;
import javax.xml.ws.AsyncHandler;
import javax.xml.ws.Response;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
public class Client {
// needed to create jaxb objects
private static final ObjectFactory of = new ObjectFactory();
@WebServiceRef
static HelloService service = null;
Hello port = null;
// Asynchronous Callback Handler
private class HelloCallbackHandler implements AsyncHandler<HelloResponseBean> {
private HelloResponseBean output;
public void handleResponse(Response<HelloResponseBean> response) {
try {
output = response.get();
} catch (ExecutionException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
HelloResponseBean getResponse() {
return output;
}
}
private Hello getPort()
{
System.out.println("Obtain port from service");
port = service.getHelloPort();
return port;
}
public static void main(String[] args) {
try {
Client client = new Client();
client.getPort();
client.invokeSynchronous();
client.invokeAsyncPoll();
client.invokeAsyncCallback();
} catch (Exception e) {
e.printStackTrace();
}
}
public void invokeSynchronous() {
System.out.println("invokeSynchronous");
String reqStr = "Synchronous";
String resStr = "Hello, Synchronous!";
try {
HelloRequestBean helloReq = of.createHelloRequestBean();
helloReq.setString(reqStr);
System.out.println("Calling remote method hello ...");
HelloResponseBean helloRes = port.hello(helloReq);
String result = helloRes.getResult();
System.out.println("result="+result);
}
catch (Exception e) {
e.printStackTrace();
}
}
public void invokeAsyncPoll() {
System.out.println("invokeAsyncPoll");
String reqStr = "AsyncPoll";
String resStr = "Hello, AsyncPoll!";
try {
HelloRequestBean helloReq = of.createHelloRequestBean();
helloReq.setString(reqStr);
System.out.println("Calling remote method helloAsync with polling ...");
Response<HelloResponseBean> response = port.helloAsync(helloReq);
System.out.println("Polling and waiting for data ...");
while (!response.isDone())
;
HelloResponseBean helloRes = response.get();
String result = helloRes.getResult();
System.out.println("result="+result);
}
catch (Exception e) {
e.printStackTrace();
}
}
public void invokeAsyncCallback() {
System.out.println("invokeAsyncCallback");
String reqStr = "AsyncCallback";
String resStr = "Hello, AsyncCallback!";
try {
HelloRequestBean helloReq = of.createHelloRequestBean();
helloReq.setString(reqStr);
HelloCallbackHandler handler = new HelloCallbackHandler();
System.out.println("Calling remote method helloAsync with CallbackHandler ...");
Future<?> response = port.helloAsync(helloReq, handler);
System.out.println("Waiting for CallbackHandler to complete ...");
While (!response.isDone())
;
HelloResponseBean helloRes = handler.getResponse();
String result = helloRes.getResult();
System.out.println("result="+result);
}
catch (Exception e) {
e.printStackTrace();
}
}
}
</code>
Step 3
Below is a sample run of the client on a GlassFish implementation:
$ ant runclient
checkPlatform:
configUnix:
configWindow:
filter.password.file:
[copy] Copying 1 file to /files/techtip/build
configPlatform:
runclient:
[echo] runclient jaxws.w2j.document.literal.async.client.Client
[exec] Obtain port from service
[exec] invokeSynchronous
[exec] Calling remote method hello ...
[exec] result=Hello, Synchronous!
[exec] invokeAsyncPoll
[exec] Calling remote method helloAsync with polling ...
[exec] Polling and waiting for data ...
[exec] result=Hello, AsyncPoll!
[exec] invokeAsyncCallback
[exec] Calling remote method helloAsync with CallbackHandler
[exec] Waiting for CallbackHandler to complete ...
[exec] result=Hello, AsyncCallback!
Posted at 12:58PM Feb 09, 2006 by artfish in Sun | Comments[6]
Developing Web Services using JAX-WS 2.0
This blog will talk about using the latest JAX-WS 2.0 technology for developing Web Service applications and clients. A series of articles demonstrating and using the features of JAX-WS 2.0 will be posted on an ongoing basis.
Posted at 01:43PM Feb 08, 2006 by artfish in Sun | Comments[0]