Section 12.5. Writing Web Services


12.5. Writing Web Services

As you might expect, implementing a SOAP web service is moderately more difficult than just using one as a client. Thankfully, JAX-RPC and the Java web service engines make the writing of the web service implementations themselves very simple (even simpler than writing a client, in some cases). The added complexity of implementing web services comes in the deployment step, which we'll discuss in the next major section.

12.5.1. Simple Java Web Services

If you look at the static proxy approach for implementing SOAP clients, you may notice a lot of similarities with the client stubs used in RMI for talking to remote RMI objects. That's not a coincidence. JAX-RPC uses the RMI programming model to provide a simple way to write web services in Java. The published interface for the web service is defined as a Remote interface. If we were implementing our simple echo web service in Java, we could define the interface for the service as shown in Example 12-2.

Example 12-2. A JAX-RPC service interface
 public interface IEcho extends java.rmi.Remote {     public String echo(String message) throws java.rmi.RemoteException; } 

The implementation for the service is equally simple to define. We just write a concrete implementation of this Remote interface, as shown in Example 12-3.

Example 12-3. A JAX-RPC service implementation
 public class Echo implements IEcho {     public Echo( ) {        super( );     }     public String echo(String message) throws RemoteException {        return message;     } } 

That's it. The IEcho interface and the Echo concrete implementation together serve as an implementation of a web service in Java. As we mentioned earlier, the hard part is deploying a web service to an application server, which we'll cover shortly. But essentially, a web service engine will manage this web service, converting SOAP messages targeted for the service into equivalent method calls on the implementation class and converting the method return values or exceptions into SOAP messages or faults that are delivered back to the client making the request.

You need to keep a few concerns in mind when writing web services . These concerns are similar to those you face when writing RMI server objects, but SOAP web services introduce the additional concern of portability, because clients of your service could be implemented in many different development environments and languages.

Arguably, the most important issue to keep in mind is the mapping of your Java service implementation into WSDL and/or SOAP entities. As described earlier, JAX-RPC defines a set of standard mappings for a specific set of Java types into their corresponding XML Schema entities. These supported Java types include the basic data types (int, boolean, etc.), their Object wrapper classes (Integer, Boolean, etc.), Java interfaces that use the JavaBeans pattern, and arrays of these supported data types. You should make every effort to restrict your service implementations so that their interfaces expose only these supported Java types. Otherwise, it's likely that you'll run into interoperability issues with some clients, especially those implemented in other platforms.

As a concrete example, let's take a look at our example "white pages" PeopleFinder application (seen in Chapters 2, 17, and 18). Suppose we wanted to create a web service, using the RMI programming model described earlier, which exposes the search capabilities of the PeopleFinder system. If we looked at this as "just" a Java developer, ignoring any web service mapping or interoperability issues, we might throw together this Remote interface for our web service:

     public interface PeopleFinder extends java.rmi.Remote {                  public Collection findPeople(Map searchParams)             throws InvalidSearchException, PersistenceException,                    RemoteException;     } 

This interface makes the implementation of the service trivial. The PersonDAO that is the core of the system has a search( ) method that accepts a Map of the search arguments and returns a Collection of Person objects. By using this same signature for our web service method, our implementation of the findPeople( ) operation on the service interface would simply be:

     public class PeopleFinderImpl implements PeopleFinder {         . . .         public Collection findPeople(HashMap searchArgs)             throws InvalidSearchException, PersistenceException {             return dao.findPeople(searchParamsMap);         }         . . .     } 

The problem we will likely face with the web service is that no standard mappings are defined for Java collection objects into XML entities. So we don't know how the web service engine will deal with the Map method argument or the Collection return value. In fact, the web service engine would be within its rights to just throw this interface back at us and refuse to deploy it. How should it map a Collection, which by definition is simply a set of Objects? How can the web service engine possibly map this into WSDL entities in such a way that a client will know what to expect as a response from the operation? The Collection could contain just about anythingStrings, Integers, complex JavaBeans structures, and so on. The same goes for the Map argument to the methodwhat should a web service client pass into this operation in order to get the desired behavior? Strings mapped to strings, something else? A much more clear, portable, and interoperable interface for our Java web service would be something like the following, which replaces the collections with arrays of specific types:

     public interface PeopleFinder extends Remote {              public Person[] findPeople(SearchArg[] args)             throws InvalidSearchException, PersistenceException,                    RemoteException;     } 

We've converted the Collection to an explicit array of Person beans, and we've introduced a new JavaBeans object, SearchArg, that holds a name/value pair representing a search argument:

     public class SearchArg implements Serializable {         private String mArgName = null;         private String mArgValue = null;         public SearchArg( ) { super( ); }         public SearchArg(String name, String value) {            setName(name);            setValue(value);         }         public String getName( ) { return mArgName; }         public void   setName(String argName) { mArgName = argName; }         public String getValue( ) { return mArgValue;  }         public void   setValue(String argValue) { mArgValue = argValue; }     } 

This makes our service implementation slightly more complicated, of coursewe'll have to convert the incoming array of SearchArg objects into a Map to pass into the PersonDAO.findPeople( ) method, and we'll have to convert the Collection returned from that method call into an array of Person objects to return to the client. But we'll have a lot fewer interoperability issues with this version and fewer issues diagnosing mapping issues with our web service engine as well.

12.5.2. EJB Web Services

