Using the J2EE Connector Architecture and SOAP to Build Web-Service-Ready Enterprise Applications, Part 2

In December 2002, we published part 1 of this series and received many valuable comments from you. In writing part 2 of the series, we've incorporated your suggestions in the following descriptions:

  • How to use Sun ONE Connector Builder to implement tightly and loosely coupled interactions
  • How to build connectors that support Simple Object Access Protocol (SOAP) with attachments and SOAP over Hypertext Transmission Protocol, Secure (HTTPS).

Note - The source code in this article is extracted from the Customer Order Tracking System (COTS) sample resource adapter that's shipped with Sun ONE Connector Builder. To adopt the sample code, download Sun ONE Connector Builder and deploy the COTS resource adapter. Alternatively, treat the sample as a general guideline only and use its approach as a reference when developing your own resource adapter.

The "What" and "Why" of Sun ONE Connector Builder 2.0

Sun ONE Connector Builder 2.0 comprises a set of tools, components, and libraries with which you can build resource adapters that comply with Java 2 Platform, Enterprise Edition (J2EE) Connector Architecture 1.0 for Enterprise Information Systems (EIS) and legacy applications. You can access the resource adapters with the Common Client Interface (CCI) API as described in the J2EE Connector 1.0 Specification. Optionally, you can add a SOAP service layer to the resource adapters and build Web service solutions for integrating enterprise applications.

The resource adapters take advantage of the J2EE Connector Architecture Service Provider Interface (SPI) implementation, which provides connection management, transaction control, and authentication. Thus, with Sun ONE Connector Builder, you can capitalize on your investments in existing custom or unique applications and integrate them with other J2EE Web service applications.

Sun ONE Connector Builder-Generated, Web-Service-Ready Connectors

Sun ONE Connector Builder 2.0 generates the following to enable connectors with SOAP services:

In addition, Sun ONE Connector Builder 2.0 contains a tool for deploying SOAP services.

Note - Sun ONE Connector Builder 2.0 generates SOAP services, which you can deploy into Apache SOAP 2.2. To prepare SOAP services that are based on Java API for XML-based remote procedure calls (JAX-RPC), use the JAX-RPC support in Sun ONE Studio 4.

SOAP Service Endpoints: Java Interaction Objects (JIOs)

In this scenario, you deploy a resource adapter in a Web server and interact with the resource adapter from a remote client. For this to happen, the resource adapter must be either servlet-wrapped or Remote Method Invocation (RMI)-enabled. To learn the details, see part 1 of this series.

Note - Because of the extensive nature of the CCI and potential serialization issues, RMI enablement of the CCI may be too costly. The problem worsens if you create the resource adapter on top of native client libraries. Here's a suggestion: Wrap the resource adapter's interaction specification with servlets, which accept or return XML as input or output.

SOAP offers an intuitive standard encoding. Since the CCI is at too low a level to be exposed through SOAP, it's wrapped with JIOs. A JIO is a Java object that's generated according to the resource adapter's interaction specification. A JIO accepts the required variables to drive the CCI as input and returns the results as output. In this case, you generate the JIOs as part of the resource adapters with Sun ONE Connector Builder.

The JIO contains two sets of overloaded methods:

  • execute -- The variants of this method act on the CCI interaction specification and input and output records. Therefore, those variants accept the input record and return the output record. The variants take the following as input parameters:
    • Input record only
    • Input record and ConnectionFactory Java Naming and Directory Interface (JNDI) lookup name
    • Input record, JNDI lookup name, and JNDIEnv context

    All three variants return the output record.

  • executeAPI -- Three methods act directly on the EIS API's input-output parameters. Therefore, they accept all individual API input parameters and return the API's return parameter. The variants take the following as input parameters:
    • The API's input parameters only
    • The API's input parameters and the ConnectionFactory JNDI lookup name
    • The API's input parameters, the ConnectionFactory JNDI lookup name, and the JNDIEnv context

    All three variants return the API's return parameter.

See the sample code for a generated JIO for the GetCustomer interaction specification of the COTS sample resource adapter that's shipped with Sun ONE Connector Builder.

Deployment Descriptors for the Service Endpoint

