Showing posts with label Web Services. Show all posts
Showing posts with label Web Services. Show all posts

Thursday, March 27, 2008

Web Services in Java (Part 1)


Web Services in Java (Part 1)




When learning Web Services in Java, you have two complicating factors:

1) Web Services in Java are built on a tall stack of Java technologies and Web specifications: JAXB, JAX-RPC, JAXM, JAXP, SAAJ, WSDL, UUDI, etc. And to gain a detailed understanding of Web Services you need some understanding of all of these technologies. That's a steep learning curve!

2) There are multiple Web Service implementations, toolkits, and IDE plugins for in Java. All of them work differently to some extent, and all of them are in some degree of flux as these technologies and toolkits evolve and mature. Even when two separate Web Service toolkits adhere to the same standards, they may differ greatly in their implementation.

These two factors present a significant barrier to developers learning how to create robust and secure Web Serivces. And this situation is further complicated by lots of poor and outdated documentation on the subject. Most books and online tutorials I have read take the approach of teaching all the details of all of the myriad technologies (JAXB, JAXM, SAAJ, JAX-RPC, etc.) before you even begin to construct your first "Hello World" Web Service. This may be a convenient way for the author to package her or his knowledge, but it's not a very effective way to learn. I find that many developers learn fastest by jumping right in and getting something working immediately, even if they don't understand how it works, and then expand their knowledge from there in any direction they want.

I suspect that authors tend to focus so much on the Java API's and Web technologies because those are the main elements of Web Services that are universal across all implementations. If they go down the path of creating a complete Web Service by example, they would have to cover different ways of doing a Web Service with each different toolkits, IDE plugins, and Application Servers; and anything they wrote would be outdated in a year or less. So, it's often easier to just present the core technologies and hope the developer can figure out the rest. (ha)

For this article, I'm going to demonstrate creating a Web Service using the Java Web Service Developer Pack 2.0 from Sun, and deploying the Web Serivice to JBoss 4.0. So if you are using Apache Axis, this example probably won't work for you exactly as-is. Sorry. That's the nature of this beast. I plan writing a future article on how to create an Apache Axis Web Service.

The good news is that it doesn't take very much to get a simple Web Service working. The modern toolkits have become easier to use, and do most of the nasty detail work for you. Once you get a simple Web Service working, you can expand to a reasonable set of production-grade functionality without tackling all the learning curves at once. Then, over time, you can choose to drill down into individual advanced aspects of Web Services as the need arises.

For this example, there are at least 4 different paths I could take to start constructing this Web Service, depending on various design decisions and considerations. (WSDL-centric vs. Java-centric, EJB vs. Servlet, etc) But I'm not going to get into all of that discussion. I'm just going to charge ahead on one simple path, and save all the alternatives and philosophy for another article.

Here we go!

Java Web Services : Step 1 - Create a new Web Application

The Web Service for this example is going to be deployed as a regular WAR file. So in your IDE of choice, create a standard Web Application directory structure including source (src) and WebRoot/WEB-INF directories.

There are no Web Service JAR files needed for your WEB-INF/lib directory. Your application server should already have Web Service support libraries installed. In our case, JBoss 4.0 comes with Web Service support.


Java Web Services : Step 2 - Create a Java Interface for your Web Service

This interface will represent the functionality you want to expose with this Web Service.

You can name your interface anything you want. I recommend naming it <Domain>Server where "<Domain>" is any name that represents the services offered. Like "WeatherServer" or "UserServer" or "OrderServer", etc.

This interface must extend the java.rmi.Remote interface. And each method in your interface must throw java.rmi.RemoteException.

Your interface can declare any regular Java methods. The method parameters and method return types can be of any primitive type (int, float, long, etc.), or standard type (String, Long, Calendar, etc.) or an array of objects. You can also use any custom Java Objects of your own. However, there are some limits and design considerations you'll discover when using your own objects. For now, try and keep it simple.

Here is my sample interface:


package com.example.webservice.user;

import java.rmi.Remote;
import java.rmi.RemoteException;
import com.example.webservice.exception.GeneralException;

