Composer Enterprise Server User

CHAPTER 4

The Runtime Framework

Most of the time, you will find Composer's native deployment facilities and packaging options more than adequate to meet the architectural requirements of your business applications. But if your development needs are such that it's essential to be able to manipulate Composer-built services on a programmatic level, you will need to know how to write code that leverages Composer's Framework API for low-level Java integration.

The Composer framework is a set of classes (in source code form) for working with, or extending, Composer runtime objects. Its features are discussed in some detail later in this chapter.

In many cases, you can create your own custom service-trigger objects without hand-writing any "setup" code. Novell exteNd Director has code-generation wizards that can create servlet, EJB, JSP, and Java stub files for you, which you can then customize. (See the Deployment chapter of the main Composer User's Guide for more detailed information on how to use these wizards.) But to fully understand the generated skel-code, you need to be familiar with the basic architectural assumptions and API requirements of Composer's runtime layer. The information in this chapter will give you the essential background info you need in order to create classes that interact with Composer runtime objects.

NOTE:   This chapter is aimed at intermediate-level (or higher) Java programmers who are interested in understanding the application programming interface for code-level access to Composer runtime objects. To benefit from this chapter, you should be thoroughly familiar with servlet and bean programming, and J2EE app server runtime idioms in general.

This chapter will be of help to you if you need to:

NOTE:   The following discussion deals with runtime issues only. A software development kit (SDK) for creating your own pluggable design-time artifacts in Composer is available by special request through the Novell exteNd marketing organization.

 
Top of page

Composer Runtime Architecture

The core functionality of Composer Enterprise Server is provided by the classes in xcs-all.jar (in Composer's \lib directory, under the app server install path), plus the three dozen or so accompanying technology-specific JAR files in the \lib directory. The classes in xcs-all.jar provide all of the essential "core services" your deployed Composer apps need in order to run on the server, including:

Instantiation and execution of service objects is done through decoding and deserialization of the metadata stored in your deployment archives. When you create a service or component in Composer (design time), you are actually creating an XML file that wrappers the actions in your service or component's Action Model. If you've ever examined the contents of a Composer-created deployment archive, you will probably have noticed that it contains no compiled classes (except if the deployment involves EJBs).

Instead of bytecode, each action in each component's Action Model consists of a metadata description. Composer Enterprise Server understands how to convert that description into executable code at runtime. The classes that do this are opaque: They are not exposed in the Framework API (see below), except for the main execute( ) method of GXSServiceComponent.

Invocation of a Composer service typically occurs through a servlet. But (again) you'll notice there are no servlets in your deployment WAR or EAR. Invocation is handled by a "master servlet" already residing on the server. Your deployment archive contains only a metadata description of how to call the server-resident "trigger servlet." (That description is in the web.xml file in the WAR.) The metadata description contains initialization parameters for the servlet. Those parameters include the name of the service that needs to be run, the name of the "converter class" that should be used for preprocessing arriving data, whether to instantiate the service as an EJB, etc.

 
Top of section

Typical Request-Handling Scenario

From Composer Enterprise Server's point of view, the events that typically accompany invocation of a Composer service include the following:

4OverallFlow

  1. A request arrives at the app server: e.g., XML arrives via HTTP POST. The server notifies the appropriate servlet, in this case GXSServiceRunnerEx (a pre-installed, always-present Composer Enterprise Server class that handles most servlet-based requests for Composer services).

  2. The service-runner servlet uses Composer Enterprise Server's GXSServiceFactory class to obtain an instance of the desired kind of service (represented by the GXSServiceComponent shown above).

  3. The service runner calls on the appropriate converter class (one of several core Composer Server utility classes) to fetch arriving data and put it in String or String-array format. Converter classes are discussed in more detail below.

  4. Finally, the service runner calls the service component's execute( ) method. In the typical case, this method returns a Java String containing the XML output of the service. (Various overloaded versions of the method exist, each with its own return type.)

Once the service has finished executing, the servlet performs any necessary post-processing on the output data (for example, last-minute XSL transformations), in its processResponse( ) method.

There are many possible variations on the scheme just described. The above diagram describes one common scenario, involving servlets and HTTP requests. It is intended to illustrate important Composer architectural idioms, such as:

Obviously, not all data travels by HTTP, and it's not always convenient to invoke services from a servlet. Other scenarios need to be taken into account.

 
Top of section

Alternative Request-Handling Scenarios

One useful variation on the above invocation scheme is afforded by the GXSServiceComponentBean class, wherein a bean implements the IGXSServiceRunner interface. The GXSServiceComponentBean provides extra isolation between the client/request layer and the invocation-target layer, so that it becomes possible for a single type of Java object (the bean) to field requests from many potential types of client objects (servlets, JSPs, arbitrary Java objects). Experienced developers will recognize features of the well-known Proxy and Facade design patterns in this approach.

Remote access to Composer services can also occur through EJBs. The GXSEJBServiceComponent class implements javax.ejb.EnterpriseBean, IGXSServiceRunner, java.io.Serializable, and javax.ejb.SessionBean. Likewise, there is an EJB equivalent of GXSServiceComponent, called GXSEJBService. Enterprise Java Beans make possible the use of any number of well-known design patterns.

In addition to the familiar "request-response" paradigm, of course, it's possible to enlist Composer services in other operational flows. For example, you might have a Composer service that starts up in response to a scheduling daemon of some kind and executes at regular timed intervals. It might not use any input data; it may or may not produce any output. Perhaps it performs a recurring maintenance function. This type of specialized invocation scenario can be supported through the use of a custom trigger object (your own, or derived from a framework object) that implements the IGXSServiceRunner interface.

Source code for many of the classes and interfaces just mentioned can be found in the Composer Enterprise Server framework distribution archive, xcs-src.jar (see next section). The main classes are discussed in more depth below, but for definitive information you should consult the source code or the Javadoc.

 
Top of page

Framework Classes

To facilitate working with Composer deployment and runtime objects, Novell provides a set of framework classes that can be used to create custom Service Triggers for Composer services, alter the Composer JSP tag library, change the way data is passed, etc. This framework comprises a runtime API for working with Composer services.

 
Top of section

Where to Find the Source Files and JavaDoc

You will find the framework files in the AppServer\Composer\lib path under your main \exteNd install directory. Look for these two files:

 
Top of section

Packages of Interest

Unless you have unusually far-reaching requirements, it's unlikely that you will work with more than a handful of the 130+ classes in xcs-src.jar. Nevertheless, a great deal of useful example code can be found there for working with Composer services using servlet technology, EJB technology, SOAP, JSP taglib, transaction managers, etc.

Some of the more interesting packages include:

 
Top of section

Static Constants

See the file called constant-values.html in xcs-src.jar for a comprehensive list of constants used in the framework classes.

 
Top of page

What Types of Programming Needs Does the Framework Address?

The framework allows you to use your own objects to instantiate and execute Composer services. This capability can be important for many development scenarios. For example:

 
Top of page

High-Level Architecture

The framework affords a great deal of flexibility in choosing how to invoke a service. A few of the possible choices are depicted in the diagram below.

3architecturalOptions

The choice of how to set up your invocation layer will probably be dictated by architectural concerns related to:

The invocation patterns shown in the foregoing diagram are all supported, in one way or another, by the design-time deployment options of Director and Composer. If you are using the framework, it's presumably because you need to customize some aspect of the invocation layer (by extending one or more of the classes shown). That's what this discussion will focus on.

 
Top of section

Input and Data Conversion

Most (but not all) Composer services operate on input data of some kind. Composer services expect to receive input data (if any) in one of the following forms:

If your input data will be arriving via HTTP, you may find it convenient to use or extend one of the framework's existing converter classes, which are designed to handle the most common HTTP transport scenarios. (See the Javadoc and/or source code for the com.sssw.b2b.xs.service.conversion framework package.)

Whether your input data arrive by HTTP or not, and whether you choose to use the framework converter classes or not, your code must be prepared to pass input data to your service in one of the formats described above.

 
Top of section

Service Names within Framework Objects

When referring to a service name within a framework object (such as a service runner servlet), you should use only the full-context name of your service: That is to say, you should combine the deployment context with the service component name.

The following is an example of a fully qualified service name:

  com.yourcompany.composer.ProductInquiry 

Where:

NOTE:   Novell recommends, as a best practice, that you include "composer" in the deployment context of every Composer-created artifact, and "director" in the context of every Director-built artifact. This is not only to provide namespace separation of artifacts that might be built by different development team members working remotely, but to make debugging easier. (At stack-trace time, it's valuable to be able to see, at a glance, which product the artifact was created in.)

 
Top of section

Obtaining a Service Instance

You will generally use the static createService( ) method of the GXSServiceFactory object to obtain a reference to a so-called service component This overloaded methods comes in three flavors, with signatures as follows:

  IGXSServiceComponent createService(java.lang.String fullServiceName) 
  
  IGXSServiceComponent createService(IGXSServiceRunner aOriginator) 
  
  IGXSServiceComponent createService(javax.naming.InitialContext aContext, java.lang.String aJNDIName) 
  

The first case is simplest: You can obtain a (non-EJB) service by name. In the second case, the caller (an IGXSServiceRunner) passes a reference to itself; the factory inspects the caller's properties to obtain initialization parameters, then instantiates and configures the service.

The third method produces a service component as an EJB (assuming the service was deployed that way to begin with). The factory needs to know the initial JNDI context and JNDI Name of the service's home interface in order to obtain a reference to the EJB (or its accessor object). After that, the factory takes care of any communication with the EJB container.

 
Top of section

Executing the Service

The code for executing a service directly is straightforward. First, obtain an instance of the desired service by means of a service factory object. Then call the execute( ) method of the service object. The execute method returns the service's output document(s) as native XML in String form.

Code for calling a service can be as simple as:

  String inputDoc = 
         "<?xml version=\"1.0\" encoding=\"UTF-8\"?><root/>";
  String outputDoc = "";
  String serviceName = "com.acme.composer.ProductInquiry";
  
  try {
  // Obtain an instance of the desired service:
  IGXSServiceComponent myService = 
  	        GXSServiceFactory.createService( serviceName );
  
  // Execute the service:
  outputDoc = myService.execute( inputDoc );
  }
  catch( GXSException gxsEx )
   {
      // Do something with exception
   }

Using this kind of code, you can invoke a Composer service from any kind of custom Java object (not just a servlet). Of course, it's the caller's job to obtain the input data for the service, so it can be passed directly in the execute method. In the bare-minimal code shown above, you are passing a single input document as a native-XML string. If you need to pass more than one document, perhaps as a DOM object (i.e., an object of type org.w3c.dom.Document), you can call one of the other variants of execute( ) or executeEx( ); see the discussion under "Data-Passing Options" below.

 
Top of section

Delegating Service Calls Through GXSServiceComponentBean

Instead of calling execute( ) on a factory-obtained service instance, you might find that a more flexible and architecturally robust way of doing things is to delegate service operations through an accessor object: namely, a bean. (Not an EJB, but a regular Java bean.) In this strategy, you instantiate a general-purpose bean directly, use the bean's setter methods to specify the desired service name, input document(s), and other parameters, then call execute( ) on the bean. (The bean then delegates the call to the service.)

The framework provides a utility bean for this purpose, in a class called GXSServiceComponentBean. Code for utilizing this bean typically looks similar to that shown below.

  private static final String SERVICE_NAME = 
       "com.composer.MyService";
  
  // Legal values here are "Normal" or "EJB":
  private static final String SERVICE_TYPE = "Normal";
  
  // Instantiate the bean
  GXSServiceComponentBean lService = 
       new GXSServiceComponentBean();
  
  // Configure it
  lService.setInputXMLDoc( aXML );
  lService.setServiceName( SERVICE_NAME );
  lService.setServiceType( SERVICE_TYPE );
  
  // Now execute the service:
  try {
              lService.execute();
  }
  catch ( GXSException e ) {
              System.out.println( e );
          }
  
  // Obtain the service's output:
  String myOutput = lService.getOutputXMLDoc();

The bean mechanism offers a great deal of flexibility. The bean itself is generic: It can be "configured" dynamically to bind to any service. It implements the IGXSServiceRunner interface, which means that through a variety of setter methods, you can specify XSL resource info, converter class name, and other config parameters for the service before invoking it. Likewise, you can use a wide variety of "getters" to obtain information back from the service after it executes. In addition, the GXSServiceComponentBean class has utility methods, such as getXPath( ) and findDocByPartName( ), that can be helpful in manipulating output data.

The service-runner bean (GXSServiceComponentBean) allows you to specify, via setServiceType( ), whether to use EJB access to obtain and execute the target service (assuming it was deployed in EJB fashion), or non-EJB ("Normal") access. This hides some of the complexity of working with services deployed as EJBs.

The custom tag library used in Director-generated (and Composer-generated) JSP code is built around usage of the GXSServiceComponentBean object. (Source code for the tag library itself is part of the framework.)

NOTE:   The GXSServiceComponentBean class inherits from a utility class called GXSServiceComponentBase (which in turn implements the service-runner interface). Consult the source code and/or Javadoc for these two classes to learn more about the numerous setter, getter, and utility methods they offer.

 
Top of section

Data-Passing Options

The execute( ) method on GXSServiceComponent is overloaded to allow you to pass and receive XML data in various ways. Variants of this method exist to allow passing more than one input document (as either a String array or an array of DOM objects), or passing input as a java.io.Reader. In each case, the return type mimics the input type.

There is also an overloaded method called executeEx( ) that differs from execute( ) in that it returns a GXSExResponse object, which is a lightweight wrapper object for responses from SOAP services that might involve one or more output parts and/or header parts.

The various signatures of execute( ) and executeEx( ) are shown below, along with a brief description of the intended usage..

java.lang.String execute()

Executes a Composer service that does not expect an input document.

org.w3c.dom.Document execute(org.w3c.dom.Document aInputDoc)

Executes the Composer service using the supplied DOM.

org.w3c.dom.Document execute(org.w3c.dom.Document[] aInputDocs)

Executes the Composer service using the supplied mulitple DOMs.

java.io.Reader execute(java.io.Reader xmlIn)

Executes the Composer service using the supplied XML Reader.

java.lang.String execute(java.lang.String xmlIn)

Executes the Composer service using the supplied XML string.

java.lang.String execute(java.lang.String[] aInpDocs)

Executes the Composer service using the supplied XML strings.

GXSExResponse executeEx(java.lang.String[] aInpDocs)

Executes the Composer service using the supplied XML strings.

GXSExResponse executeEx(java.lang.String[] aInpDocs, java.lang.String[] aInpHdrDocs)

Executes the Composer service using the supplied XML strings.

 
Top of page

Service Triggers

A service trigger, broadly speaking, is any object responsible for obtaining a service instance and executing it. In the framework, the principal trigger objects are GXSServiceRunnerand GXSServiceComponentBean. The former is a servlet; the latter is a general-purpose bean.

The GXSServiceRunner class inherits from javax.servlet.http.HttpServlet and implements the IGXSServiceRunner interface (as well as java.io.Serializable). The GXSServiceRunnerEx class differs from GXSServiceRunner in its ability to deal with one or more input documents.

GXSServiceComponentBean inherits from GXSServiceComponentBase. Both implement IGXSServiceRunner as well as java.io.Serializable. The parent class, GXSServiceComponentBase, has many getter and setter methods, allowing you to fine-tune its functionality dynamically. It is not limited to handling HTTP requests.

If you are implementing a trigger that handles data arriving via HTTP, a convenient starting point may be GXSServiceRunner or GXSServiceRunnerEx.

Of course, strictly speaking, it is not necessary for you to extend any of the framework's preexisting service-runner classes in order to execute a service. In fact, it's not even necessary for your custom trigger object to implement IGXSServiceRunner. (See "Executing the Service" for example code that neither extends nor implements framework classes.) Even so, you should understand how these classes and interfaces work.

 
Top of section

IGXSServiceRunner

The interface that all framework service-runner objects implement is IGXSServiceRunner. This interface has two methods, called getServiceProperty( ) and getClassLoader( ), plus numerous predefined public/static properties (Strings) that are used for parameter discovery at runtime. The getServiceProperty( ) method takes a String as an argument; the String should match one of the static property strings defined on IGXSServiceRunner. The getServiceProperty( ) method uses the String passed to it as a key to look up information about the service environment.

For example, one of the properties is called SERVICE_NAME. The hard-coded (final) value of IGXSServiceRunner.SERVICE_NAME is "servicename." If your service-runner object receives this value in a call to getServiceProperty( ), the method should return the name of the service that will be called.

The getServiceProperty( ) method is called by various objects that, from time to time, might receive a reference to your service-runner and might need to look up information about the service your runner intends to run. For example, the GXSServiceFactory object has an overloaded method called createService( ). One of the createService( ) methods takes an IGXSServiceRunner argument. Using the passed-in service-runner reference, the factory object can inspect properties on the caller to determine how to configure a service instance before returning it to the caller. This same mechanism is used by various data-converter objects in the framework.

As it turns out, your service runner does not need to define lookup values (nor "get" methods) for all of the String properties in the IGXSServiceRunner interface. Some of the properties are relevant only in specialized scenarios involving (for example) digitally signed XML in SOAP transactions. For most common scenarios, the only "discovery" properties you must make available before every call to a service factory's createService( ) method are the SERVICE_NAME and SERVICE_TYPE properties. (The latter allows the factory or converter object to discover whether the caller is expecting an EJB, or non-EJB service.)

A bare-minimal implementation of IGXSServiceRunner is shown below:

  class MyServiceRunner implements IGXSServiceRunner
  {
     private String mFullServiceName;
  
     MyServiceRunner(String fullServiceName)
     {
        mFullServiceName = fullServiceName;
     }
  
     public String getServiceProperty(String aName)
     {
        if( aName == IGXSServiceRunner.SERVICE_NAME )
           return mFullServiceName;
        else if( aName == IGXSServiceRunner.SERVICE_TYPE )
           return IGXSServiceRunner.SERVICE_TYPE_NORMAL;
        else
           return null;
     }
  
     public ClassLoader getClassLoader()
     {
        return Thread.currentThread().getContextClassLoader();
     }
  }

Note that if getServiceProperty( ) is called with an argument other than SERVICE_NAME or SERVICE_TYPE, the method returns null. It is important to return null here, because the Composer runtime objects that call getServiceProperty( ) implement default behaviors of various kinds based on a null return value being encountered. If you return a dummy value (such as "Not supported"), you will get unpredictable results.

In addition to getServiceProperty( ), your service runner needs to provide an implementation of getClassLoader( ) for use by factory objects. The implementation shown in above is appropriate for most cases.

 
Top of section

GXSServiceRunner and GXSServiceRunnerEx

If your code will be handling HTTP requests, you might want to extend GXSServiceRunnerEx. This is the framework's all-purpose servlet for triggering Composer services.

The following code shows how to extend GXSServiceRunnerEx. It implements a custom Java class called MyComposerServiceRunner.

  package com.composer;
  
  import javax.servlet.*;
  import javax.servlet.http.*;
  import java.io.*;
  import java.util.*;
  import com.sssw.b2b.xs.*;
  import com.sssw.b2b.xs.service.GXSServiceRunnerEx;
  
  public class MyComposerServiceRunner extends GXSServiceRunnerEx
  {
      static final String CONTENT_TYPE = "text/html";
  
     // Overload the following method if you want to 
     // override the default converter class architecture
  
     protected String[] processRequestEx(HttpServletRequest aReq)
  throws ServletException
     {
  	    return super.processRequestEx( aReq );
     }
  
     // Overload the following method if you want to 
     // override default response architecture
  
     public void processResponse( String xmlOut,
                                  HttpServletRequest req,
                                  HttpServletResponse res )                                         throws GXSException
     {
        super.processResponse( xmlOut, req, res );
     }
  }

The processRequestEx( ) and processResponse( ) methods offer convenient hooks for implementing your own special data pre- and post-processing logic. The above code merely delegates execution to the parent's default implementations of these methods. Remove the "super" calls and insert your own code to take over control of pre- and post-processing.

The example class shown above inherits from GXSServiceRunner, which in turn inherits from HttpServlet. Normal servlet control flow applies. In GXSServiceRunner, the following flow occurs:

Initialization Parameters

It's important to understand that the default implementation of GXSServiceRunner depends on framework methods (specifically, methods belonging to GXSServiceFactory and GXSInputConverterBean) in which the service runner itself is an argument to the method. When a reference to the service runner is passed this way, it's because the factory object needs access to the caller's properties. The properties in question usually involve configuration parameters of some kind.

For example, when GXSServiceRunner calls the GXSServiceFactory method createService( ), passing `this' as an argument, the factory uses the servlet reference to find out the name of the service to obtain and the type of service (EJB or non-EJB). These pieces of information ultimately come from the servlet's initialization parameters (in particular, the params called "servicename" and "xcs_servicetype"). The initialization parameters, in turn, are specified in the web.xml file in the servlet's WAR module.

The following listing shows what the web.xml servlet entry for the MyComposerServiceRunner class might look like. This example assumes that the target Composer service is called HelloWorld and that the framework-supplied GXSInputFromHttpParams converter class will be used to obtain data from the HTTP request.

  <servlet>
    <servlet-name>
      MyComposerServiceRunner
    </servlet-name> 
    <display-name /> 
    <servlet-class>
     com.composer.MyComposerServiceRunner
    </servlet-class> 
    <init-param>
    <param-name>servicename</param-name> 
    <param-value>com.composer.HelloWorld</param-value> 
    </init-param>
  <init-param>
    <param-name>xcs_servicetype</param-name> 
    <param-value>NORMAL</param-value> 
    </init-param>
  <init-param>
    <param-name>transform_into_html</param-name> 
    <param-value>false</param-value> 
    </init-param>
  <init-param>
    <param-name>rootname</param-name> 
    <param-value>greeting</param-value> 
    </init-param>
  <init-param>
    <param-name>converterclassname</param-name> 
    <param-value>
      com.sssw.b2b.xs.service.conversion.GXSInputFromHttpParams
    </param-value> 
    </init-param>
  </servlet>

Note that the "servicename" init param specifies the complete (full-context) name of the target service, in this case com.composer.HelloWorld.

Other parameters are supplied as well, such as "rootname" (to specify the root element name of the XML document that the converter class will create as input to the service), a "transform_into_html" flag to indicate to GXSServiceRunner whether to attempt XSL transformation of the output data in processResponse( ), and so on.

The important point to note is that if you intend to extend GXSServiceRunner or GXSServiceRunnerEx, you should ensure that the web.xml file for your servlet class specifies, at a minimum, the init params "servicename", "xcs_servicetype", and "converterclassname" (and valid values for them), as shown above. The other initialization parameters are optional.

The framework factories "understand" a large number of possible init parameter types: see the properties defined on the IGXSServiceRunner interface for a full list. Some of the more commonly used params are shown in the following table. (Required params are in bold.)

IGXSServiceRunner Property Name

Description

Initialization Parameter

GXSServiceRunner method

SERVICE_NAME

The name of the Composer service component.

servicename

getServiceName()

ROOT_NAME

The root node that is expected as the input document.

rootname

getRootName()

JNDI_NAME

The JNDI name of the EJB home interface, for the Composer service component.

jndiname

getJndiServiceName()

CONVERTER_ CLASS_NAME

The class that should be used to convert the HTTP request into an XML document.

converterclassname

getConverterClassName()

PARAM_NAME

The name of the parameter that contains the input XML document.

xcs_paramname

getxcsParamName()

SERVICE_TYPE

Whether the service component reference is an EJB or NORMAL.

xcs_servicetype

getServiceType()

PROVIDER_PARAM

The JNDI provider URI.

providerURI


CONTEXT_FACTORY

The JNDI context factory.

contextfactory


HTML_INDICATOR

An indicator used to specify whether the output document will be rendered as HTML.

transform_into_html

getOutputHTMLIndicator()

OUTPUT_XSL

If the output document is being transformed into HTML, this will give the URI of the style sheet. This is only necessary if the XSL processing instruction has not been embedded in the output XML document.

output_xsl_URI

getOutputXSL()

If your servlet class will be used in an environment where the web.xml init-param mechanism can't be relied upon, you should provide custom implementations of the following methods:

 
Top of section

IGXSInputConversion and IGXSExInputConversion

The framework's servlet-based service runner class (GXSServiceRunnerEx) makes use of so-called converter classes to obtain data arriving via HTTP. These classes are intended to provide a clean separation of "data marshalling and unmarshalling" logic from service invocation logic.

The converter classes in the framework implement the IGXSExInputConversion interface (which in turn extends IGXSInputConversion). This interface has only one method:

  String[] processMultipleRequests(HttpServletRequest aReq)

As you can see, this method essentially converts a servlet request to an array of XML strings.

NOTE:   Since you cannot implement the IGXSExInputConversion interface if your custom service runner class does not use (or cannot supply) a HttpServletRequest object, this discussion applies only if you are extending GXSServiceRunnerEx (or if you are implementing a custom servlet that will eventually get passed to factory objects). If your trigger class does not inherit from HttpServlet, you can implement your own scheme for fetching data, and simply pass the data to the service's execute( ) method.

Most of the framework's converter classes implement a constructor that takes a IGXSServiceRunner argument so that the converter can obtain initialization parameters (or other information) from the caller. Study the source code for the framework converter classes if you want to see examples of this technique in use.

Framework-Supplied Converter Classes

The framework contains a number of predefined converter classes (that is, classes that implement the IGXSExInputConversion interface). The names of these classes can be specified in servlet init-params, or supplied to the setConverterClassName( ) method of GXSServiceComponentBase.

Converter classes available in the framework include:

You should study the source code for these classes to see how they work before implementing any but the most trivial of custom converter classes. Depending on what kinds of data conversion you need to do, you may be able to extend an existing converter class (and save yourself a lot of coding).

A custom converter class will look something like this.

  public class MyConverterClass implements IGXSInputConversion
  {
     // Attribute that holds the service 
  // runner for querying parameters.
     IGXSServiceRunner mRunner = null;
  
     // Constructor to take the IGXSServiceRunner 
     // so that the class can retrieve params 
     public MyConverterClass( IGXSServiceRunner aRunner )
     {
        mRunner = aRunner;
     }
  
     // the processRequest method should take 
     // an HttpServletRequest as 
     // a parameter and return an XML doc as a String:
     public String processRequest( HttpServletRequest aReq )
        throws GXSConversionException
     {
        String lsExpandedDoc = null;
     // ( create or obtain XML . . . )
        return lsExpandedDoc;
     }
  }

 
Top of page

EJB-Deployed Services

The Enterprise Java Bean (EJB) API implements a container architecture designed to facilitate clean separation of logic, data-access, and presentation layers while also providing connection pooling, transaction management, persistence, access control ( via Roles), "naming services" (JNDI), and remote invocation mechanisms, so as to free applications from having to implement or manage such features individually.

Composer services can be deployed as EJBs. In Composer Enterprise Edition, a simple drag-and-drop UI exists for designating EJB associations at design time (see the separate Composer User's Guide), such that when you choose a service to deploy as an EJB session bean, you can specify whether it is to be Stateful or Stateless, the transaction participation level (Mandatory, Never, Supports, etc.), and the JNDI name of the service.

Since EJBs cannot be instantiated directly by use of constructors, you must use the GXSServiceFactory's static createService( ) method to obtain a reference to a service. The signature of the method in question is:

  public static IGXSServiceComponent createService(javax.naming.InitialContext aContext,
                java.lang.String aJNDIName)
                                            throws GXSException

The returned service object is of type IGXSServiceComponent, which means it supports all of the various execute( ) overloaded signatures discussed previously.

An alternative to using the GXSServiceFactory is to utilize the GXSServiceComponentBean class, which can act as a kind of "proxy object" for interacting with Composer services. Example code for using this JavaBean was given earlier (under "Delegating Service Calls Through GXSServiceComponentBean"). To use this bean as an EJB-service accessor, follow the procedure discussed before, but specify "EJB" in setServiceType( ), and in addition to calling setServiceName( ) with the name of the deployed service, use setJndiServiceName( ) to specify the JNDI name that you supplied for the service at deployment time. If you are implementing the IGXSServiceRunner interface yourself, you should provide an implementation of getJndiServiceName( ) in your service runner and vector to it from getServiceProperty( ) when the latter gets a request for JNDI_NAME.

Getting the EJB Home and Remote Interfaces

The EJB remote interface, called IGXSEJBServiceComponent, is located in the com.sssw.b2b.xs.ejb package. When deploying an Composer service as an EJB, you will assign a JNDI name to the EJB. It is this name rather than the qualified Composer service name that will be used to get a reference to the EJBs home interface. The name of the EJB home interface for creating the IGXSEJBServiceComponent is IGXSEJBServiceHome.

When specifying the JNDI name for an EJBs home interface remember that, for the Novell exteNd Application Server, the string "sssw://host/RMI/" needs to be prepended. For example, if you were deploying an EJB into an Application Server called main.server, and the JNDI name for the EJB happens to be com/acme/inventory/ProductInquiry, then the fully qualified JNDI name would be sssw://main.server/RMI/com/acme/inventory/ProductInquiry.

Once the home interface has been retrieved, much like the GXSServiceFactory's createService() method, a method called create() can be invoked which will return the remote interface of the EJB. (This is the closest thing to "instantiating" an EJB that exists in the EJB world.) The remote interface contains several execute methods, as described below:

  java.lang.String execute() 

Method to execute a Composer service that does not expect an input document.

   java.lang.String execute(java.lang.String inXML) 

Method to execute the Composer service against a single XML document.

   java.lang.String execute(java.lang.String[] asInputStrs) 

Method to execute the Composer service component using multiple input documents.

   GXSExResponse executeEx(java.lang.String[] asInputStrs) 

Executes the Composer service using the supplied XML strings.

   GXSExResponse executeEx(java.lang.String[] aInpDocs, java.lang.String[] aInpHdrDocs) 

Executes the composer service component using the supplied XML strings as inputs and headers.

You will notice that the Reader or Document versions of execute( ) available in the IGXSServiceComponent are not available in the EJB remote interface. This is because neither Reader nor Document is serializable and thus neither one is able to appear in a remote method.

Factory to Obtain EJB Home Interfaces

If you want low-level control over EJB access, you will want to know about a factory class called GXSEJBAccessor, located in the com.sssw.b2b.xs.sssw package. It contains two methods to obtain an EJB's home interface from a Novell exteNd Application Server.

One method can be used within a server that does not require authentication; the second provides two extra parameters for username and password.

When using the factory, there is no need to fully qualify the JNDI name assigned to the EJB. The factory creates the fully qualified hostname with the supplied parameters. In the following example, the JNDI name of the EJB is com/acme/inventory/ProductInquiry, the Novell exteNd Application Server name is main.server and the ports are at their installation default of 80 for HTTP and 54890 for RMI.

  import com.sssw.b2b.xs.ejb;
  import com.sssw.b2b.xs.sssw.GXSEJBAccessor;
  
  public void doSomeEJBStuff() throws java.rmi.RemoteException
  {
  	 	 IGXSEJBServiceHome srvcHome = GXSEJBAccessor.getHomeBean(
  	 	 	 "com.sssw.b2b.xs.ejb.IGXSEJBServiceHome",
  	 	 	 "com/acme/inventory/ProductInquiry", "main.server",
  	 	 	 80, 54890 );
  	 	 IGXSEJBServiceComponent ejbSrvc = srvcHome.create();
  	 	 // Do something with the service component	 	 	 
  }




Copyright © 2004 Novell, Inc. All rights reserved. Copyright © 1997, 1998, 1999, 2000, 2001, 2002, 2003 SilverStream Software, LLC. All rights reserved.  more ...