You need a deployment descriptor to register a service in the SOAP runtime environment. The descriptor must comply with the format required by the runtime. The Apache SOAP runtime requirements are:

  • Service Unique Uniform Resource Name (URN) -- Unique name for the service
  • Provider information -- The provider type, scope, class name, method name, and such
  • Type mapping -- The encoding style, the fully qualified class name, and the Java object-to-XML (and vice versa) serializer and deserializer class names for each data type that participates in the object graph of the input variable and the output variable

Each deployment descriptor lists its corresponding JIO class as the provider class and exposes the execute and executeAPI methods of the JIO as SOAP accessible. In addition:

  • execute acts on the CCI input-output records and interaction specifications. Hence, if you use execute, you must construct the input record in the SOAP request.
  • executeAPI acts on the API method parameters. Hence, if you use executeAPI, you can send the API method parameters in the SOAP request. The deployment descriptor also lists the type mappings for each unique class that occurs in the input and output parameter graph, along with details of its Java object-to-XML (and vice versa) class names (deserializers and serializers).

See the sample code for a generated deployment descriptor for the COTSAPIGetCustomer JIO.

Java Object-to-XML (and Vice Versa) Serializers and Deserializers

Depending on the requirements you specify for the types of existing input-output parameters, Sun ONE Connector Builder also creates the required Java object-to-XML (or vice versa) template serializers or deserializers.

For each input-output parameter type, Sun ONE Connector Builder prompts you to specify the serializer or deserializer class information. For each data type, you can specify the following:

  • Whether it's a JavaBeans component (bean)
  • Whether Sun ONE Connector Builder must generate a template type-mapping class or the name of the data type's serializer or deserializer class

The template type-mapping class that is generated contains the marshall and unmarshall methods that you need for implementation. Because the Apache SOAP runtime provides the marshalling or unmarshalling classes after a generic installation, Sun ONE Connector Builder skips the objects of certain types. For collections, you must specify the participating classes and information on their corresponding serializers or deserializers.

The generated type-mapping class implements the serializer and deserializer interfaces as required by the Apache SOAP runtime. The class contains two methods-- marshall (Java object to XML) and unmarshall (XML to Java object), both user customization points.

See the sample code for a generated template serializer-deserializer class for a non-bean data type.

SOAP Service Clients

The resource adapter is enabled for interactions between SOAP and HTTP through Java Interaction Objects (JIOs) and other generated support infrastructure. Once those services are deployed in SOAP runtime, you can access them from external clients by posting a SOAP XML message to the SOAPRPCRouter servlet provided by Apache SOAP. Apache SOAP also provides certain helper classes and framework for clients to construct the SOAP XML message and interaction with the Apache SOAP runtime. Generation of the client stub consists of creating a Java class for each JIO-based SOAP service that's developed on top of the resource adapter.

Each generated client contains four methods. main is the starting point of execution, in which the class instantiates itself and calls the execute method. The Return parameter that execute returns is printed inside main. This process then follows:

  1. execute calls the initClient method, which creates a SOAP mapping registry and fills it with the details of the type mappings.
  2. execute creates a SOAP "call" object and sets on it the values of the method name, encoding style, and mapping registry.
  3. execute sets the parameters on the call object by calling the method populateInputData--a user customization point--which creates a vector of input parameters, adds the individual input parameters to the vector, and returns it.
  4. execute sets the input parameter details on the call object, then invokes the call.invoke() method with the URL of the SOAP service.
  5. execute checks the response for any SOAP fault.
    • In the absence of faults, execute returns the response value to main.
    • If a fault exists, execute prints the details of the fault and returns null.

See the sample code for a generated SOAP Client for the COTSAPIGetCustomer SOAP service.

Ant Build Scripts to Package SOAP Services and Clients

Sun ONE Connector Builder generates an Ant-based build script ( build.xml) for each resource adapter--a script that contains the targets you then adopt to compile and package generated SOAP services. For example, build.xml contains the target package_soap, with which you can package the Java Interaction Objects (JIOs) along with the resource adapter.

To accommodate deployment and use, Sun ONE Connector Builder also packages the deployment descriptors and SOAP service clients into separate Java archive (JAR) files.

By using the Sun ONE Studio IDE or by directly running build.xml, you can execute those targets.

Deployment Tool for SOAP Services

Sun ONE Connector Builder provides a command-line tool, icwsdeploy, for deploying and undeploying resource adapter SOAP services into the target server environment. icwsdeploy accepts the deployment descriptors package created with the build script and deploys the SOAP services accordingly.

Other Features for Generated SOAP Services