public interface AccountManagementServer extends Remote{

public boolean isUserKeyValid(String pid)
throws RemoteException, NoSuchAccountException, GeneralException;

public PortableUser getUser(String userName)
throws RemoteException, NoSuchUserException, GeneralException;
}






These methods throw some custom exceptions I have created.

Here is the code for one of those exceptions:


package com.example.webservice.exception;
public class GeneralException extends Exception {
public GeneralException(String message) { super(message); }
public String getMessage() { return super.getMessage(); }
}

There's nothing difficult or special in creating an Exception that can be thrown across a Web Service, but one minor rule you have to follow is that there must be a getter method for any parameter used in a constructor. So, since this constructor takes a "message" as a parameter, we must declare a getMessage() method in this class. (The parent class "Exception" has a getMessage method, but that doesn't count.)


Java Web Service : Step 3 - Create the Implementation


Create a Java class that implements the Web Service interface. Also create any support classes that you want to use.

This class must implement javax.xml.rpc.server.ServiceLifecycle, and implement the interface you defined previously.

You can name this implementation class anything you want, but I recommend just appending "Impl" to the Interface Name. Like "WeatherServerImpl" or "OrderServerImpl".


package com.example.webservice.user;

import java.rmi.RemoteException;
import javax.xml.rpc.ServiceException;
import javax.xml.rpc.server.ServiceLifecycle;
import javax.xml.rpc.server.ServletEndpointContext;
import com.example.webservice.exception.GeneralException;
import com.example.webservice.exception.NoSuchAccountException;
import com.example.webservice.exception.NoSuchUserException;

public class AccountManagementServerImpl implements
AccountManagementServer, ServiceLifecycle {

private ServletEndpointContext ctx;

public boolean isUserKeyValid(String pid) throws NoSuchAccountException,
GeneralException
{
try {
return UserService.isUserKeyValid(pid);
} catch (NoSuchAccountException nsae) {
throw nsae;
} catch (Exception ex) {
throw new GeneralException(ex.toString());
}
}

public PortableUser getUser(String userName)
throws RemoteException, NoSuchUserException, GeneralException {

User user = null;
try {
user = UserService.getUser(userName);
} catch (NoSuchUserException nsue) {
throw nsue;
} catch (Exception ex) {
throw new GeneralException(ex.toString());
}
if (user == null) return null;

//Convert the big User object down to the small PortableUser object
PortableUser pu = new PortableUser();
pu.setUserName(user.getUserName());
pu.setPassword(user.getPassword());
pu.setPortalId(user.getPortalId());
return pu;
}

public void init(Object context) throws ServiceException {
ctx = (ServletEndpointContext) context;
}

public void destroy() {
}
}

The code above uses another application service called "UserService". The source code is not provided here, but it is not important. The key concept is that your Web Service implementation should re-use an pre-existing application services whenever possible. Keep as much business logic out of your Web Service as possible.

In the above example, one of the methods in my Web Service returns an object of type "PortableUser". This is just a simple data class that I created as follows:


package com.example.webservice.user;

public class PortableUser {
private String userName;
public String getUserName() { return userName; }
public void setUserName(String userName) { this.userName = userName;}

private String portalId;
public String getPortalId() { return portalId; }
public void setPortalId(String portalId) { this.portalId = portalId;}

private String password;
public String getPassword() { return password; }
public void setPassword(String password) { this.password = password;}
}

I created the object "PortableUser", because my application's regular "User" object is a huge object with a large number of methods and nested child objects. If I had used my User object as a return value for my Web Service, performance would have suffered significantly.

Besides, I only wanted to expose a subset of my User object anyway. In cases like these, you should make lightweight Java classes (Data Transfer Objects) that package the values you want to use in your Web Service.


Java Web Service : Step 4 - Create a configuration file

We are now going to use a Web Service generator tool to create the Web Service WSDL and JAX-RPC mapping files. If you don't know what WSDL and JAX-RPC is, don't worry about it. The WSDL file describes your Web Service to the outside world, and the JAX-RPC mapping files tie the WSDL into your Java Classes.

