Technical Article
Developing Web Services with Java 2 Platform, Enterprise Edition (J2EE) 1.4 Platform
Qusay H. Mahmoud, February 2004
The Java 2 Platform, Enterprise Edition (J2EE) version 1.4 has evolved to integrate web services. Web services are now one of the many service delivery channels of the J2EE platform; existing J2EE components can be easily exposed as web services. Many benefits of the J2EE platform are available for web services, including portability, scalability, reliability, and no single-vendor lock-in. For example, J2EE containers provide transaction support, database connections, life cycle management, and other services that are scalable and require no code from application developers.
The J2EE 1.4 platform provides comprehensive support for web services through the JAX-RPC 1.1 API, which can be used to develop service endpoints based on SOAP. JAX-RPC 1.1 provides interoperability with web services based on the Web Services Description Language (WSDL) and Simple Object Access Protocol (SOAP). The J2EE 1.4 platform also supports JSR 109, that builds upon JAX-RPC and focuses on the programming model for implementing web services, as well as deploying web services in the J2EE 1.4 platform. In addition, J2EE 1.4 supports the WS-I Basic Profile to ensure that web services developed using the J2EE platform are portable not only across J2EE implementations, but are also interoperable with any web service developed, using any platform that conforms to the WS-I standards.
To develop web services, a developer usually needs extensive knowledge of XML-based standards and protocols (such as WSDL and SOAP), as well as a fair amount of programming experience. In the J2EE 1.4 platform, however, developing web services is seamless, since you don't really need to know about WSDL and SOAP; the mapping between the Java language and such XML-based standards is handled by the web service runtime system, thus freeing the developer from such low-level programming details.
This article provides a tutorial and step-by-step instructions on how to develop, deploy, and use web services, using the J2EE 1.4 SDK, with little programming required. We'll cover:
- A brief overview of web services
- Overview of JSR 109
- Examples of web services and clients
- A flavor of the effort involved in developing web services using the J2EE 1.4 platform
- Sample code that you can adapt to your own web service applications
Overview of Web Services
Web services are application components that are designed to support interoperable machine-to-machine interaction over a network. This interoperability is gained through a set of XML-based open standards, such as the Web Services Description Language (WSDL), the Simple Object Access Protocol (SOAP), and Universal Description, Discovery, and Integration (UDDI). These standards provide a common and interoperable approach for defining, publishing, and using web services. If you don't know anything about these technologies yet, no worries: you can develop web services in the J2EE 1.4 platform without knowing a thing about these XML-based standards and protocols. Figure 1 shows how the Java APIs for XML Registries (JAXR) and Java APIs for XML Remote Procedure Calls (JAX-RPC) play a role in publishing, discovering, and using web services.
Figure 1: J2EE 1.4 Publish-Discover-Invoke model
From a software architect's point of view, a web service can be considered as a service-oriented architecture, which consists of a collection of services that communicate with each other (and end-user clients) through well-defined interfaces. One advantage of service-oriented architecture is that it allows the development of loosely coupled applications that can be distributed and accessed, from any client, across the network.
The J2EE 1.4 SDK provides the tools you need to quickly build, test, and deploy web services and clients that interoperate with other web services and clients running on Java technology-based or non-Java technology-based platforms. In addition, it enables businesses to expose their existing J2EE applications as web services. Servlets and Enterprise JavaBeans (EJBs) components can be exposed as web services that can be accessed by Java technology-based or non-Java technology-based web service clients. J2EE applications can act as web service clients themselves, and they can communicate with other web services, regardless of how they are implemented.
J2EE 1.4 SDK
- J2EE 1.4 Application Server
- Java 2 Platform, Standard Edition (J2SE) 1.4.2_01
- J2EE Samples (Java Pet Store, Java Adventure Builder, Smart Ticket, and others)
- Sun ONE Message Queue
- PointBase Database Server
By default, the J2EE 1.4 SDK will be installed (on Microsoft Windows) at c:\sun\AppServer
, and the JDK 1.4.2_01 would be installed at c:\sun\AppServer\jdk
.
To configure your installation, just edit your path to include c:\sun\AppServer\bin
and c:\sun\AppServer\jdk\bin
. The c:\sun\AppServer\bin
gives you access to several tools, including wscompile
, which takes the service definition interface and generates the client-side stubs or server-side skeletons, or a WSDL description for the provided interface.
JSR 109
The process of developing and deploying web services is coupled with the runtime system. For example, deploying a web service on Apache Axis is different from deploying the same web service on Apache SOAP or any other platform. The Java Community Process (JCP) specification JSR 109 (Implementing Enterprise Web Services) promotes building portable and interoperable web services in the J2EE 1.4 environment. JSR 109 leverages J2EE technologies to provide an industry standard for developing and deploying web services on the J2EE platform, and it provides a service architecture that is familiar to J2EE developers. This specification outlines the lifecycle of web services to include:
- Development: Standardizes the web services programming model as well as the deployment descriptors
- Deployment: Describes the deployment actions expected of a J2EE 1.4 container
- Service publication: Specifies how the WSDL is made available to clients
- Service consumption: Standardizes the client deployment descriptors and a JNDI lookup model
J2EE Web Services
JAX-RPC is a Java API for XML-based Remote Procedure Calls (RPC). You can use it to build web services and clients that use RPC and XML. An RPC is represented using an XML-based protocol such as SOAP, which defines an envelope structure, encoding rules, and convention for representing RPC calls and responses, which are transmitted as SOAP messages over HTTP. The advantage of JAX-RPC is that it hides the complexity of SOAP messages from the developer. Here is how it works:
The developer specifies the remote procedures (web services) that can be invoked by remote clients in a Java programming language interface; the developer implements the interface. The client view of a web service is a set of methods that perform business logic on behalf of the client. A client accesses a web service using a Service Endpoint Interface as defined by JAX-RPC. Client developers create the client -- a proxy (or a local object that represents the remote service) that is automatically generated -- and then simply invoke the methods on the proxy. The developer doesn't need to worry about generating or parsing SOAP messages; this is all taken care of by the JAX-RPC runtime system. Note that J2EE web services can be invoked by any web service client, and any J2EE web service client can invoke any web service.
To get a feeling for what happens behind the scenes, consider Figure 2, which shows how a Java client communicates with a Java web service in the J2EE 1.4 platform. Note that J2EE applications can use web services published by other providers, regardless of how they are implemented. In the case of non-Java technology-based clients and services, the figure would change slightly. As mentioned earlier, all the details between the request and the response happen behind the scenes. You only deal with typical Java programming language semantics, such as method calls and data types. You need not worry about mapping Java to XML and vice-versa, or constructing SOAP messages. All this low-level work is done behind the scenes, allowing you to focus on the high-level issues.
Figure 2: A Java client calling a J2EE web service
Note that a web service client never accesses a service directly; it does so through the container. This is a good thing, since it allows a web service to benefit from the added functionality that the container provides -- such as security, enhanced logging, and quality-of-service guarantees. This means that the container frees the developer from worrying about such low-level details.
Working with JAX-RPC
When working with JAX-RPC, remember that it maps Java types to XML/WSDL definitions. The good news is that you don't need to know the details of these mappings, but you should be aware that not all J2SE classes can be used as method parameters or return types in JAX-RPC. JAX-RPC supports the following primitive data types: boolean
, byte
, double
, float
, int
, long
, short
, and arrays. In addition, it supports the following wrapper and utility classes:
java.lang.Boolean
java.lang.Byte
java.lang.Double
java.lang.Float
java.lang.Integer
java.lang.Long
java.lang.Short
java.lang.String
java.math.BigDecimal
java.math.BigInteger
java.net.URI
java.util.Calendar
java.util.Date
JAX-RPC also supports something called a value type, which is a class that can be passed between a client and a service as a parameter or a return value. A value type must follow these rules:
- It must have a public default constructor.
- It must not implement
java.rmi.Remote
. - Its fields must be JAX-RPC supported types. Also, a
public
field cannot befinal
ortransient
, and a non-public field must have the corresponding getter and setter methods.
Creating Web Services
Building an XML-RPC style web service using the J2EE 1.4 platform involves five steps:
- Design and code the web service endpoint interface.
- Implement the interface.
- Write a configuration file.
- Generate the necessary files.
- Use the
deploytool
to package the service in a WAR file and deploy it.
1. Design and Code the Service Endpoint Interface
The first step in creating a web service is to design and code its endpoint interface, in which you declare the methods that a web service remote client may invoke on the service. When developing such an interface, ensure that:
- It extends the
java.rmi.Remote
interface - It does not have constant declarations such as
public static final
- Its methods throw the
java.rmi.RemoteException
(or one of its subclasses) - Its method parameters and return data types are supported JAX-RPC types
To get started, create a directory of your choice. For the following example, I created an apps
directory under c:\sun\AppServer
and a subdirectory of apps called build
. The apps
directory contains the .java
files, and the build
directory contains the .class
, as well as other files that will be automatically generated.
The web service I will be developing for the rest of this article provides a summation service for adding two numbers. Nothing fancy, but as you will see, it will demonstrate how to develop, deploy, and use web services. Code Sample 1 shows the service endpoint interface, which is a regular Java language interface that extends the java.rmi.Remote
interface. The MathFace
interface declares one method, add
, which takes two integer values and returns an integer value representing the sum of the two integer parameters:
Code Sample 1: MathFace.java
package math;
import java.rmi.Remote;
import java.rmi.RemoteException;
public interface MathFace extends Remote {
public int add(int a, int b) throws RemoteException;
}
2. Implement the Service Endpoint Interface
The next step is to implement the MathFace
interface defined in Code Sample 1, which is quite straightforward, as shown in Code Sample 2.
Code Sample 2: MathImpl.java
package math;
import java.rmi.RemoteException;
public class MathImpl implements MathFace {
public int add(int a, int b) throws RemoteException {
return a + b;
}
}
Now, compile the .java
files specifying the .class
files to be written to the build
directory created above. The -d
option instructs the compiler to write the output .class
files into the build
directory:
prompt>javac -d build Math*.java
The next step is to define a configuration file to be passed to the wscompile
tool. Here I call the file config.xml
(and save it under the apps
directory). In this configuration file, I describe the name of the service, its namespace, the package name ( math
in this case) and the name of the interface ( MathFace
). Code Sample 3 shows the configuration file:
Code Sample 3: config.xml
<?xml version="1.0" encoding="UTF-8"?>
<configuration
xmlns="#">
<service
name="MyFirstService"
targetNamespace="urn:Foo"
typeNamespace="urn:Foo"
packageName="math">
<interface name="math.MathFace"/>
</service>
</configuration>
This file tells wscompile
to create a WSDL file with the following information:
- The service name is
MyFirstService
. - The WSDL namespace is
urn:Foo
. - The classes for the service are in the
math
package under thebuild
directory. - The service endpoint interface is
math.MathFace
.
4. Generate the Necessary Mapping Files
Now, use the wscompile
tool to generate the necessary files. Consider the following command executed from the apps
directory:
prompt> wscompile -define -mapping build/mapping.xml -d build -nd build -classpath build config.xml
This command, which reads the config.xml
file created earlier, creates the MyFirstService.wsdl
file and mapping.xml
in the build
directory. The command line options or flags are:
-define
: instructs the tool to read the service endpoint interface and create a WSDL file.-mapping
: specifies the mapping file and where it should be written.-d
and-nd
: specifies where to place generated output files and non-class output files, respectively.
Believe it or not, you have now built a web service that is ready to be packaged and deployed.
The WSDL file MyFirstService.wsdl
, generated by the wscompile
tool, is shown in Code Sample 4. This file provides an XML description (based on WSDL) of the service that clients can invoke. To understand the details of the file you need some knowledge of WSDL -- but you don't need to understand all the details, so don't worry if you have no knowledge of WSDL.
Code Sample 4: MyFirstService.wsdl
<?xml version="1.0" encoding="UTF-8"?>
<definitions name="MyFirstService" targetNamespace="urn:Foo" xmlns:tns="urn:Foo"
xmlns="http://schemas.xmlsoap.org/wsdl/" xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/">
<types/>
<message name="MathFace_add">
<part name="int_1" type="xsd:int"/>
<part name="int_2" type="xsd:int"/></message>
<message name="MathFace_addResponse">
<part name="result" type="xsd:int"/></message>
<portType name="MathFace">
<operation name="add" parameterOrder="int_1 int_2">
<input message="tns:MathFace_add"/>
<output message="tns:MathFace_addResponse"/></operation></portType>
<binding name="MathFaceBinding" type="tns:MathFace">
<soap:binding transport="http://schemas.xmlsoap.org/soap/http" style="rpc"/>
<operation name="add">
<soap:operation soapAction=""/>
<input>
<soap:body encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
use="encoded" namespace="urn:Foo"/>
</input>
<output>
<soap:body encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
use="encoded" namespace="urn:Foo"/>
</output>
</operation></binding>
<service name="MyFirstService">
<port name="MathFacePort" binding="tns:MathFaceBinding">
<soap:address location="REPLACE_WITH_ACTUAL_URL"/>
</port>
</service>
</definitions>
The mapping file, mapping.xml
, generated by the wscompile
tool is shown in Figure 5. This file follows the JSR 109 standard for Java <-> WSDL mappings. As you can see, the structure of the JAX-RPC mapping file matches closely with the structure of a WSDL file -- note the relationship between Java packages and XML namespaces. Each service offered is represented as a service-interface-mapping
element. This element contains the mapping for the fully qualified class name of the service interface, WSDL service names, and WSDL port names. In addition, the JAX-RPC mapping file provides mappings for WSDL bindings, WSDL port types, WSDL messages, and so forth. And once again, the good news is that you needn't worry about the WSDL file (Code Sample 4) of the JAX-RPC mapping file (Code Sample 5) in order to develop, deploy, and use web services.
Code Sample 5: mapping.xml
<?xml version="1.0" encoding="UTF-8"?>
<java-wsdl-mapping version="1.1" xmlns="http://www.oracle.com/webfolder/technetwork/jsc/xml/ns/j2ee/index.html"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.oracle.com/webfolder/technetwork/jsc/xml/ns/j2ee/index.html
http://www.ibm.com/webservices/xsd/j2ee_jaxrpc_mapping_1_1.xsd">
<package-mapping>
<package-type>math</package-type>
<namespaceURI>urn:Foo</namespaceURI>
</package-mapping>
<package-mapping>
<package-type>math</package-type>
<namespaceURI>urn:Foo</namespaceURI>
</package-mapping>
<service-interface-mapping>
<service-interface>math.MyFirstService</service-interface>
<wsdl-service-name
xmlns:serviceNS="urn:Foo">serviceNS:MyFirstService</wsdl-service-name>
<port-mapping>
<port-name>MathFacePort</port-name>
<java-port-name>MathFacePort</java-port-name>
</port-mapping>
</service-interface-mapping>
<service-endpoint-interface-mapping>
<service-endpoint-interface>math.MathFace</service-endpoint-interface>
<wsdl-port-type xmlns:portTypeNS="urn:Foo">portTypeNS:MathFace</wsdl-port-type>
<wsdl-binding xmlns:bindingNS="urn:Foo">bindingNS:MathFaceBinding</wsdl-binding>
<service-endpoint-method-mapping>
<java-method-name>add</java-method-name>
<wsdl-operation>add</wsdl-operation>
<method-param-parts-mapping>
<param-position>0</param-position>
<param-type>int</param-type>
<wsdl-message-mapping>
<wsdl-message
xmlns:wsdlMsgNS="urn:Foo">wsdlMsgNS:MathFace_add</wsdl-message>
<wsdl-message-part-name>int_1</wsdl-message-part-name>
<parameter-mode>IN</parameter-mode>
</wsdl-message-mapping>
</method-param-parts-mapping>
<method-param-parts-mapping>
<param-position>1</param-position>
<param-type>int</param-type>
<wsdl-message-mapping>
<wsdl-message
xmlns:wsdlMsgNS="urn:Foo">wsdlMsgNS:MathFace_add</wsdl-message>
<wsdl-message-part-name>int_2</wsdl-message-part-name>
<parameter-mode>IN</parameter-mode>
</wsdl-message-mapping>
</method-param-parts-mapping>
<wsdl-return-value-mapping>
<method-return-value>int</method-return-value>
<wsdl-message
xmlns:wsdlMsgNS="urn:Foo">wsdlMsgNS:MathFace_addResponse</wsdl-message>
<wsdl-message-part-name>result</wsdl-message-part-name>
</wsdl-return-value-mapping>
</service-endpoint-method-mapping>
</service-endpoint-interface-mapping>
</java-wsdl-mapping>
5. Packaging and Deploying the Service
A JAX-RPC web service is really a servlet (or a web component, in J2EE terminology), and hence you can use deploytool
to package and generate all the necessary configuration files, and deploy the service. If you haven't yet used the deploytool
to package and deploy applications, start the J2EE application server (or default domain) and then follow these instructions to package and deploy the math service. For the rest of the article, I will assume that the service can be accessed using the URL http://localhost:8080/math-service/math
.
Creating Web Service Clients
Now, let's create a client that accesses the math service you have just deployed. A client invokes a web service in the same way it invokes a method locally. There are three types of web service clients:
- Static Stub: A Java class that is statically bound to a service endpoint interface. A stub, or a client proxy object, defines all the methods that the service endpoint interface defines. Therefore, the client can invoke methods of a web service directly via the stub. The advantage of this is that it is simple and easy to code. The disadvantage is that the slightest change of web service definition lead to the stub being useless... and this means the stub must be regenerated. Use the static stub technique if you know that the web service is stable and is not going to change its definition. Static stub is tied to the implementation. In other words, it is implementation-specific.
- Dynamic Proxy: Supports a service endpoint interface dynamically at runtime. Here, no stub code generation is required. A proxy is obtained at runtime and requires a service endpoint interface to be instantiated. As for invocation, it is invoked in the same way as a stub. This is useful for testing web services that may change their definitions. The dynamic proxy needs to be re-instantiated but not re-generated as is the case with stub.
- Dynamic Invocation Interface (DII): Defines
javax.xml.rpc.Call
object instance for dynamic invocation. Unlike stub and proxy, it must be configured before it can be used. A client needs to provide: operation name, parameter names, types, modes, and port type. As you can tell, much more coding is involved here. The major benefit is that sinceCall
is not bound to anything, there is no impact of changes on the client side (whenever the web service definition changes). The DII client is outside the scope of this article.
1. Static Stub Client
Let's develop a stand-alone client that calls the add
method of MyFirstService
. It makes the call through a stub, or a local object that acts as a client proxy to the remote service. It is called a static stub because the stub is generated before runtime by the wscompile
tool. Before developing the Java client itself, you need to write a configuration file (in XML) that describes the location of the WSDL file ( MyFirstService.wsdl
). The configuration file is shown in Code Sample 6. I suggest that you create a new subdirectory under apps
call it static-stub
(where you save the .xml
and .java
files) and under that a build
subdirectory.
Code Sample 6: config-wsdl.xml
<?xml version="1.0" encoding="UTF-8"?>
<configuration
xmlns="#">
<wsdl location="http://localhost:8080/math-service/math?WSDL"
packageName="sstub"/>
</configuration>
As you can see, the URL http://localhost:8080/math-service/math?WSDL
identifies the location of the WSDL file for MyFirstService
. If you try this URL, you'd see something similar to Figure 3 (assuming you have deployed the web service developed above).
Figure 3: MyFirstService.wsdl
Once you have written the configuration file, you're ready to generate client stubs, using the following command:
prompt> wscompile -gen:client -d build -classpath build config-wsdl.xml
This commands reads the MyFirstService.wsdl
(the location of which is specified in the config-wsdl.xml), then generates files based on the information in the WSDL file and on the command-line flags. The -gen:client
instructs wscompile
to generate the stubs, as well as other needed runtime files such as serializers and value types. The -d
flags tells the tool to write the output to the build
subdirectory.
Now, you're ready to develop the client. A sample implementation is shown in Code Sample 7. Note that the stub generated earlier is used, and a service endpoint address is used to locate the service http://localhost:8080/math-service/math
. As you can see, a stub object is created using the MathFirstService_Impl
object generated by the wscompile
tool; the endpoint address that the stub uses to access the service is set; and then the stub is cast to the MathFace service endpoint interface; finally, the add
method is invoked.
Code Sample 7: MathClient.java
package sstub;
import javax.xml.rpc.Stub;
public class MathClient {
private String endpointAddress;
public static void main(String argv[]) {
try {
// Invoke createProxy() to create a stub object
Stub stub = createProxy();
// Set the endpoint address the stub uses to access the service
stub._setProperty(javax.xml.rpc.Stub.ENDPOINT_ADDRESS_PROPERTY,
"http://localhost:8080/math-service/math");
// Cast the stub to the service endpoint interface (MathFace)
MathFace math = (MathFace) stub;
// Invoke the add method
System.out.println(math.add(12, 24));
} catch (Exception ex) {
ex.printStackTrace();
}
}
private static Stub createProxy() {
// Create a stub object
// Note that MyFirstService_Impl, generated by wscompile, is implementation-specific
return (Stub) (new MyFirstService_Impl().getMathFacePort());
}
}
You can now compile MathClient.java
and write the output to the build
directory:
C:\sun\APPSER~1\apps\static-stub> javac -classpath build -d build MathClient.java
Note: If you don't already use Ant, I'd recommend that you learn how to use it. In this article, however, and for the benefit of those who do not use it, I am not using Ant. The easiest way to get things running is to copy all lib/*.jar
and lib/endorsed/*.jar
to jdk\jre\lib\ext
; this way, you don't have to worry about the classpath for the JAX-RPC classes.
Run the client:
C:\sun\APPSER~1\apps\static-stub> java -classpath build sstub.MathClient
36
2. Dynamic Proxy
The client in this case calls a remote procedure through a dynamic proxy or a class that is created at runtime. Note that in the case of the static stub, the code relied on an implementation-specific class, but here (dynamic proxy) the code doesn't have this limitation. The first step is to create a configuration file. You can use the one in Code Sample 6 but make sure you change the packageName
to dynamicproxy
or whatever you like. Again, create a directory dynamic-proxy
under apps
, and build
subdirectory under dynamic-proxy
.
Now, use the wscompile
to generate the needed interfaces:
C:\Sun\APPSER~1\apps\dynamic-proxy> wscompile -import -d build -nd build -f:norpc structures -classpath build config-wsdl.xml
Now, develop the client. Code Sample 8 provides a sample implementation. Here, an instance of the service factory is created. The service factory is used to create a service object that acts as a factory for proxies. As you can see, the createService
method takes two parameters: a URL of the WSDL files and a QName
object, which is a tuple that represents an XML qualified name -- the namespace URI and the local part of the qualified name (the service name). A proxy object is then created, and finally the add
method is invoked on that object.
Code Sample 8: MathClient.java
package dynamicproxy;
import java.net.URL;
import javax.xml.rpc.Service;
import javax.xml.rpc.JAXRPCException;
import javax.xml.namespace.QName;
import javax.xml.rpc.ServiceFactory;
import dynamicproxy.FirstIF;
public class MathClient {
public static void main(String[] args) {
try {
String nameSpaceUri = "urn:Foo";
String serviceName = "MyFirstService";
String portName = "MathFacePort";
// Specify the location of the WSDL file
URL url = new URL("http://localhost:8080/math-service/math?WSDL");
// Create an instance of service factory
ServiceFactory serviceFactory = ServiceFactory.newInstance();
// Create a service object to act as a factory for proxies.
Service mathService = serviceFactory.createService(url,
new QName(nameSpaceUri, serviceName));
// Create a proxy
dynamicproxy.MathFace
myProxy = (dynamicproxy.MathFace) mathService.getPort(new
QName(nameSpaceUri,
portName), dynamicproxy.MathFace.class);
// Invoke the add method
System.out.println(myProxy.add(23, 12));
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
Compile the client:
c:\Sun\APPSER~1\apps\dynamic-proxy> javac -classpath build -d build MathClient.java
And, now run the client:
C:\Sun\APPSER~1\apps\dynamic-proxy> java -classpath build dynamicproxy.MathClient
35
Command-Line J2EE Application Client
Unlike a stand-alone client, a J2EE application client can obtain a service interface using JNDI lookup. In this case, the client developer defines a logical JNDI name (service reference) for the web service. The container binds the service interface implementation under the client's environment context java:comp/env
using the logical name of the service reference. The following snippet of code shows an example:
try {
Context ic = new InitialContext();
MyFirstService myFirstService = (MyFirstService)
ic.lookup("java:comp/env/service/MyFirst");
j2ee.MathFace mathPort = myFirstService.getMathFacePort();
((Stub) mathPort)._setProperty(Stub.ENDPOINT_ADDRESS_PROPERTY,
"http://localhost:8080/math-service/math");
System.out.println(mathPort.add(22, 1));
} catch (Exception ex) {
ex.printStackTrace();
System.exit(1);
}
Browser-Based Client
Finally, let's see how to develop a web client in which the web service is invoked from a browser-based form (here we use static stub). To start, you need a configuration file similar to the one in Code Sample 6. Just change the packageName
to whatever you like; here, it's webclient
. Once you have that, generate the client stub using the following command:
c:\Sun\APPSER~1\aps\web\> wscompile -gen:client -d build -classpath build config-wsdl.xml
The next step is to write the web client as a servlet or a JavaServer Pages technology page (JSP). Code Samples 9 and 10 show my JSP implementation of the web client. Code Sample 9 presents the form to the user and retrieves the parameters, and Code Sample 10 (based on static stub) is similar to the client in Code Sample 7.
Code Sample 9: math.jsp
<html>
<head><title>Hello</title></head>
<body bgcolor="#ffcccc">
<h4>Welcome to the Math Web Service</h4>
<form method="get">
<input type="text" name="num1" size="25">
<p></p>
<input type="text" name="num2" size="25">
<p></p>
<input type="submit" value="AddNumbers">
<input type="reset" value="Reset">
</form>
<%
String str1 = request.getParameter("num1");
String str2 = request.getParameter("num2");
if ( str1 != null && str2 != null ) {
%>
<%@include file="add.jsp" %>
<%
}
%>
</body>
</html>
Code Sample 10: add.jsp
<%@ page import="javax.xml.rpc.Stub,javax.naming.*,webclient.*" %>
<%
String resp = null;
int result = 0;
try {
// Create a stub object
Stub stub = (Stub)(new MyFirstService_Impl().getMathFacePort());
// Set the endpoint address the stub uses to access the service
stub._setProperty(javax.xml.rpc.Stub.ENDPOINT_ADDRESS_PROPERTY,
"http://localhost:8080/math-service/math");
// Cast the stub to MathFace
MathFace math = (MathFace)stub;
// Retrieve and parse the parameters from the request message
int x = Integer.parseInt(request.getParameter("num1"));
int y = Integer.parseInt(request.getParameter("num2"));
// Invoke the method
result = math.add(x, y);
} catch (Exception ex) {
}
%>
<h4>The sum is: <%=result%></h4>
The next step is to deploy the web client as a JSP web component using the deploytool
. Again, if you haven't used deploytool
, start the J2EE application server (if it is not already running) and follow these instructions (steps 5 to 11) to package and deploy the web client as a JSP web component. During deployment, I specified this URL http://localhost:8080/math-webservice/add
to be used to access the service. Figure 4 shows the a client calling a web service into action.
Figure 4: A JSP-based web client calling a web service
Conclusion
Web services are the next step in the web's evolution, since they promise the infrastructure and tools for automation of business-to-business relationships over the Internet. The integration of web services into the J2EE 1.4 platform simplifies the task of building and consuming web services, by freeing the Java technology developer from the low-level details of XML and web services standards.
The code samples in this article demonstrate how easy it is to develop web services using J2EE 1.4. It's important to note that such services can be invoked by non-J2EE clients; also, J2EE-based clients can consume web services developed using other technologies.
In a world of constantly changing technology requirements, J2EE is leading the way to enable the Enterprise to integrate the latest and greatest technology standards, while still leveraging existing IT investment.
Acknowledgments
Special thanks to Vijay Ramachandran and Dennis MacNeil of Sun Microsystems, whose feedback helped me improve this article.