This section describes two other procedures you can add on top of the SOAP services that are generated by Sun ONE Connector Builder:

HTTPS Access

You can securely access the SOAP services generated by Sun ONE Connector Builder by enabling them for SOAP-over-HTTPS interaction. Do the following:

  1. Secure the Web server by configuring it to use the Secure Sockets Layer (SSL).

    This step is server specific; see your server administration documentation.

  2. Modify the generated SOAP service client code to enable SOAP-HTTPS interaction.

    Follow these steps to enable SOAP service clients to use secure communication.

Note - The sample code below provides general guidelines. It runs in the Java 2 Platform, Standard Edition, (J2SE) environment only. In your server environment, follow similar--but perhaps not the exact--steps.

Use keytool to create a client.keystore file for SSL authentication. Be sure to specify this file in the client code. Type:



% jdk1.3.1/bin/keytool -import -alias  
                 alias_name
-file client.cer -keystore client.keystore
       
   

When prompted, type a password.

If this is the first time you type the command, type any password.

The client.cer certificate in this directory is valid with servers that are secured with the Verisign 14-day trial SSL certificates. Thus, with client.cer, you can create your client.keystore file.

In case of errors, create client.keystore from a certificate that you install on your browser to access the Web server through HTTPS.

Download Java Secure Socket Extension (JSSE) and copy the three JAR files ( jsse.jar, jnet.jar, and jcert.jar ) into the generated resource adapter's lib/ext directory.

Modify the client code, as follows:

Add these two import statements to the client code:



import javax.net.ssl.SSLSocketFactory;
import java.security.Security;

Add the following three lines in the initClient() method:



// Specify where to find key material for the default
// TrustManager (specify the correct location of the file).
System.setProperty("javax.net.ssl.trustStore",
"C:\jdk1.3.1\bin\client.keystore"
);

// Use Sun's reference implementation of a URL handler
// for the "https" URL protocol type.

System.setProperty("java.protocol.handler.pkgs",
"com.sun.net.ssl.internal.www.protocol");

// Dynamically register Sun's SSL provider.
Security.addProvider(new
com.sun.net.ssl.internal.ssl.Provider());

Specify the correct value in the URL field, as follows:

theURL = new URL("https://localhost:483/soap/servlet/rpcrouter");

See the sample code for creating a customized SOAP service client to access the COTSAPIGetCustomer service with SOAP over HTTPS.

For details on setting up the clients for secure communication, see the paper, Setting up Apache Tomcat and a Simple Apache SOAP Client for SSL Communication, on the Apache site.

Attachment Handling

You can directly transmit most of the regular data types, such as String and Date, in the SOAP message body. However, if you must pass on a Binary Large Object (BLOB) as a method parameter, the SOAP body cannot hold the object's value. As a resolution, the World Wide Web Consortium (W3C) has specified in its SOAP with Attachments Specification a mechanism to pass on such values as attachments to the SOAP body. Apache SOAP 2.2 supports that specification.

The following sections explore two implementation approaches that use the attachment handling mechanism and point out the pros and cons of each approach. To understand the approaches, consider a Java Interaction Object (JIO) with the method void executeAPI( String name, Image img), which saves an image with a name. The implementation of the JIO interacts with the resource adapter to do its job.

Approach 1: Use a Custom Serializer

To send BLOBs in SOAP RPC, you can create a custom serializer for that object type instead of using the MimePart serializer. In this approach, the custom serializer can use the built-in Base64Serializer in Apache SOAP 2.x. Also, rather than being sent as a MIME attachment, the actual object data are part of the SOAP body as a base 64-encoded byte stream.

Here are the changes you must make to components:

  • SOAP service -- The SOAP runtime at the server side receives the request. When it sees the type specified as java.awt.Image, it calls the custom serializer to deserialize the data into an Image type. It then calls executeAPI with String and Image object parameters. You need not customize JIOs generated by Sun ONE Connector Builder.
  • Custom serializer -- The custom serializer uses the Apache SOAP built-in Base64Serializer to serialize and deserialize the custom objects--in this case, an Image type. Base64Serializer works with a byte-array object while serializing and deserializing. Therefore, the custom serializer's marshall method must first convert the image into a byte array and pass the byte-array object to Base64Serializer's marshall method. Similarly, in the unmarshall method, the custom deserializer must first call Base64Serializer's unmarshall method, then with the resulting Bean value of type byte array, construct an Image object from the byte array and return a new Bean of Image type.