The Web Service generator tools can generate WSDL files from Java classes, OR they can generate Java classes from WSDL files. In our case, we started by defining a Java class (an interface), and so we will be generating the WSDL. But there are times when you might want to design a Web Service starting with a hand-made WSDL file. But that's another design discussion to be addressed in a future article.

In Apache Axis, the two relevant tools are called (appropriately) "WSDL2Java" and "Java2WSDL".

In the JWSDP, there is only one tool, called "wscompile", and it can perform either conversion. I'm going to use wscompile for this example.

For wscompile, you will need to create a config file for generating your Web Service. This config file is only used to generate the WSDL and mapping files. You can name the config file anything you want, but I recommend you name it config-<Domain>Service.xml. Example: "config-UserService.xml". If you have more than one Web Service package to generate, you will need to have more than one config file, which is why I recommend against naming it just "config.xml".

There are 5 things you must define in your config file. Two of these are namespaces, and there really are a lot of different ways you could name your namespaces, but I'll leave that for a future discussion. What I offer below are just loose suggestions we can use for now:



config-AccountManagementService.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration>
<configuration xmlns="http://java.sun.com/xml/ns/jax-rpc/ri/config">
<service name="AccountManagementService"
targetNamespace="http://com.example.webservice.user/"
typeNamespace="http://com.example.webservice.user/types"
packageName="com.example.webservice.user">
<interface name="com.example.webservice.user.AccountManagementServer"/>
</service>
</configuration>

Notes about the above configuration file:

  • The Web Service name is "AccountManagementService".

  • The "targetNamespace" defines the namespace to use for the Web Service.*

  • The "typeNamespace" defines the namespace for your Web Services types. I simply added "types" to the end of my targetNamespace.

  • The packageName is the name of the Java package where your Web Service classes live, and where any generated classes are to be placed. (We will not be generating classes in this example.)

  • The interface name is the full name of your Java Interface for the Web Service.


*A namespace is just a universally unique string. It is not a URL, even though it's a common practice to format namespaces as URL's. It's also common practice to include Java package name information in the namespace name. The format I've chosen here does both....and I'm starting to think that's confusing.

Another way to name a namespace is to specify a real http domain, and then use the URL path to try and specify the remaining parts of your package path that aren't referenced in the domain (in reverse order). If your website is "myapp.example.com", and your package name is com.example.myapp.webservice.user, then your namespace could be "http://myapp.example.com/webservice/user". This is less confusing, but not representative of the true package name.

The source of the confusion here is that Java packages are named in descending order (com.example.myapp) but URL's are always named in ascending order (myapp.example.com). And if you make any effort to mix the two things, you'll have problems. =)

So for my example Web Service here, don't take my namespace naming method too seriously.


Java Web Service : Step 5 - Generate the WSDL file and the JAX-RPC mapping files


wscompile.sh is part of the Java WebServices Developer Pack from Sun. And in this example, I'm going to show using wscompile from the command line, and using wscompile from an ant task. You can choose to run wscompile either way. The command line version might get you going faster, but for long-term manageability I recommend eventually creating the ant task.

The Java Web Services Development Pack (with wscompile) can be downloaded from here: http://java.sun.com/webservices/jwsdp/index.jsp

You could also use Java2WSDL from Apache Axis. Both will generate somewhat similar results, assuming both tools have done a good job of adhering to the same versions of the same standards.

Once you have the JWSDP installed, you can run wscompile.sh from the command-line as follows:

wscompile.sh -cp WebRoot/WEB-INF/classes -verbose -define -f:rpcliteral
-nd WebRoot/WEB-INF/wsdl
-mapping WebRoot/WEB-INF/mapping-AccountManagementService.xml
config-AccountManagementService.xml

You can use "wscompile.sh -help" to get detailed information on what all these parameters do.

For automated building and less command-line fussing, I recommend using an ant task for wscompile. To run the wscompile ant task, you need the following JAR files in your ant's classpath. All of these JAR files can be found in the JWSDP:

activation.jar
jaas.jar
jaxrpc-api.jar
jaxrpc-impl.jar
jaxrpc-spi.jar
jta-spec1_0_1.jar
mail.jar
relaxngDatatype.jar
resolver.jar
saaj-api.jar
saaj-impl.jar
xmlsec.jar
xsdlib.jar