In addition to the "straight" Java implementation of a web service described in the previous section, JAX-RPC and the EJB specifications support the use of session EJBs as web services as well. Put another way, the previous section showed how to use the RMI programming model to implement a web service in Java, but you can also use the EJB programming model to implement a web service. Put yet another way, JAX-RPC and EJB provide a standard way to expose a session EJB component as a SOAP web service.

Only stateless session beans can support web service clients. Beyond that, the restrictions on the interface to an EJB that will be exposed as a web service are roughly the same as for web services implemented using the RMI programming model described in the previous section. You need to be mindful of the arguments and return values on the methods to be mapped to service operations to ensure that they can be cleanly mapped using a standard JAX-RPC mapping.

The following is the interface for an EJB implementation of our PeopleFinder service:

     public interface PeopleFinder extends javax.ejb.EJBObject {         public Person[] findPeople(SearchArg[] args)             throws InvalidSearchException, PersistenceException,                    RemoteException;     } 

This interface looks very similar to the one we saw earlier using the RMI modelthe only difference is that we're extending EJBObject instead of Remote, in keeping with the EJB programming model for components.

Our EJB implementation class, PeopleFinderBean, has an implementation of the findPeople( ) method that is virtually identical to our earlier RMI version. The difference here, again, is the use of the EJB component model, which requires that a session bean have certain callback methods implemented as well, such as ejbCreate( ) and setSessionContext( ). The purpose and implementations of these EJB-related methods are covered in Chapter 6.

     public class PeopleFinderBean implements javax.ejb.SessionBean {         public PeopleFinderBean( ) {             super( );         }         // EJB-related callback methods         public void ejbActivate( ) throws EJBException, RemoteException {}         public void ejbPassivate( ) throws EJBException, RemoteException {}         public void ejbRemove( ) throws EJBException, RemoteException {}         public void setSessionContext(SessionContext arg0)             throws EJBException, RemoteException {}         public void ejbCreate( ) throws CreateException {}         // Perform the actual search, using the PersonDAO         public Person[] findPeople(SearchArg[] args)              throws InvalidSearchException, PersistenceException {             Map searchArgsMap = new HashMap( );             for (int i = 0; i < args.length; i++) {                 searchArgsMap.put(args[i].getName( ), args[i].getValue( ));             }             Collection people =                 PersonDAO.getInstance( ).findPeople(searchArgsMap);             Person[] peopleArray = new Person[people.size( )];             Iterator pIter = people.iterator( );             int i = 0;             while (pIter.hasNext( )) {                 peopleArray[i++] = (Person)pIter.next( );             }             return peopleArray;         }     } 

From the standpoint of implementing a web service, this session bean represents a web service implementation that will be managed by an EJB container, in terms of the lifecycle of the component, its security services, and so forth. These runtime management services are the real reason that having this EJB option for implementing a web service is interesting. If, for example, our web service needs to make heavy use of transactions (with databases, messaging services, ERP systems, etc.), having the transaction management services of an EJB container for our web service might be very useful. Similarly, if our web service is expected to receive high load levels from clients, the runtime instantiation and instance-pooling services of EJB could be valuable as well.

12.5.3. Nonstandard Implementation Schemes

There are several nonstandard, proprietary models for implementing Java-based web services in addition to the standard approaches specified in the JAX-RPC and related specifications. Typically, these implementation schemes are meant to provide a faster way to get a web service up and running but work only with a specific Java web service engine and therefore won't port to alternative engines.

12.5.3.1. Axis JWS approach

Apache Axis, for example, provides a very simple scheme called JWS for implementing a web service. You simply take the source file for a concrete Java class, rename it with a .jws extension, and place it somewhere in your application's web context. Assuming that your web service has the Axis engine servlet installed to handle all .jws files, the Axis engine automatically processes the .jws file when a client requests its URL and maps the code found in the JWS file to a web service, complete with a WSDL descriptor. For example, we could implement our earlier Echo web service using JWS with the following Java class:

     public class EchoJWS {         public String echo(String message) {             return message;         }     } 

If we put this Java code into an EchoJWS.jws file in the root of our web application (either in a war file or in the root of the expanded web application directory, depending on how we're deploying our application to the application server), then Axis will deploy this as a web service. If our application is deployed with a context root of myApp on the server myserver.com, the endpoint URL of the web service will be:

     http://myserver.com/myApp/EchoJWS.jws 

and the WSDL for the service will be available at the URL:

     http://myserver.com/myApp/EchoJWS.jws?wsdl 

The Axis JWS scheme for implementing web services has a number of limitations. The most important one is that the mapping process (from Java entities to WSDL and back) is not configurable. As we'll see in the upcoming section on deploying web services, the ability to configure the mapping of Java entities (interfaces, JavaBeans, exceptions, etc.) into XML entities for the SOAP service can be very useful, sometimes critical. If you need to use a complex Java structure that requires a custom mapping, for example, you cannot use JWS.

In addition to the lack of configurability, JWS-based web services cannot make use of Java packages. The JWS file must have no package declaration, and the file has to sit in the root of the web application context, not in a subdirectory. This limits your ability to organize and manage your web service implementation code and its corresponding endpoint URLs.

So while the JWS scheme implemented in Axis is useful for quick prototypes of simple web services, it's not likely that it will be sufficient for all of your needs for implementing web services in enterprise systems.



Java Enterprise in a Nutshell
Java Enterprise in a Nutshell (In a Nutshell (OReilly))
ISBN: 0596101422
EAN: 2147483647
Year: 2004
Pages: 269

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net