To accomplish that serialization and deserialization, the custom serializer can do either of the following:

  • Extend from Base64Serializer and delegate to the super class methods, where required.
  • Instantiate a Bean64Serializer object and call the marshall and unmarshall methods on this object, where required.

    The following sample code demonstrates the unmarshall method:

    
    
    public org.apache.soap.util.Bean
    unmarshall(java.lang.String inScopeEncStyle,
    org.apache.soap.util.xml.QName elementType,
    org.w3c.dom.Node src,
    org.apache.soap.util.xml.XMLJavaMappingRegistry xjmr,
    org.apache.soap.rpc.SOAPContext ctx)
    throws java.lang.IllegalArgumentException{
    
    //Instantiate a Base64Serializer object.
    Base64Serializer srlizr = new Base64Serializer();
    
    //Call its unmarshall method with the same parameters.
    Bean bn = srlizr.unmarshall(inScopeEncStyle,
    elementType, src, xjmr, ctx);
    
    //Take the byte array value from the resulting
    //bean from base64 unmarshall method.
    byte barr[] = (byte [])bn.value;
    
    //Create an image from the byte array.
    Image img = Toolkit.getDefaultToolkit().createImage(barr);
    
    //Create and return a new bean of type Image.
    return new Bean(java.awt.Image.class, img);
    
    }
    
    
  • Deployment descriptor -- You must instruct the server-side SOAP runtime to use the custom serializer to serialize objects of type java.awt.Image. Do so through the deployment descriptor.

    Suppose soapattach.soap.serializers.ImageSerializer is the fully qualified class name of the custom serializer-deserializer. Add the following entry to the deployment descriptor:

    
    
    <isd:map encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
    xmlns:x="urn:xml-soap-j2eeca-soapattach-serverSaveImage"
    qname="x:Image"
    
    javaType="java.awt.Image"
    java2XMLClassName="soapattach.soap.serializers.ImageSerializer"
    xml2JavaClassName="soapattach.soap.serializers.ImageSerializer" />
    
    
  • SOAP service client -- When the client encounters the java.awt.Image type, it must declare to the SOAP runtime that the custom serializer is to be used. To make that declaration, create the SOAPMappingRegistry object and set the mapTypes.

    Here is the sample code:

    
    
    //Create the registry and add mappings with the serializer info.
    theRegistry = new SOAPMappingRegistry();
    
    //Create our new custom serializer object.
    mypkg.ImageSerializer serializer1 = new mypkg.ImageSerializer();
    
    //Add the mapping to SOAP mapping registry.
    theRegistry.mapTypes(Constants.NS_URI_SOAP_ENC,
    new QName("urn:xml-soap-j2eeca-soapattach-serverSaveImage",
    "Image"),
    java.awt.Image.class, serializer1, serializer1);
    
    

    The client does not perform conversions for the parameters. As the method suggests, the client creates the String and Image objects and sets them as the parameters on the SOAP call object.

    Here is the sample code:

    
    
    //Create the parameter vector.
    Vector params = new Vector();
    
    //Create and add the name parameter to the parameter vector.
    String fileName = new String("theNewFileName");
    params.addElement(new Parameter("name",String.class,fileName, null));
    
    //Create an Image object.
    Image img = Toolkit.getDefaultToolkit().getImage("c:\employeeimage.jpg");
    
    //Add the image parameter to the parameter vector.
    params.addElement( new Parameter("image", java.awt.Image.class, img, null);
    
    return params;
    
    

When SOAP runtime sees the java.awt.Image type, it calls the custom serializer to serialize the Image object.

Consider the pros and cons of this approach:

Pros

  • Only minimal customizations are required.
  • The BLOB can occur at any place in the object graph.
  • The BLOB can serve both types of JIO signatures: execute and executeAPI.

Cons

  • You must program the custom serializer. However, as the sample demonstrates, that's a straightforward process.

Approach 2: Use a Built-In MimePart Serializer

Apache SOAP contains a MimePart serializer that can serialize or deserialize objects of type InputStream, DataSource, DataHandler, and MimeBodyPart. Consequently, when a parameter is one of those four types, it does the following:

  • Creates a Multipurpose Internet Mail Extensions (MIME) envelope and a MIME attachment with the object's value.
  • Adds the attachment to the SOAP body.

To pass BLOBs to method parameters, you can make use of this built-in runtime support by adopting any of the preceding four data types. (Use DataHandler whenever possible.) Because the BLOBs are sent as attachments, this approach is also known as the attachment approach.

Here are the changes you must make to components:

  • SOAP service -- Given that attachments are always deserialized as DataHandler types, when the SOAP runtime at the server side receives an attachment, it calls the MimePart serializer to deserialize the DataHandler type. The runtime looks for the method executeAPI with a signature ( String, DataHandler), so you must provide a wrapper method as an overloaded implementation of the original method), like this:
    
    
    void
    executeAPI(String name, javax.activation.DataHandler dh)
    
    

    In this wrapper method, you convert DataHandler to the correct type, which is Image, then call the original method. See the following sample code:

    
    
    void executeAPI(String name, javax.activation.DataHandler dh) {
    
    //Create a ByteArrayInputStream from the DataHandler content.
    ByteArrayInputStream fis = (ByteArrayInputStream) dh.getContent();
    
    //Find the available size of the ByteArrayInputstream
    //and read that into a byte array.
    int size = fis.available();
    byte b[] = new byte[size];
    fis.read(b);
    
    //Create the image from the byte array.
    Image img = Toolkit.getDefaultToolkit().createImage(b);
    
    //Call the original method with the name and image.
    executeAPI(name,img);
    }
    
    
  • Deployment descriptor -- You need not change the deployment descriptor. The implementation uses the default built-in support from Apache SOAP, so you need not add any additional mapping information to the deployment descriptor. Also, Apache SOAP uses only the method name at deployment and maps the parameter information at runtime; you need not specify the parameter types for any overloaded methods.
  • SOAP service client -- Instead of directly using the Image type, the client must convert the image ( java.awt.Image) into a DataHandler type and add the DataHandler object as the parameter to the method. The client must also set the parameters on the SOAP call object.

    Make the appropriate changes in the populateInputData(...) method of the SOAP service client. The following sample code demonstrates the populateInputData method:

    
    
    private Vector populateInputData() {
    // Create the parameter vector
    Vector params = new Vector();
    
    //create and add the name parameter to the parameter vector
    String fileName = new String("theNewFileName");
    params.addElement(new
    Parameter("name",String.class,fileName, null));
    
    // create and add the image parameter to the parameters vector.
    DataSource ds = new ByteArrayDataSource(new
    File("c:\employeeimage.jpg"), "image/jpeg");
    DataHandler dh = new DataHandler(ds);
    params.addElement(new Parameter("img",
    DataHandler.class, dh, null));
    
    return params;
    }
    
    
    