Once these libraries are in your ant's path, the following task will generate the WSDL and mapping files you need:

<taskdef name="wscompile" classname="com.sun.xml.rpc.tools.ant.Wscompile" />
<target name="generate">
<wscompile verbose="true" classpath="WebRoot/WEB-INF/classes"
define="true" features="rpcliteral" nonclassdir="WebRoot/WEB-INF/wsdl"
mapping="WebRoot/WEB-INF/mapping-AccountManagementService.xml"
config="config-AccountManagementService.xml"/>
</target>

Notes about the above ant task:

  • "classpath" is the path of your interface and related classes, as well as any external jars your code needs.

  • "define" indicates that we are generating just the WSDL and mapping files, and no other classes.

  • "features" are the various options you can use for specifying what type of WSDL to generate.

  • "mapping" is the location of the output JAX-RPC mapping files.

  • "nonclassdir" is the location to place the WSDL file (and other nonclass files that are generated)

  • "config" is the name of the config file for wscompile



Whether you use the ant file, or the command-line, you must execute this wscompile from the project's root directory, because that's how I've setup this example. (That's why you see "WebRoot/WEB-INF" prepended to the wsdl and mapping locations.) You could also run wscompile from the WebRoot/WEB-INF directory itself, and then you wouldn't need those directories specified in all your parameters above.



Java Web Service : Step 6 - Add your service to your web.xml file

A Web Service can either be created as a Web Component (essentially a servlet), or an EJB. In this example, I have created this Web Service to be a Web Component, and so it must be defined in web.xml as a servlet. The servlet-class is just the Web Service implementation class I've already created.

<?xml version="1.0" encoding="UTF-8"?>

<web-app xmlns=http://java.sun.com/xml/ns/j2ee
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee
http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"
version="2.4">

<servlet>
<servlet-name>AccountManagementService</servlet-name>
<servlet-class>com.example.webservice.user.AccountManagementServerImpl</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>AccountManagementService</servlet-name>
<url-pattern>/AccountManagementService</url-pattern>
</servlet-mapping>
</web-app>



Java Web Service : Step 7 - Add your service to your webservices.xml file

If you don't have a webservices.xml file, you can just add one to your WEB-INF directory. This is the file that will connect all the pieces together, and specifies which WSDL file goes with which mapping file and which servlet.

Add service to webservices.xml


<?xml version="1.0" encoding="UTF-8"?>

<webservices
xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:impl="http://com.example.webservice.user/"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation=
"http://java.sun.com/xml/ns/j2ee
http://www.ibm.com/webservices/xsd/j2ee_web_services_1_1.xsd"
version="1.1">

<webservice-description>
<webservice-description-name>AccountManagementService</webservice-description-name>
<wsdl-file>WEB-INF/wsdl/AccountManagementService.wsdl</wsdl-file>
<jaxrpc-mapping-file>
WEB-INF/mapping-AccountManagementService.xml
</jaxrpc-mapping-file>
<port-component>
<port-component-name>PortComponent</port-component-name>
<wsdl-port>impl:AccountManagementServerPort</wsdl-port>
<service-endpoint-interface>
com.example.webservice.user.AccountManagementServer
</service-endpoint-interface>
<service-impl-bean>
<servlet-link>AccountManagementService</servlet-link>
</service-impl-bean>
</port-component>
</webservice-description>

</webservices>

Here are the key parts of this file you have to get right:

  1. The xmlns:impl (near the top) must be the same as the namespace you used in your config file for generating the WSDL.

  2. The wsdl-file specifies the location of your WSDL file relative from WebRoot

  3. The jaxrpc-mapping-file specifies your mapping file, also relative from WebRoot.

  4. The wsdl-port will be "impl:" + <your interface class name> + "Port". When you learn more, you'll learn what a "Port" is, and see how the Port name is defined in the WSDL file, and how it is referenced by this "impl" namespace.

  5. The service-endpoint-interface is the full name of your interface class.

  6. The servlet-link is the "servlet-name" you defined in your web.xml



Java Web Service : Step 8 - Package and deploy these files

There's nothing special about building a Web Service war file. Just package up all if the class files you have created, plus the generated XML and WSDL files. A simple ant-build for the war should suffice:



<target name="build-war">
<war destfile="wsexample.war" webxml="WebRoot/WEB-INF/web.xml">
<classes dir="WebRoot/WEB-INF/classes" includes="**/*"/>
<lib dir="WebRoot/WEB-INF/lib" includes="*.jar"/>
<webinf dir="WebRoot/WEB-INF" includes="**/*.xml,**/*.wsdl"
excludes="web.xml"/>
</war>
</target>


Deploy your WAR file to your application server (in my case JBoss). If all is well, you should see messages in the log that indicate your Web Service has self-installed gracefully. (Note: When using JBoss, you must have the jboss-ws4ee.sar module installed).

Java Web Service : Step 9 - Verify the Web Service is installed

You can test that your Web Service is installed using the following URL:

http://localhost/wsexample/AccountManagementService?wsdl

When you call your Web Service with the "?wsdl" parameter, it should respond by giving you it's WSDL.

Alternatively, you can go to http://localhost/ws4ee. You'll get two links that will verify that Web Services are running properly on your server, and another link that will show that your Web Service is installed.

These tests will only ensure that your Web Serivice has a proper WSDL file. To really test the full functionality of your Web Service, you need to create some kind of test client that can call your services. You can either write a simple client of your own (covered in the next step), or you can use some of the various IDE plugin's on the market (like the Web Services Explorer for Eclipse) that will self-discover the details of your service.


Java Web Service : Step 10 - Test the Web Service

Making a Web Service client is just a miniature version of the previous steps you went through to make a Web Service itself.

You start off by making any kind of Java project you want. It doesn't have to be a Web Application.

This time we'll be generating client classes instead of WSDL. Create your configuration file for wscompile as follows:

config-AccountManagementServer.xml

<configuration
xmlns="http://java.sun.com/xml/ns/jax-rpc/ri/config">
<wsdl location="http://localhost/wsexample/AccountManagementService?wsdl"
packageName="com.example.webservice.user.client"/>
</configuration>

Specify the URL of your Web Service (with ?wsdl appended) in the "location" attribute. Specify the package name of the classes to generate in the "packageName" attribute.

Next, run wscompile as follows:

wscompile.sh -verbose -gen:client -f:rpcliteral -s src -d src -keep
-mapping mapping-AccountManagementServer.xml
config-AccountManagementServer.xml

OR, if using ant:

<taskdef name="wscompile" classname="com.sun.xml.rpc.tools.ant.Wscompile" />
<target name="generate">
<wscompile verbose="true" client="true" features="rpcliteral" sourceBase="src"
base="src" keep="true" mapping="mapping-AccountManagementService.xml"
config="config-AccountManagementService.xml"/>
</target>

Finally, here is a Test Class you can run that calls the Web Service methods:


package com.example.webservice.user.test;

import java.net.URL;
import javax.xml.namespace.QName;
import javax.xml.rpc.Service;
import javax.xml.rpc.ServiceFactory;
import com.example.webservice.user.client.AccountManagementServer;
import com.example.webservice.user.client.PortableUser;

public class ServiceTest {

public static final String host = "http://localhost";
public static final String path = "/wsexample";

public static void main(String args[]) {
try {
URL url = new URL(host + path + "/AccountManagementService?wsdl");
QName qname = new QName("http://com.example.webservice.user/",
"AccountManagementService");
ServiceFactory factory = ServiceFactory.newInstance();
Service service = factory.createService(url, qname);
AccountManagementServer userServer = (AccountManagementServer)
service.getPort(AccountManagementServer.class);
PortableUser user = userServer.getUser("demo");
System.out.println(user.getUserName());
System.out.println(user.getPortalId());
System.out.println(user.getPassword());
} catch (Exception e) {
System.out.println("ServiceTest: " + e);
}
}
}

And that concludes our end-to-end Web Service example. =)





Monday, March 24, 2008

Simplify Your Web Services Development with JSR 181

Source

By Ayyappan Gandhirajan