Consider the pros and cons of this approach:

Pros

  • Sending BLOBs as attachments is efficient.
  • You need not customize or specify any serializers or deserializers.

Cons

  • You cannot use this approach on the execute method variants because they expose input and output records only, but not individual parameters.
  • This approach does not work in places where the BLOB is required deep in the object graph. The BLOB must be at the top of the graph for this approach to work.
  • You must customize multiple areas: server, client code, and so forth.

Recommendation

Approach 1 offers the following advantages over Approach 2:

  • The service client can use both types of JIO method signatures ( execute and executeAPI).
  • The BLOB can occur anywhere in the object graph-at the top, in the middle, or deep down in a leaf node.
  • You need not customize the generated JIO.

Conclusion

With Sun ONE Connector Builder, you can efficiently build Web-service-ready connectors. By providing an end-to-end feature set (adapter, SOAP service endpoint, deployment descriptor, and generator of build scripts), Sun ONE Connector Builder helps you enable the resource adapter for SOAP interaction competently, seamlessly, and intuitively.

Moreover, through simple customizations of the generated code, you can enhance the generated services for SOAP-over-HTTPS interaction and attachment handling.

Explore the robustness and versatility of Sun ONE Connector Builder for yourself!

Acknowledgment

Thanks are due Vamsi Bondalapati for developing part of the sample code for this article.

About the Authors

Venkat Amirisetty, a senior staff engineer at Sun Microsystems, has been with Sun for six years. Currently, he s developing technology products for application integration and is the engineering lead for Sun ONE Connector Builder. Venkat works closely with the J2EE Connector Architecture 1.5 (JSR-112) Expert Group.

Marina Sum is a staff writer for Sun ONE for Developers. She has been writing for Sun for 14 years, mostly in the technical arena.