Go to page: 1 2 3 Next

Web Services are a true interoperable technology that enables disparate systems talk to each other using a common protocol. After realizing the potentials of Web Services in Application Integration and Service Oriented Architecture (SOA), the list of companies that embraces Web Services to gain edge over competitors is growing steadily. For the standardization of Web Service description, invocation and management, there are several specifications and standards available from Consortiums like W3C and OASIS. However, there are still issues in developing portable Web Services, which could run in any J2EE runtime environment. Though J2EE 1.4 tried to standardize the Web Service deployment process, the developers still have to create the deployment configuration files, create the WSDL from the Java class and package them together.

In order to trim down the complexities involved in the configuration and deployment of Web Services, the Java Specification Request (JSR) community endeavors to solve the problem by presenting us two JSRs:

  • JSR 181 — Web Services Metadata for the JavaTM Platform
    • Standardize the development of Web Service interfaces
  • JSR 921 — Implementing Enterprise Web Services 1.1
    • Standardize the vendor implementation of Web Services

This article is an attempt to highlight the features of JSR 181 specification and show you how easily you can build and deploy JSR 181 Web Services in WebLogic application server.

JSR 181 Web Services

JSR 181 or Web Services Metadata for the Java Platform is a Java Specification Request that defines an annotated Java format that uses Java Language Metadata (JSR 175) to enable easy definition of Java Web Services in a J2EE container. To put simply, JSR 181 enables developers to create portable Java Web Services from a simple Plain Old Java Object (POJO) class by adding annotations.

This Web Service development model may be related to JSP model where a JSP file is translated into a Servlet that gets in turn compiled to a class file that finally runs inside the container. The JSP model frees the developers from manual compilation and deployment of Servlets into container. Similarly in this model too, Web Service developers defines only the annotated Java Web Service (JWS) file and leave the job of creating necessary deployment and configuration files to the vendor (who actually implements Web Services based on JSR 921 specification). For example, WebLogic application server provides an Ant task called jwsc to create the JSR 921 compliant Web Service implementations. As the generation of JSR 921 compliant Web Services is hidden to developers, they can focus on developing core business services rather than worrying on learning and implementing generalized APIs and deployment descriptors.

The annotated Web Service development model also provides fine-grained control over exposing the Web Services. It enables the developers to expose the entire class or only the selected methods as the Web Service. This feature may be of great help when the Java class contains core business methods bundled along with non-business methods and/or data service methods, which the developer might not want to expose as services. In this scenario, the developer may take advantage of the annotations to specify how a Web Service should be exposed to outside world.

Putting simply, JSR 181 is a specification to define standard and portable Web Services. It offers the following benefits:

  • Provide a simplified model for developing Web Services
  • Abstract the implementation details
  • Achieve robustness, easy maintenance, and high interoperability

Sample Web Service Development

This section adopts the incremental development approach to guide you through developing a sample JSR 181 Web Service and deploying in WebLogic application server 10.0.

In the example, you will build a "Zip Code Finder" Web Service that has a single operation namely getZipCode. For a given pair of city and state, the getZipCode operation will return the corresponding zip code. Following are the step-by-step instructions:

Step 1 — Service end point object

The service end point object here is a simple POJO class that is going to be exposed as Web Service. This POJO class is a mock implementation of Zip Code Finder Service that returns 08817 (i.e., the zip code for "Edison, NJ") for any input pair of city and state. Listing 1 shows the complete code implementation.

Listing 1: Service end point object

package service;

public class ZipCodeFinderService {
public String getZipCode( String city, String state ) {
return "08817";
}
}

Step 2 — Declare POJO as Web Service

JSR 181 API provides an annotation type called @WebService to mark the Java class as Web Service. The @WebService annotation type has five attributes, which can be optionally used to define the port type and service name of the Web Service. Some of the important attributes are given below:

  • Name — Maps to wsdl:portType in WSDL 1.1
  • Service name — Maps to wsdl:service in WSDL 1.1
  • Target name space — Maps to targetNamespace in WSDL 1.1

Listing 2 shows the complete code implementation with @WebService annotation type:

Listing 2: Declaring WebService

package service;
import javax.jws.WebService;

@WebService ( name = "ZipCodeFinder",
serviceName="ZipCodeFinder",
targetNamespace = "http://sampleweb.com/services")


public class ZipCodeFinderService {
public String getZipCode( String city, String state ) {
return "08817";
}
}

The highlighted lines in Listing 2 (and the highlighted lines in rest of the listings in this article) will show you the incremental change that is done to the code displayed in Listing 1.

Step 3 — Declare SOAP Binding

This @SOAPBinding annotation type is used to define the Web Service binding. The Web Service developer may assign proper values for message encoding and format. This annotation type has following important attributes:

  • Messaging Style
    • Encoding style for message that is transported in the wire
    • Possible values: RPC or DOCUMENT
    • Default: DOCUMENT
  • Messaging Use
    • Formatting style for message that is transported in the wire
    • Possible values: ENCODED or LITERAL
    • Default: LITERAL
  • Parameter Style
    • Decides whether operation name needs to be part of the SOAP body
    • Possible values: BARE or WRAPPED
    • Default: WRAPPED

Listing 3 shows the code implementation along with @SOAPBinding annotation type:

Listing 3: Define SOAP Binding

package service;
import javax.jws.WebService;
import javax.jws.soap.SOAPBinding;

@WebService( name = "ZipCodeFinder",
serviceName="ZipCodeFinder",
targetNamespace = "http://sampleweb.com/services")

@SOAPBinding( style = SOAPBinding.Style.DOCUMENT,
use = SOAPBinding.Use.LITERAL,
parameterStyle = SOAPBinding.ParameterStyle.WRAPPED )


public class ZipCodeFinderService {
public String getZipCode( String city, String state ) {
return "08817";
}
}

Step 4 — Optional Transport Declaration

You can use an optional WebLogic annotation type (not a standard) to refine the Web Service further. The @WLHttpTransport annotation type helps you change the context and service URI of the Web Service. The important attributes are:

  • Context Path — The context path of the Web Service URL
  • Service URI — Information next to context path in the Web Service URL

Listing 4 shows the implementation of the optional transport declaration:

Listing 4: Defining optional transport

package service;
import javax.jws.WebService;
import javax.jws.soap.SOAPBinding;
import weblogic.jws.WLHttpTransport;

@WebService( name = "ZipCodeFinder",
serviceName="ZipCodeFinder",
targetNamespace = "http://sampleweb.com/services")

@SOAPBinding( style = SOAPBinding.Style.DOCUMENT,
use = SOAPBinding.Use.LITERAL,
parameterStyle = SOAPBinding.ParameterStyle.WRAPPED )

@WLHttpTransport( contextPath = "ZipCodeServices",
serviceUri = "ZipCodeFinderService" )


public class ZipCodeFinderService {
public String getZipCode( String city, String state ) {
return "08817";
}
}

Step 5 — Declare Web Method

This section will help you how to expose selectively the operations in the POJO class. For some scenario, you might want only to expose business operations and bar data operations in the Java class. In that scenario, you may find @WebMethod annotation type useful.

The @WebMethod has the following attributes:

  • Operation name — Name of the Java method that needs to be exposed as operation
  • Action — SOAP action

Listing 5 shows the implementation of the Web method:

Listing 5: Define WebMethod

package service;
import javax.jws.WebMethod;
import javax.jws.WebService;
import javax.jws.soap.SOAPBinding;
import weblogic.jws.WLHttpTransport;

@WebService( name = "ZipCodeFinder",
serviceName="ZipCodeFinder",
targetNamespace = "http://sampleweb.com/services")

@SOAPBinding( style = SOAPBinding.Style.DOCUMENT,
use = SOAPBinding.Use.LITERAL,
parameterStyle = SOAPBinding.ParameterStyle.WRAPPED )

@WLHttpTransport( contextPath = "ZipCodeServices",
serviceUri = "ZipCodeFinderService" )

@WebMethod( operationName = "getZipCode" )
public class ZipCodeFinderService {
public String getZipCode( String city, String state ) {
return "08817";
}
}

Step 6 — Build file

To reiterate, the Web Service developer needs only to define the Web Service interfaces using JSR 181 annotation but does not need to worry about the remaining deployment process. The corresponding vendor platform, in our case WebLogic, will take care of implementing (or generating) necessary classes and configuration files, which are JSR 921 compliant.

As mentioned earlier, WebLogic application server provides an ant task namely jwsc. The ant task will take a few inputs like name of the annotated Web Service file name and build directory and then create the necessary files in EAR format, which can be deployed into WebLogic application server using a separate ant task (wldeploy) or manual deployment via administration console.

Listing 6 shows a sample ant configuration file with two important tasks:

  1. Build
    1. Clean directories and files generated as part of previous build (if any)
    2. Use jwsc to generate Web Service deployment files in EAR format
  2. Deploy
    1. Use wldeploy ant task to deploy the ear into WebLogic application server

Listing 6: Build file for building and deploying Web Service






jwsc"
classname="weblogic.wsee.tools.anttasks.JwscTask" />
all" depends="build, deploy" />
build" depends="clean, server" />
clean">


server">

srcdir="${basedir}/src/service"
destdir="${ear.dir}"
classpath="${java.class.path}"
fork="true"
keepGenerated="true"
deprecation="${deprecation}"
debug="${debug}"
verbose="false">



deploy">
<wldeploy action="deploy"
source="${ear.dir}" user="weblogic"
password="weblogic" verbose="true"
adminurl="t3://localhost:7001"
targets="AdminServer" />


Step 7 — Running build file

In order to run ant build and deploy targets, you will need to do the following steps:

  1. Open command prompt
  2. Call %WL_HOME%\server\bin\setWLSEnv.cmd
    1. This will set up all necessary path and classpath
  3. Now, run "ant build deploy"
    1. The build target will create necessary classes in a proper layout
    2. The deploy target will deploy the ear into application server

Step 8 — Testing

Now all dirty jobs of creating Java class, build files, running ant tasks are over. It is time to test the Web Service that was deployed in the previous steps.

If you strictly followed the steps mentioned above, the Web Service would be deployed with a URL http://localhost:7001/ZipCodeServices/ZipCodeFinderService and its WSDL can be accessed from http://localhost:7001/ZipCodeServices/ZipCodeFinderService?WSDL

This article does not create or attempt to create the Web Service client that can consume the deployed Web Services as nothing has fundamentally changed in the way the Web Service client works. You are encouraged to use whatever implementation you are comfortable to create Web Service clients.

I used Apache Axis 1.4 to create Web Service stubs to consume the deployed Web Service. The following listings (Listing 7 and Listing 8) capture the incoming and outgoing SOAP messages respectively.

Listing 7: Request SOAP message for Zip Code Finder Service





Edison
NJ



Listing 8: Response SOAP message for Zip Code Finder Service





08817



Comparison with conventional Web Services

The conventional Web Services development required developers to spend extra time in configuration and deployment whenever the underlying SOAP container was changed. The annotated Java Web services will save developers not to worry too much about deployment but concentrate on the core business areas.

The source level configuration of JSR 181 Web Services will also help the developers achieving uniform configuration and development process where the deployment of Web Service will be taken care by JSR 921 specification.

Conclusion

Web Service technology is one of those areas which evolves rapidly towards maturity. JSR 181 is an attempt to standardize and simplify the Web Service development, which will free you as a developer from the underlying implementation details and you concentrate on core business areas. Apart from standardization, they also help Web Service to achieve robustness, easy maintenance and interoperability.

Reference

About the author

Ayyappan Gandhirajan holds a Master's degree in Software Systems from BITS, Pilani, India and a Bachelor's degree in Electronics & Communication Engineering from MK University, India. He has 10 years of profound software experience in Travel, Telecom/Mobility, and e-commerce domains using technologies such as SOA, ESB, Web Service, Web Service Security, Spring, AOP and J2EE. He is currently working for Perot Systems, Bangalore, India. Before joining Perot Systems, he worked for good five years with Hewlett-Packard ISO, Bangalore, India. He can be reached at G_Ayyapparaj@yahoo.com / ayyappan.gandhirajan@ps.net