GT 4.1.2 Java WS Core : Developer's Guide

1. Introduction

This guide contains information of interest to developers working with Java WS Core. It provides reference information for application developers, including APIs, architecture, procedures for using the APIs and code samples.

2. Before you begin

2.1. Feature summary

New Features in the GT 4.1.2 release

Other Supported Features

  • A standalone and embeddable container
  • Tomcat 4.1, 5.0, and 5.5 support
  • Basic API for resource persistence and recovery
  • Persistent subscriptions support
  • Automatic service and ResourceHome activation on startup
  • Operation providers

Deprecated Features

  • None

2.2. Tested platforms

Java WS Core should work on any platform that supports J2SE 1.4.1 or higher.

Tested platforms for Java WS Core:

  • Linux (Debian 3.1)
  • Windows XP
  • Solaris 9

Tested JVMs for Java WS Core:

JVM notes:

  • GCJ is not supported.
  • If using IBM JVM 1.4.1 please see bug 2828 for more information.

Tested containers for Java WS Core:

  • Java WS Core container
  • Tomcat 4.1.34
  • Tomcat 5.0.30
  • Tomcat 5.5.20
  • JBoss 4.0.2

2.3. Backward compatibility summary

Protocol changes since GT version 4.0

  • No changes.

API changes since GT version 4.0

Schema changes since GT version 4.0

  • No changes.

Other changes since GT version 4.0

2.5. Security Considerations

2.5.1. Permissions of service configuration files

The service configuration files such as jndi-config.xml or server-config.wsdd (located under $GLOBUS_LOCATION/etc/<gar>/ directory) may contain private information such as database passwords, etc. Ensure that these configuration files are only readable by the user that is running the container. The deployment process automatically sets the permissions of the jndi-config.xml and server-config.wsdd files as user readable only. However, this might not work correctly on all platforms and this does not apply to any other configuration files.

2.5.2. Permissions of persistent data

The services using subscription persistence API or other basic persistence helper API will store all or part of its persistent data under the ~/.globus/persisted directory. Ensure that the entire ~/.globus/persisted directory is only readable by the user running the container.

2.5.3. Invocation of non-public service functions

A client can potentially invoke a service function that is not formally defined in the WSDL but it is defined in the service implementation class. There are two ways to prevent this from happening:

  1. Define all service methods in your service class as either private or protected.
  2. Configure appropriate allowedMethods or allowedMethodsClass parameter in the service deployment descriptor (please see Configuring Java WS Core for details).

3. Architecture and design overview

4. Public interface

4.1. Semantics and syntax of APIs

4.1.1. Programming Model Overview

There are two main parts to the Java WS Core programming model: the service and the resource. The service performs the business logic on the resource, and the resource represents the managed state.

The web service is just a stateless POJO (Plain Old Java Object) that discovers the resource associated with the request and then performs operations on that resource.

The resources are managed and discovered via ResourceHome implementations. The ResourceHome implementations can also be responsible for creating new resources, performing operations on a set of resources at a time, etc.

The ResourceHome implementations are configured in JNDI and are associated with a particular web service. JNDI is a central transient registry that is mainly used for discovery of ResourceHome implementations but it can be used to store and retrieve arbitrary information.

The web services are configured in the Web Services Deployment Descriptor (WSDD).

WSDL in document/literal style is assumed as a starting point for writing a service. A number of generic interfaces are defined so that custom implementations of these interfaces can be used instead of the default implementations included in the Java WS Core. No special base classes are required for a web service or resource implementation. However, the resource object at minimal must implement the org.globus.wsrf.Resource interface (it defines no operations; it is used just as a marker interface). The service developer can pick and choose which other resource interfaces the resource object should implement to tailor the implementation to his/her needs.

4.1.2. Component API

  • Stable API:

    • org.globus.wsrf.Resource
    • org.globus.wsrf.ResourceKey
    • org.globus.wsrf.ResourceProperty
    • org.globus.wsrf.ResourcePropertyMetaData
    • org.globus.wsrf.ResourcePropertySet
    • org.globus.wsrf.ResourceProperties
    • org.globus.wsrf.ResourceHome
    • org.globus.wsrf.ResourceLifetime
    • org.globus.wsrf.ResourceIdentifier
    • org.globus.wsrf.ResourceContext
    • org.globus.wsrf.RemoveCallback
    • org.globus.wsrf.PersistentCallback
    • org.globus.wsrf.Subscription
    • org.globus.wsrf.Topic
    • org.globus.wsrf.TopicList
    • org.globus.wsrf.TopicListMetaData
    • org.globus.wsrf.TopicAccessor
    • org.globus.wsrf.TopicListener
    • org.globus.wsrf.TopicListenerList

  • Less stable API:

    • org.globus.wsrf.NotificationConsumerCallbackManager
    • org.globus.wsrf.NotificationConsumerManager
    • org.globus.wsrf.NotifyCallback
    • org.globus.wsrf.encoding.ObjectSerializer
    • org.globus.wsrf.encoding.ObjectDeserializer
    • org.globus.wsrf.impl.SimpleResourceKey
    • org.globus.wsrf.impl.BaseResourceProperty
    • org.globus.wsrf.impl.ReflectionResourceProperty
    • org.globus.wsrf.impl.SimpleResourceProperty
    • org.globus.wsrf.impl.SimpleResourcePropertyMetaData
    • org.globus.wsrf.impl.SimpleResourcePropertySet
    • org.globus.wsrf.impl.ResourceContextImpl
    • org.globus.wsrf.impl.ResourceHomeImpl
    • org.globus.wsrf.impl.SingletonResourceHome
    • org.globus.wsrf.impl.ServiceResourceHome
    • org.globus.wsrf.impl.ResourcePropertyTopic
    • org.globus.wsrf.impl.SimpleTopic
    • org.globus.wsrf.impl.SimpleTopicList
    • org.globus.wsrf.impl.SimpleTopicListMetaData
    • org.globus.wsrf.query.QueryEngine
    • org.globus.wsrf.query.ExpressionEvaluator
    • org.globus.wsrf.topicexpression.TopicExpressionEngine
    • org.globus.wsrf.topicexpression.TopicExpressionEvaluator
    • org.globus.wsrf.utils.FaultHelper
    • org.globus.wsrf.utils.XmlUtils
    • org.globus.wsrf.utils.AddressingUtils
    • org.globus.wsrf.container.ServiceHost

The complete Java WS Core API.

4.2. Semantics and syntax of the WSDL

Java WS Core contains an implementation of the following specifications. Please see the corresponding specifications for details:

4.2.1. Resource Properties

4.3. Semantics and syntax of non-WSDL protocols

[describe non-WSDL protocols. if none, state so.]

4.4. Command-line tools

Java WS Core Commandline Reference

4.5. Overview of Graphical User Interface

There is no support for this type of interface for Java WS Core.

4.6. Semantics and syntax of domain-specific interface

There is no content available at this time.

4.7. Configuration interface

Configuring Java WS Core

4.8. Environment variable interface

Table 1. Globus standard environment variables

NameValueDescriptionComments
GLOBUS_LOCATION<path>The <path> is the root location of the Java WS Core installation. Must be an absolute path. Required
GLOBUS_TCP_PORT_RANGE<min,max>The <min,max> is the minimum and maximum port range for TCP server sockets (useful for systems behind firewalls). For example, if set, the notification sink on the client will be started within that port range. Optional
GLOBUS_TCP_SOURCE_PORT_RANGE<min,max>The <min,max> is the minimum and maximum port range for TCP outgoing sockets (useful for systems behind firewalls). Optional
GLOBUS_UDP_SOURCE_PORT_RANGE<min,max>The <min,max> is the minimum and maximum port range for UDP outgoing sockets (useful for systems behind firewalls). Optional
GLOBUS_HOSTNAME<host>The <host> is either a hostname or ip address. The host ip address under which the container and services will be exposed. Optional

Table 2. Launch script specific environment variables

NameValueDescriptionComments
GLOBUS_OPTIONS<arguments> The <arguments> are arbitrary arguments that can be passed to the JVM. See below for a detailed list of supported options.Optional
JAVA_HOME<path>The <path> is the root location of the JVM installation. If set, the JVM from that installation will be used. Otherwise, the first one found in path will be used. Optional
CLASSPATH<classpath>This environment property is ignored by launch scripts. Ignored

Table 3. Options supported by the GLOBUS_OPTIONS environment property

NameValueDescription
-Dorg.globus.wsrf.proxy.portintThis property specifies the port number of the proxy server. The proxy server must run on the same machine as the container. This setting will cause the service address to have the port of the proxy instead of the container (only applies to code that uses the ServiceHost or AddressingUtils API.
-Dorg.globus.wsrf.container.server.idstringThis property specifies the server id. The server id is used to uniquely identify each container instance. For example, each container gets its own persistent directory based on the server id. By default the standalone container will store the persistent resources under the ~/.globus/persisted/<ip>-<containerPort> directory. While in Tomcat the ~/.globus/persisted/<ip>-<webApplicationName> directory will be used instead. This property overwrites the default server id and therefore indirectly controls which storage directory is used by the container. If set, the container will store the persisted resources under ~/.globus/persisted/<server.id>/ instead. Note, that if somehow multiple containers running as the same user on the same machine end up with the same server id / persistent directory they might overwrite each other's persistent data.
-Dorg.globus.wsrf.container.persistence.dirdirectoryThis property specifies the base directory that will be used for storing the persistent resources. This property overwrites the default (~/.globus/persisted/) base directory assumed by the container.

Any JVM options can also be passed using the GLOBUS_OPTIONS environment property.

5. Usage scenarios

5.1. Basics

5.1.1. WSDL/SOAP rules

The WSRF and WSN specifications schemas follow the document/literal mode as described in WS-I Basic Profile. The Basic Profile defines certain rules to follow for document/literal and other modes to ensure interoperability.

Java WS Core relies on these restrictions so please keep them in mind when designing your own schema.

5.1.1.1. Document/literal

In the document/literal mode as defined in the WS-I Basic Profile at most one <wsdl:part> is allowed in the <wsdl:message> element and it must use the 'element' attribute. Also, the wire signatures must be unique (cannot use the same 'element' attribute in <wsdl:part> in two different <wsdl:message> elements).

[Note]Note

Axis' WSDL2Java tool might sometimes incorrectly detect that schema follows the wrapped/literal mode and generate wrong stub and type classes. To ensure that document/literal mode is always used:

  • use Java WS Core's generateStub* Ant tasks in <install>/share/globus_wsrf_tools/build-stubs.xml file
  • if you are using Axis' WSDL2Java tool directly, you can alternatively specify the -W command line option.

Also, with wrapped/literal mode, the element name had to match the operation name in wsdl. This is not necessary with document/literal mode.

5.1.1.2. SOAP Encoding

Do not use or mix the literal mode with the SOAP encoding mode (R2706). For example, do not use the soapenc:Array type. Please see the 5.2.3 section in the WS-I Basic Profile for details.

5.1.2. Operation providers and its configuration

GT3 introduced the concept of operation providers where a service could be composed of different parts/classes. Java WS Core also supports this functionality. In GT3 operation providers had to implement a specific interface. In Java WS Core no such interface is required. In fact, an operation provider is not in any way different from a standard web service. That means that any web service implementation can automatically be used as an operation provider (as long as it uses common or standard interfaces to operate on resources).

To enable operation provider support for your service, make the following changes to the service deployment descriptor:

  1. Change the value of the provider attribute to Handler.
  2. Add a handleClass parameter with a value of org.globus.axis.providers.RPCProvider.
  3. Specify providers in the providers parameter.

    The value of the parameter is a space-separated list of either provider names or class names. If provider names are used, they must first be defined as parameters in the <globalConfiguration> element of the main deployment descriptor (etc\globus_wsrf_core\server-config.wsdd).

    For example:

    <globalConfiguration>
      ...
      <parameter name="GetRPProvider" 
                  value="org.globus.wsrf.impl.properties.GetResourcePropertyProvider"/>
      ...
    </globalConfiguration>
  4. Add or change the value of the scope parameter to Application or Request.

The following is an example of a modified service deployment descriptor:

<service name="SubscriptionManagerService"  provider="Handler" use="literal" style="document">
  <parameter name="allowedMethods" value="*"/>
  <parameter name="scope" value="Application"/>
  <parameter name="providers" value="
                     GetRPProvider 
                     org.globus.wsrf.impl.lifetime.SetTerminationTimeProvider
                     PauseSubscriptionProvider"/>
  <parameter name="handlerClass" value="org.globus.axis.providers.RPCProvider"/>
  <parameter name="className" 
            value="org.globus.wsrf.impl.notification.ResumeSubscriptionProvider"/>
  <wsdlFile>share/schema/core/notification/subscription_manager_service.wsdl</wsdlFile>
</service>

[Note]Note

The operations defined in the className service always overwrite the providers' operations. That is, if one provider defines the same method as the service specified in the className parameter, the operation will be invoked on the service. Also, if two providers define the same method, the first one specified in the providers parameter will be invoked.

5.1.3. JNDI configuration and programming

Java WS Core uses a simple in-memory JNDI implementation provided by the Naming subproject of the Apache Directory Project.

The jndi-config.xml files are used to configure the JNDI registry in Java WS Core. The file format of jndi-config.xml is slightly different from the Tomcat's server.xml file. One main difference is that the <resourceParams> are specified as children of <resource> objects. Also, Java WS Core's jndi-config.xml parser is case sensitive and all element names are lowercase.

All elements defined in the <global> section of the JNDI configuration file are deployed into the java:comp/env context under the name specified in the 'name' attribute. All <service> elements are deployed into the java:comp/env/services/<service name> context. New objects and contexts can be added or modified dynamically at runtime but they will not be persisted. The only way to always have an object around is to deploy it in the jndi-config.xml file. All services share the same java:comp/env context. This is different from EJBs where each EJB has a separate java:comp/env context.

Please see The JNDI Tutorial for more information on JNDI programming.

5.1.3.1. Deploying Java Beans

When deploying a Java Bean using the <resource> entry in jndi-config.xml please note the following:

  • The Java Bean must have a default constructor.

  • If the Java Bean implements the org.globus.wsrf.jndi.Initializable interface the initialize() method will be automatically called after all parameters are set on the bean.

  • If the Java Bean implements the org.globus.wsrf.jndi.Destroyable interface the destroy() method will be automatically called on container shutdown.

  • Each Java Bean is configured with a factory class specified by a factory resource parameter. The factory is an optional parameter and therefore can be omitted. If this parameter is not specified, the default org.globus.wsrf.tools.jndi.BeanFactory factory class will be used. Otherwise, the class specified by the parameter will be used. Do not use org.apache.naming.factory.BeanFactory as a factory class.

5.1.3.2. Obtaining InitialContext

To obtain JNDI InitialContext do:

import org.globus.wsrf.jndi.JNDIUtils;
...
InitialContext ctx = JNDIUtils.getInitialContext();
Foo foo = (Foo)ctx.lookup(...);
[Note]Note

It is important to use this API to obtain the InitialContext as the context must be initialized with the right properties. That ensures that the right JNDI implementation is used as there might be other JNDI implementations present as for example in application servers such as JBoss.

5.1.4. Lifecycle and activation

5.1.4.1. Activating a service

To activate a service, an RPCProvider is available from both Axis and Globus.

5.1.4.2. Activating a service using the Axis RPCProvider

The scope setting of the service dictates when and how service instances are created:

Table 4. Scope settings

Application One instance of the service is used for all invocations.
Request One instance is created per invocation. This is the default (if scope parameter is not set in the deployment descriptor).
Session One instance is created per session.

If the service implements the javax.xml.rpc.server.ServiceLifecycle interface, the lifecycle methods will be called according to the scope setting as a service instance is created and destroyed.

For example, in Application scope, destroy() will be called on container shutdown, and in Request scope it will be called after the service method is called.

With Axis RPCProvider, JAAS credentials are never associated with the invocation thread.

5.1.4.3. Activating a service using the Globus RPCProvider

The scope setting of the service dictates when and how service instances are created (only Application and Request scopes are supported with Globus RPCProvider):

Table 5. Scope settings and activation

Application

Service/provider instances are created either on first invocation or on container startup. The behavior is determined by the value of the "loadOnStartup" parameter. This will work in the same way in both the stand-alone container and in Tomcat.

If the service or the container is configured with a security descriptor, the appropriate credentials will be associated with the thread during activation (using JAAS). Also, during activation a basic Axis MessageContext will be associated with the thread with only Constants.MC_HOME_DIR, Constants.MC_CONFIGPATH, and the right target service properties set (see Section 5.2.2.3, “Obtaining standard MessageContext properties” for details). If service or providers implement the javax.xml.rpc.server.ServiceLifecycle interface, the lifecycle methods will be called accordingly.

Request

One instance is created per invocation. This is the default (if scope parameter is not set in the deployment descriptor).

Behaves more or less just like the Axis RPCProvider (service/providers instances are created per invocation, ServiceLifecycle methods called right before and after service method invocation, no JAAS credentials during ServiceLifecycle methods).

5.1.4.4. Activating a ResourceHome

A ResourceHome will be activated either on the first service invocation or, if "loadOnStartup" parameter is set to "true", during container startup. Both mechanisms trigger actual activation by looking up the ResourceHome in the JNDI directory. This initial lookup causes a proper MessageContext and/or JAAS subject to be associated with the current thread, instantiation of the object implementing the ResourceHome and, if the ResourceHome implements the org.globus.wsrf.jndi.Initializable interface, the invocation of the initialize() function.

In fact, the same steps are performed upon initial lookup of any JNDI resource entry that uses the org.globus.wsrf.jndi.BeanFactory class for its factory and is defined directly under a service entry in a jndi-config.xml file.

5.1.4.5. Activating ServiceResourceHome

If you are using a ServiceResourceHome please make sure to deploy the service with the "loadOnStartup" option enabled and in Application scope. That will ensure that the ResourceHome is initialized with the right service/resource.

5.2. Programming

5.2.1. General

5.2.1.1. Using Apache Addressing API

The WS-RF and WS-N specifications distributed with Java WS Core use WS-Addressing (the March 2004 version of the specification) for addressing services and resources. Java WS Core uses the Apache Addressing library for WS-Addressing support. The API is pretty straightforward and easy to use. Most of the work is done in AddressingHandler deployed in the client and server configuration files. See Apache Addressing documentation for details.

5.2.1.1.1. Using the call object directly

If you are using the javax.xml.rpc.Call object directly, you can pass the addressing information by setting a Constants.ENV_ADDRESSING_REQUEST_HEADERS property on the call object.

For example:

Service service = new Service();
Call call = (Call) service.createCall();

String url = "http://localhost:8080/axis/services/Version";

AddressingHeaders headers = new AddressingHeaders();
headers.setTo(new To(url));

// pass the addressing info to the addressing handler
call.setProperty(Constants.ENV_ADDRESSING_REQUEST_HEADERS, headers);

call.setTargetEndpointAddress(new URL(url));
call.setOperationName(new QName(url, "getVersion")); // url here is just a namespace

String ret = (String) call.invoke(new Object[]);

5.2.1.1.2. Using the AddressingLocator class

The Apache Addressing library also contains a version of Axis' WSDL2Java tool. It extends the Axis' WSDL2Java tool functionality by generating, in addition to all the regular classes, the <service>Addressing interface and <service>AddressingLocator class.

The AddressingLocator class can be used to get a stub for a service by passing the Apache Addressing EndpointReferenceType parameter.

For example:

String url = "http://localhost:8080/axis/services/Version";

EndpointReferenceType epr = new EndpointReferenceType();
epr.setAddress(new Address(url));

VersionServiceAddressingLocator locator =
     new VersionServiceAddressingLocator();

VerionServicePortType port = locator.getVersionPort(epr);

port.getVersion();

5.2.1.1.3. ReferenceProperties

In the WS-RF and WS-N specifications, the WS-Addressing ReferenceProperties are used to carry resource identity information. The resource identity can be anything as long as it serializes as a XML element. The ReferenceProperties are serialized as separate SOAP headers in the SOAP envelope.

The Apache Addressing library only allows a DOM Element or a SOAPElement to be a reference property.

For example, create ReferencePropertiesType and fill it with resource key info:

// create a reference property
QName keyName = new QName("http://axis.org", "VersionKey");
String keyValue = "123";
 
SimpleResourceKey key = new SimpleResourceKey(keyName, keyValue);

ReferencePropertiesType props = new ReferencePropertiesType();

// convert to SOAPElement and add to the list
props.add(key.toSOAPElement()); 
...

Then pass it to AddressingHeaders:

...
Service service = new Service();
Call call = (Call) service.createCall();

String url = "http://localhost:8080/axis/services/Version";

AddressingHeaders headers = new AddressingHeaders();
headers.setTo(new To(url));
headers.setReferenceProperties(props);

// pass the addressing info to the addressing handler
call.setProperty(Constants.ENV_ADDRESSING_REQUEST_HEADERS, headers);

call.setTargetEndpointAddress(new URL(url));
call.setOperationName(new QName(url, "getVersion")); // url here is just a namespace

String ret = (String) call.invoke(new Object[]);

Or set it on EndpointReferenceType:

...
String url = "http://localhost:8080/axis/services/Version";

EndpointReferenceType epr = new EndpointReferenceType();
epr.setAddress(new Address(url));
epr.setProperties(props);

VersionServiceAddressingLocator locator =
    new VersionServiceAddressingLocator();

VerionServicePortType port = locator.getVersionPort(epr);

port.getVersion();

5.2.1.2. Working with AnyContentType content

Java Beans generated by Apache Axis that represent a XML type with the xsd:any content implement the org.apache.axis.encoding.AnyContentType interface and have get_any() and set_any() methods. There are several API to help you deal with the AnyContentType content.

To convert AnyContentType content to a Java object use the ObjectDeserializer API. For example:

  AnyContentType bean = ...;
  Integer value = (Integer)ObjectDeserializer.getFirstAsObject(
                                 bean), Integer.class);

To convert a Java object into a type that can be used with AnyContentType content use the ObjectSerializer API. For example:

  // convert Java object into SOAPElement
  EndpointReferenceType object = ...;
  QName elementName = new QName("http://example.com", "EPR");
  SOAPElement element = ObjectSerializer.toSOAPElement(object, elementName);
 
  // set the SOAPlement as Any content
  AnyContentType bean = ...;
  AnyHelper.setAny(bean, element);

To examine the raw AnyContentType content use the AnyHelper API to serialize it as XML. For example:

  AnyContentType bean = ...;
  String contents = AnyHelper.toSingleString(bean);
  System.out.println("Contents: " + contents);
5.2.1.3. Serializing and deserializing Java objects to and from files

You can use the ObjectSerializer API to serialize the Java object into a file (in XML format):

  // object to serialize
  EndpointReferenceType epr = ...;
  // root element name
  QName elementName = new QName("http://example.com", "EPR");

  FileWriter out = null;
  try {
      out = new FileWriter("epr.xml");
      ObjectSerializer.serialize(out, epr, elementName);
      out.write('\n');
  } catch (IOException e) {
      System.err.println("Error: " + e.getMessage());
  } finally {
      if (out != null) {
          try { out.close(); } catch (Exception ee) {}
      }
  }

Similarly, use can use the ObjectDeserializer API to deserialize a file containing XML data into a Java object:

  // deserialized object
  EndpointReferenceType epr = ...;

  FileInputStream in = null;
  try {
      in = new FileInputStream("epr.xml");
      epr = (EndpointReferenceType)ObjectDeserializer.deserialize(
                            new InputSource(in), EndpointReferenceType.class);
  } catch (IOException e) {
      System.err.println("Error: " + e.getMessage());
  } finally {
      if (in != null) {
          try { in.close(); } catch (Exception ee) {}
      }
 }
5.2.1.4. Setting up and receiving notifications (Notification Consumer)

There are a few steps involved in setting up and receiving notifications:

5.2.1.4.1. Step 1: Implement the callback

The notification consumer application must provide an implementation of the NotifyCallback interface. The deliver function of the interface will be invoked whenever a notification for that consumer arrives.

[Note]Note

The deliver function should be thread-safe as multiple notifications might come at once. Notifications might also come unordered and some might even be lost (due to network failures).

5.2.1.4.1.1. Resource property notifications

For resource property notifications the message parameter will usually be of ResourcePropertyValueChangeNotificationElementType type. From that type you can retrieve the real notification message which contains the new and optionally the old value of the resource property. Example:

public void deliver(List topicPath, 
                    EndpointReferenceType producer,
                    Object message) {
  ResourcePropertyValueChangeNotificationType changeMessage =
    ((ResourcePropertyValueChangeNotificationElementType) message).
                           getResourcePropertyValueChangeNotification();

  Integer newValue = (Integer)ObjectDeserializer.getFirstAsObject(
                                 changeMessage.getNewValue(), Integer.class);
  System.out.println("New value: " + newValue);

  if (changeMessage.getOldValue() != null) {
     Integer oldValue = (Integer)ObjectDeserializer.getFirstAsObject(
                                 changeMessage.getNewValue(), Integer.class);
     System.out.println("Old value: " + oldValue);
  }
}

The resource property values are of AnyContentType type. Please see the Working with AnyContentType content section for more information on how to deal with such types.

5.2.1.4.1.2. Other notifications

For other non-resource property notifications the message parameter will either be of the type into which the message type maps into (if there is an appropriate type mapping defined) or of org.w3c.dom.Element type if there is no appropriate type mapping defined. Example:

public void deliver(List topicPath, 
                    EndpointReferenceType producer,
                    Object message) {
  EndpointReferenceType epr = null;
  if (message instanceof Element) {
     // type mapping not defined, try to deserialize into right Java 
     // type using ObjectDeserializer API.
     epr = (EndpointReferenceType)ObjectDeserializer.toObject(
                      (Element)message, EndpointReferenceType.class);
  } else if (message instanceof EndpointReferenceType) {
     // type mapping defined
     epr = (EndpointReferenceType)message;
  } else {
     // some other type
  }
}

The custom notification message type mappings can be defined in a client-server-config.wsdd file. This file can be deployed with your service (it must be placed directly under the etc/ directory in the GAR file). Please see the $GLOBUS_LOCATION/etc/globus_wsrf_core/client-server-config.wsdd file for an example. If your callback implementation will be used on the server-side, you might also need to define the type mappings in your server-config.wsdd.

5.2.1.4.2. Step 2: Start NotificationConsumerManager

In order to facilitate the receipt of notifications, start a NotificationConsumerManager by doing the following:

import org.globus.wsrf.NotificationConsumerManager; 
...

NotificationConsumerManager consumer = null;
try {
   consumer = NotificationConsumerManager.getInstance();
   consumer.startListening();
   ...
} catch (...) {
   ...
}
[Important]Important

On the client when the consumer.startListening() is called an embedded container is actually started in the background. That embedded container is the same as the standalone container but configured with only one or two services needed to handle the notifications. Therefore, any client using notification consumer API will have the same dependencies on the libraries and configurations files as the basic standalone container code. Also, please check the Section 4.1, “Program fails with "Failed to acquire notification consumer home instance from registry" error” if the consumer.startListening() call failed on the client.

On the server when the consumer.startListening() is called the container in which the service is running in is used to receive the notifications. Therefore, there are no extra dependencies.

5.2.1.4.3. Step 3: Register the callback

Register the callback implementation with the NotificationConsumerManager (once it is started) using the createNotificationConsumer function.

The createNotificationConsumer function returns an endpoint for this notification consumer.

Example:

import org.globus.wsrf.NotifyCallback;
import org.apache.axis.message.addressing.EndpointReferenceType;
...

   MyCallback callback = new MyCallback();
   EndpointReferenceType consumerEPR =
       consumer.createNotificationConsumer(callback);
   ...

class MyCallback implements NotifyCallback {
  ....
}

5.2.1.4.4. Step 4: Subscribe to the callback

Pass the endpoint returned by the createNotificationConsumer function to the subscribe call.

Example:

import org.oasis.wsn.TopicExpressionType;
import org.oasis.wsn.Subscribe;
import org.oasis.wsn.SubscribeResponse;
import org.globus.wsrf.WSNConstants;
import org.globus.wsrf.WSRFConstants;
...

TopicExpressionType topicExpression = new TopicExpressionType();
topicExpression.setDialect(WSNConstants.SIMPLE_TOPIC_DIALECT);
topicExpression.setValue(WSRFConstants.TERMINATION_TIME);

Subscribe request = new Subscribe();
request.setUseNotify(Boolean.TRUE);
request.setConsumerReference(consumerEPR);
request.setTopicExpression(topicExpression);

SubscribeResponse subResponse = port.subscribe(request);
...

5.2.1.4.5. Step 5: Clean up

Once done with the notifications, do the following clean up tasks.

Step 5a: Destroy subscriptions resource. Make sure to explicitly destroy the subscription resource or set its termination time. Example:

import org.globus.wsrf.core.notification.SubscriptionManager;
import org.globus.wsrf.core.notification.service.SubscriptionManagerServiceAddressingLocator;
import org.oasis.wsrf.lifetime.Destroy;
...

SubscriptionManagerServiceAddressingLocator sLocator = new SubscriptionManagerServiceAddressingLocator();
SubscriptionManager manager = sLocator.getSubscriptionManagerPort(
                        subResponse.getSubscriptionReference());
manager.destroy(new Destroy());
...

Step 5b: Un-register the callback.  Make sure to call (especially in error cases) the NotificationConsumerManager.removeNotificationConsumer() function to unregister the callback from the NotificationConsumerManager.

Step 5c: Release resources. In addition, make sure to always call the NotificationConsumerManager.stopListening() function when finished using the NotificationConsumerManager. Otherwise, some resources might not be released. Example:

   ...
} catch(Exception e) {
   ...
} finally {
   if (consumer != null) {
      try { consumer.stopListening(); } catch (Exception ee) {}
   }
}

5.2.1.5. Obtaining version information

The Version API can be used to obtain Java WS Core version information programmatically. For example to display major, minor and patch version information do:

import org.globus.wsrf.utils.Version;
...
System.out.println("Major: " + Version.getMajor());
System.out.println("Minor: " + Version.getMinor());
System.out.println("Micro: " + Version.getMicro());

5.2.1.6. Adding and retrieving SOAP Attachments

Java WS Core supports SOAP with Attachments. DIME, MIME, and MTOM formats are supported. This section provides brief sample code. Detailed code can be found in the automated tests for this feature at http://viewcvs.globus.org/viewcvs.cgi/wsrf/java/core/test/unit/src/org/globus/wsrf/impl/security/, AttachmentTestService.java and AttachmentsTests.java.

To add an attachment to a request do:

import javax.activation.DataHandler;
import javax.activation.FileDataSource;
import javax.xml.rpc.Stub;
import org.apache.axis.client.Call;
....
File file = new File(..);
DataHandler dataHandler = 
        new DataHandler(new FileDataSource(file));

((Stub)port)._setProperty(
        Call.ATTACHMENT_ENCAPSULATION_FORMAT,
        Call.ATTACHMENT_ENCAPSULATION_FORMAT_MTOM);
            
((org.apache.axis.client.Stub)port).addAttachment(dataHandler);

To retrieve attachments associated with a request do:

import javax.activation.DataHandler;
import javax.xml.soap.AttachmentPart;
import org.apache.axis.Message;
import org.apache.axis.MessageContext;
....
MessageContext msgContext = MessageContext.getCurrentContext();
Message reqMsg = msgContext.getRequestMessage();
if (reqMsg.getAttachmentsImpl() == null) {
  throw new Exception("Attachments are not supported");
}
Iterator it = reqMsg.getAttachments();
while (it.hasNext()) {
  AttachmentPart part = (AttachmentPart) it.next();
  DataHandler dataHandler = part.getDataHandler();
}

The SwA support adds a small overhead to overall SOAP processing. To disable SwA support for improved performance delete $GLOBUS_LOCATION/lib/common/geronimo-activation_1.0.2_spec-1.1-SNAPSHOT.jar and $GLOBUS_LOCATION/lib/common/geronimo-javamail_1.3.1_spec-1.1-SNAPSHOT.jar files.

Please note that SOAP attachments can be used with message security but they will not be signed or encrypted.

5.2.1.7. Querying resource properties using XPath

Java WS Core currently provides two ways of querying the resource properties using XPath: the standard method and a proprietary method. The standard method is defined by the WS-ResourceProperties specification and all implementations of this specification support it. The proprietary method is a custom solution and therefore only supported by Globus Toolkit. The proprietary method is a new query dialect called TargetedXPath. The TargetedXPath query dialect offers three key advantages over the standard XPath query method:

  • Namespace mappings - a set of explicit namespace mappings to be passed along with the query. With these mappings the query expression can be dramatically simplified as namespace prefixes can be used freely within the query.

  • Single resource property querying - a specific resource property can be queried instead of the entire resource property document.

  • WS-Enumeration support - the query results can be returned as an enumeration.

The globus-xpath-query command line tool can be used to query resource properties with the TargetedXPath query dialect. If the query results were returned as an enumeration they can be retrieved using the ws-enumerate command line tool.

5.2.1.7.1. API [FIXME: more specific title]

Example querying resource properties using the TargetedXPath query dialect:

import org.globus.wsrf.core.query.targetedXPath.TargetedXPathQueryType;
import org.globus.wsrf.core.query.targetedXPath.NamespaceMappingType;
import org.globus.wsrf.query.targetedXPath.TargetedXPathConstants;
...
TargetedXPathQueryType targetedQuery = 
   new TargetedXPathQueryType();

NamespaceMappingType nsMap[] = new NamespaceMappingType[1];
nsMap[0] = new NamespaceMappingType();
nsMap[0].setMappedName("fooPrefix");
nsMap[0].setNamespace(new URI("http://fooNamespace"));
        
targetedQuery.setNamespaceMappings(nsMap);
targetedQuery.setQueryString("boolean(//fooPrefix:fooElement)");

QueryExpressionType query = new QueryExpressionType();
query.setDialect(TargetedXPathConstants.TARGETED_XPATH_DIALECT);
query.setValue(targetedQuery);

QueryResourceProperties_Element queryRequest = 
   new QueryResourceProperties_Element();
queryRequest.setQueryExpression(query);

QueryResourcePropertiesResponse queryResponse =
   port.queryResourceProperties(queryRequest);

To query a specific resource property do:

...
targetedQuery.setNamespaceMappings(nsMap);
targetedQuery.setQueryString("boolean(//fooPrefix:fooElement)");
QName rp = new QName("http://foo", "bar");
targetedQuery.setResourcePropertyName(rp);
...

To return query results as an enumeration do:

import org.xmlsoap.schemas.ws._2004._09.enumeration.EnumerationContextType;
...
targetedQuery.setNamespaceMappings(nsMap);
targetedQuery.setQueryString("boolean(//fooPrefix:fooElement)");
targetedQuery.setEnumerateResults(Boolean.TRUE);
...
QueryResourcePropertiesResponse queryResponse =
   port.queryResourceProperties(queryRequest);

EnumerationContextType context =
   (EnumerationContextType)ObjectDeserializer.getFirstAsObject(
                                                 queryResponse,
                                                 EnumerationContextType.class);

5.2.2. Service-side specific

5.2.2.1. Obtaining container and service endpoint information

In most cases, a service will need to return the endpoint information of the container to a client. Unfortunately, getting that information might not be easy. The only reliable way of getting the container endpoint information is to extract it from the MessageContext.TRANS_URL property of the MessageContext/ResourceContext associated with the current thread.

To obtain base container endpoint information use the ServiceHost API. For example:

import org.globus.wsrf.container.ServiceHost;
...
URL containerBaseUrl = ServiceHost.getBaseURL();
...

The above will return the base container URL such as http://localhost:8080/wsrf/services/.

To obtain service endpoint information use the ResourceContext API. For example:

import org.globus.wsrf.ResourceContext;
...
URL serviceUrl = ResourceContext.getResourceContext().getServiceURL();
...

The above will return the service URL such as http://localhost:8080/wsrf/services/MyService.

To obtain WS-Addressing endpoint for the service use the AddressingUtils API. For example:

import org.apache.axis.message.addressing.EndpointReferenceType;
import org.globus.wsrf.utils.AddressingUtils;
...
EndpointReferenceType containerEndpoint = 
       AddressingUtils.createEndpointReference(null);
...

The above will create a EndpointReferenceType object initialized with the Address field set to the service URL (as before) and empty reference properties. Also, you can pass a non-null ResourceKey instance to the createEndpointReference() function to create an endpoint for a specific resource. The reference properties field of the created EndpointReferenceType object will be set to the given ResourceKey.

[Note]Note

The ServiceHost API will return the correct information and AddressingUtils API will work correctly only if called from the same thread as the service method was invoked from.

5.2.2.2. Obtaining service parameters

While we strongly recommend that you use the JNDI mechanism to provide your service with configuration information, it is sometimes necessary to obtain the value of parameters set in the WSDD file. Java WS Core provides some helper functions to ease this process:

import org.globus.wsrf.utils.ContextUtils;
import org.apache.axis.MessageContext;
...
MessageContext context = MessageContext.getCurrentContext();
String sampleProperty = (String)
    ContextUtils.getServiceProperty(context, "myProperty");
...

Note that this function requires that a MessageContext is associated with the current thread, which in general means that the call needs to happen within the context of a web service invocation.

[Note]Note

Specifying parameters using WSDD files depends on Axis and will likely not be supported in future versions of the toolkit.

5.2.2.3. Obtaining standard MessageContext properties

The following properties can be obtained from the SOAPContext/MessageContext associated with the current thread:

  • org.apache.axis.Constants.MC_HOME_DIR - the base directory from which the wsdl files are loaded.
  • org.apache.axis.Constants.MC_CONFIGPATH - the base directory from which different configuration files are loaded.
  • org.apache.axis.Constants.MC_REMOTE_ADDR - the IP address of the client.
  • org.apache.axis.MessageContext.TRANS_URL - the URL of the request.

The Constants.MC_CONFIGPATH property should be used to load any type of configuration file. Only Constants.MC_CONFIGPATH and Constants.MC_HOME_DIR are associated with the thread during activation. In the standalone container the Constants.MC_HOME_DIR and Constants.MC_CONFIGPATH properties will usually point to the same directory. However, in Tomcat they will point to two different directories. Since GT 4.0.1, the Constants.MC_HOME_DIR value can be accessed using the org.globus.wsrf.ContainerConfig.getSchemaDirectory() static call, and Constants.MC_CONFIGPATH value via the org.globus.wsrf.ContainerConfig.getBaseDirectory() static call.

5.2.2.4. Making local calls

Services in the container can be invoked locally. Local invocations work just like remote invocations (all handlers are called, messages get serialized/deserialized) but messages do not travel over the network - everything happens in memory.

Local invocations can only be made on the server side. URLs with "local" protocol name are used for local invocations.

To invoke a service locally, do the following:

  1. Create a service URL with "local" protocol:

    URL url = new URL("local:///wsrf/services/MyService");
  2. And as normal make the call:

    MyServiceAddressingLocator locator = 
           new MyServiceAddressingLocator();
    MyService port = locator.getMyServicePort(url);
    
    port.hello();

That's all. By default the local invocations are made using the default instance of the AxisServer engine. It is possible to make local invocations using a different AxisServer engine instance if there is a MessageContext associated with the current thread (the MessageContext should have a reference to the desired AxisServer engine instance). To make a local invocation using non-default AxisServer engine add the following (using above example):

import org.globus.axis.transport.local.LocalTransportUtils;
...
MyService port = ...

LocalTransportUtils.enableLocalTransport((Stub)port);

port.hello();
5.2.2.4.1. Registering URL protocol handler

The "local" protocol URL handler is automatically registered by Java WS Core. However, sometimes it might be necessary to install the handler explicitly. To register the "local" URL protocol hander do the following:

import org.globus.axis.transport.local.LocalTransportUtils;
...
LocalTransportUtils.init();
...

This step must be done before creating URLs with "local" protocol. Also, make sure that axis-local.jar is accessible from the system classloader.

5.2.2.5. Associating MessageContext with current thread

Axis associates a MessageContext object with the current thread of execution only during a remote service invocation (when a service method is actually called remotely). Sometimes, it might be necessary to execute a piece of code with some specific MessageContext object associated with the current thread. For example, a background server-side task might want to call some service method directly.

The MessageContextHelper API can be used to temporarily associate a specific MessageContext object with the current thread in order to call some code that needs that context to be associated with the thread. Example:

import org.globus.axis.utils.MessageContextHelper;
...
MessageContext newCtx = ...;
MessageContextHelper helper = new MessageContextHelper(newCtx);
helper.set();
try {
   // call code that needs MessageContext associated with the thread
} finnally {
   helper.restore();
}
...

5.2.3. Client-side specific

5.2.3.1. Controlling connection settings

By default Java WS Core clients will use HTTP 1.1 protocol with chunked encoding. Java WS Core clients will also attempt to reuse HTTP/S connections between the calls. The default timeout for clients is 10 minutes. All of these connection properties can be controlled programmatically using the HTTPUtils API as shown below.

[Note]Note

Please note that once a connection property is set on a given Stub, it is applied to ALL calls made using that Stub.

5.2.3.1.1. Setting connection timeout

To set connection timeout do (the timeout value is in milliseconds):

import org.globus.axis.transport.HTTPUtils;
...
MyServiceAddressingLocator locator = 
       new MyServiceAddressingLocator();
MyService port = locator.getMyServicePort(url);

// set timeout to 2 minutes
HTTPUtils.setTimeout((Stub)port, 1000 * 60 * 2);

port.hello();
5.2.3.1.2. Controlling connection reuse

To control connection reuse do:

import org.globus.axis.transport.HTTPUtils;
...
MyServiceAddressingLocator locator = 
       new MyServiceAddressingLocator();
MyService port = locator.getMyServicePort(url);

// close connection after the call
HTTPUtils.setCloseConnection((Stub)port, true);

port.hello();

// do not close connection - let it be reused
HTTPUtils.setCloseConnection((Stub)port, false);

port.hello();
5.2.3.1.3. Controlling HTTP chunked encoding

To control whether HTTP chunked encoding should be used do:

import org.globus.axis.transport.HTTPUtils;
...
MyServiceAddressingLocator locator = 
       new MyServiceAddressingLocator();
MyService port = locator.getMyServicePort(url);

// disable chunked encoding
HTTPUtils.setChunkedEncoding((Stub)port, false);

port.hello();

// re-enable chunked encoding
HTTPUtils.setChunkedEncoding((Stub)port, true);

port.hello();
5.2.3.1.4. Controlling HTTP protocol version

To control what HTTP protocol version should be used do:

import org.globus.axis.transport.HTTPUtils;
...
MyServiceAddressingLocator locator = 
       new MyServiceAddressingLocator();
MyService port = locator.getMyServicePort(url);

// force HTTP 1.0
HTTPUtils.setHTTP10Version((Stub)port, true);

port.hello();

// force HTTP 1.1
HTTPUtils.setHTTP10Version((Stub)port, false);

port.hello();
5.2.3.2. Checking if container is running
5.2.3.2.1. Remote container

To check if a container is running remotely (given the service URL) add ?wsdl to the end of the service URL. If only the host and the port number information of the container are known, create an appropriate service URL using a standard service such as the Version service. For example: http://[host]:[port]/wsrf/services/Version?wsdl (assuming the services are deployed under /wsrf/services/ context).

5.2.3.2.2. Local container

Use DeploymentState API to check if a container is running locally (given the GLOBUS_LOCATION of the container is known). For example:

import org.globus.tools.gar.DeploymentState;
...
String globusLocation = ...;
DeploymentState state = new DeploymentState(globusLocation);
boolean running = state.isRunning();
[Note]Note

This method will work only with a standalone container only.

5.2.3.3. Client notes

Any program that is based on Java WS Core should contain as a first entry in its classpath the directory of the Java WS Core installation. This is to ensure that the right client-config.wsdd is used by the client. That configuration file contains important client-side information such as handlers, type mappings, etc.

Also, any program that is a notification consumer should be initialized with the appropriate GLOBUS_LOCATION system property (set to the installation directory of Java WS Core). If the system property is not set, the notification consumer might not initialize or work properly.

5.3. Deploying GARs

5.3.1. Grid Archive (GAR)

The GAR (Grid Archive) file is a single file which contains all the files and information that the container needs to deploy a service. The GAR files are deployed using globus-deploy-gar (globus-deploy-gar) and undeployed using globus-undeploy-gar (globus-undeploy-gar) tools.

5.3.1.1. GAR file structure

Table 6. GAR file structure

docs/ This directory contains service documentation files.
share/ This directory contains files that can be accessed or used by all services.
schema/ This directory contains service WSDL and schema files.
etc/ This directory contains service configuration files and a post-deploy.xml Ant script.
bin/ This directory contains service executables such as command line tools, GUI, etc.
lib/ This directory contains service and third party library files and any LICENSE files.
server-deploy.wsdd This file is the server side deployment descriptor.
client-deploy.wsdd This file is the client side deployment descriptor.
jndi-config-deploy.xml This file is the JNDI configuration file.
5.3.1.2. Deployment process

The contents of the GAR file are processed in the following way (all steps are performed only if necessary):

  • Any files in the docs/ directory in the GAR are copied into the $GLOBUS_LOCATION/docs/<gar.id>/ directory.
  • Any files in the share/ directory in the GAR are copied into the $GLOBUS_LOCATION/share/<gar.id>/ directory.
  • Any files in the schema/ directory in the GAR are copied into the $GLOBUS_LOCATION/share/schema/ directory.
  • Any files in the etc/ directory in the GAR are copied into the $GLOBUS_LOCATION/etc/<gar.id>/ directory.
  • Any files in the bin/ directory in the GAR are copied into the $GLOBUS_LOCATION/bin/ directory.
  • Any .jar files in the lib/ directory of the GAR are copied into the $GLOBUS_LOCATION/lib/ directory.
  • Any file that contains the word "LICENSE" in the name in the lib/ directory of the GAR is copied into the $GLOBUS_LOCATION/share/licenses/ directory.
  • The server-deploy.wsdd in the GAR is copied to $GLOBUS_LOCATION/etc/<gar.id>/server-config.wsdd. If a profile name was specified during deployment, the server-deploy.wsdd will be copied to $GLOBUS_LOCATION/etc/<gar.id>/<profile.name>-server-config.wsdd. The server-config.wsdd file will be set with user-only access permissions.
  • The jndi-config-deploy.xml in the GAR is copied to $GLOBUS_LOCATION/etc/<gar.id>/jndi-config.xml. If a profile name was specified during deployment the jndi-config-deploy.xml will be copied to $GLOBUS_LOCATION/etc/<gar.id>/<profile.name>-jndi-config.xml. The jndi-config.xml file will be set with user only-access permissions.
  • The client-deploy.wsdd in the GAR is merged into a common $GLOBUS_LOCATION/client-config.wsdd file.
  • An undeploy script ($GLOBUS_LOCATION/etc/globus_packages/<gar.id>/undeploy.xml) is created.
  • A etc/post-deploy.xml Ant script is called if the GAR contains one. The setup target is called automatically.

Notes:

  • If the post-deploy.xml script creates some files, they will not be removed by undeploy.
  • During deployment, filtering is done for contents of the server-deploy.wsdd and jndi-config-deploy.xml files to replace the @config.dir@ token with the "etc/<gar.id>" value, and the @gar.id@ token with the "<gar.id>" value.

5.3.1.3. Creating a GAR file through Ant
5.3.1.3.1. Creating GAR file

To create a GAR file use the following example:

<property name="build.packages" location=
      "${deploy.dir}/share/globus_wsrf_common/build-packages.xml"/>
...
<property name="garjars.id" value="garjars"/>
<fileset dir="lib" id="garjars"/>

<property name="garetc.id" value="garetc"/>
<fileset dir="etc" id="garetc"/>
...
<target name="dist" depends="...">
  <ant antfile="${build.packages}" target="makeGar">
    <property name="gar.name" value="mygar.gar"/>
    <reference refid="${garjars.id}"/>
    <reference refid="${garetc.id}"/>
  </ant>            
</target>

The gar.name property must be passed. That property specifies the gar file to create. The makeGar task will look for deploy-client.wsdd, deploy-server.wsdd, and deploy-jndi-config.xml files in the base directory of the calling Ant process. All of these files are optional and do not have exist. The list of files to be included in the GAR file is passed via Ant references. The makeGar accepts the following references: garjars.id, garschema.id, garetc.id, garshare.id, gardocs.id, and garbin.id. All of these references are optional and do not have to be defined.

In the above example, all files in the etc and lib directories, and the deploy-client.wsdd, deploy-server.wsdd, and deploy-jndi-config.xml files (if they exist) will be included into the GAR file.

5.3.1.3.2. Deploying GAR file

To deploy a GAR file use the following example:

<property name="build.packages" location=
      "${deploy.dir}/share/globus_wsrf_common/build-packages.xml"/>
...
<target name="deploy" depends="...">
  <ant antfile="${build.packages}" target="deployGar">
    <property name="gar.name" value="mygar.gar"/>
  </ant>
</target>

The gar.name property must be passed. That property specifies the gar file to deploy. Optionally, the profile property can be passed to indicate which configuration profile the gar should be deployed under.

5.3.1.3.3. Undeploying GAR file

To undeploy a GAR file use the following example:

<property name="build.packages" location=
      "${deploy.dir}/share/globus_wsrf_common/build-packages.xml"/>
...
<target name="undeploy">
  <ant antfile="${build.packages}" target="undeployGar">
    <property name="gar.id" value="mygar"/>
  </ant>
</target>

The gar.id property must be passed. This property specifies the base name of the gar to undeploy.

5.3.2. Generating launcher scripts

Bourne Shell and Windows batch scripts can be automatically generated to hide the details of launching a Java program from the command line.

To generate such a command line script, write a Ant task that calls the generateLauncher target in $GLOBUS_LOCATION/share/globus_wsrf_common/build-launcher.xml. The following properties/parameters must be specified:

  • ${launcher-name} - the base name of script to generate.
  • ${class.name} - the name of Java class the script must call.

For example:

...
<property name="env.GLOBUS_LOCATION" value="."/>
<property name="deploy.dir" location="${env.GLOBUS_LOCATION}"/>
<property name="abs.deploy.dir" location="${deploy.dir}"/>
<property name="build.launcher"
        location="${abs.deploy.dir}/share/globus_wsrf_common/build-launcher.xml">
...
<ant antfile="${build.launcher}" target="generateLauncher">
  <property name="launcher-name" value="myClient"/>
  <property name="class.name" value="org.mypackage.MyClient"/>
</ant>

It is also possible to specify default JVM options and command line options via the default.jvm.options and default.cmd.line parameters. When passing multiple parameters using default.jvm.options for Unix/Linux scripts the parameters must be separated by ${DELIM} delimiter. For example:

<target name="generateUnixScripts" if="generate.unix" depends="testUnix">
  <ant antfile="${build.launcher}" target="generateLauncher">
    ...
    <property name="default.jvm.options" 
              value="-DFOO=&quot;$FOO&quot;${DELIM}-DBAR=&quot;$BAR&quot;/>
  </ant>
</target>

In general the generation of the command line scripts is done in the post-deploy.xml script during GAR deployment (globus-deploy-gar).

5.4. Writing and running tests

Tests in the Java WS Core are based on the JUnit API. JUnit must first be installed with Ant. To install JUnit with Ant copy the junit.jar found in JUnit distribution to the $ANT_HOME/lib directory. Alternatively, you can add the junit.jar to your CLASSPATH, or source $GLOBUS_LOCATION/etc/globus-devel-env.sh.

5.4.1. Writing Tests

Always make sure to group your tests under the PackageTests.java and/or SecurityTests.java test suites. Put all tests that require any type of credentials in the SecurityTests.java test suite.

If you are writing basic unit tests that do not require a container to run, just use the regular JUnit classes to write such tests.

If you are writing tests that require a container to execute, use the org.globus.wsrf.test.GridTestCase class instead of junit.framework.TestCase as your base class for your tests. Also ensure your PackageTests.java or SecurityTests.java extends the org.globus.wsrf.test.GridTestSuite instead of junit.framework.TestSuite.

The org.globus.wsrf.test.GridTestSuite and org.globus.wsrf.test.GridTestCase must be used together. The org.globus.wsrf.test.GridTestCase class exposes a TEST_CONTAINER variable that can be used to obtain the URL of the container (TEST_CONTAINER.getBaseURL()). By default an embedded container will be started for all tests in the test suite. To specify an external container, pass the -Dweb.server.url=<base.url> system property on the java command line.

5.4.2. Running Tests

5.4.2.1. Group testing

To execute all tests contained in a given jar file with an internal container run the following:

$ cd $GLOBUS_LOCATION
$ ant -f share/globus_wsrf_test/runtests.xml run -Dtests.jar=<test.jar>

Where <test.jar> is an absolute path to the jar file that contains the tests.

To execute all tests contained in a given jar file with an external container run the following:

$ cd $GLOBUS_LOCATION
$ ant -f share/globus_wsrf_test/runtests.xml runServer -Dtests.jar=<test.jar>

By default, the external container is assumed to be running at http://localhost:8080/wsrf/services/. To specify a different container, use the -Dtest.server.url=<url> property.

By default, all PackageTests and SecurityTests tests will be executed. To execute PackageTests only, specify -DbasicTestsOnly=true option. To execute SecurityTests only, specify -DsecurityTestsOnly=true option.

By default, the test results will be generated in the XML format.

5.4.2.2. Single testing

To execute a single test suite with an internal container run the following:

$ cd $GLOBUS_LOCATION
$ ant -f share/globus_wsrf_test/runtests.xml runOne -Dtest.class=<test.class>

Where <test.class> is a Java class that contains a test suite.

To execute a single test suite with an external container run the following:

$ cd $GLOBUS_LOCATION
$ ant -f share/globus_wsrf_test/runtests.xml runOneServer -Dtest.class=<test.class>

By default, the external container is assumed to be running at http://localhost:8080/wsrf/services/. To specify a different container, use the -Dtest.server.url=<url> property.

By default, the test results will be generated in the plain text format.

It is also possible to execute a single test case (or a set of test cases) within a test suite by specifying a -Dtests=<testCase1[,testCaseN]> property. However, this will only work with test suites that inherit from org.globus.wsrf.test.FilteredTestSuite or org.globus.wsrf.test.GridTestSuite classes. Example:

$ cd $GLOBUS_LOCATION
$ ant -f share/globus_wsrf_test/runtests.xml runOne \
   -Dtest.class=org.globus.interop.widget.test.PackageTests \
   -Dtests="testScenario1,testScenario2"
5.4.2.3. Test results and other options

The test reports will be put in the $GLOBUS_LOCATION/share/globus_wsrf_test/tests/test-reports directory by default. A different test reports directory can be specified by passing -Djunit.reports.dir=<directory>.

Use -Djunit.test.format property to generate the test results in a specified format (xml or plain). Example:

$ ant -f share/... -Djunit.test.format=plain

Use -Djunit.jvmarg to pass arbitrary properties to the testing JVM. Example:

$ ant -f share/... -Djunit.jvmarg="-Dorg.globus.wsrf.container.server.id=myServerID"

5.5. Other

5.5.1. Adding a new query/topic expression evaluator

Java WS Core allows for custom query/topic expression evaluators to be plugged in. The process of adding a new query/topic expression evaluator is composed of three steps:

5.5.1.1. Step 1: Implement the evaluator

Table 7. Evaluator interfaces

If the evaluator is a... then it must implement:
query expression evaluator org.globus.wsrf.query.ExpressionEvaluator
topic expression evaluator org.globus.wsrf.topicexpression.TopicExpressionEvaluator
5.5.1.2. Step 2: Register the evaluator

The evaluators must be registered in order for Java WS Core to recognize them. The registration is done through the JNDI configuration file. The expression evaluators must be deployed as global resources under a specific subcontext.

5.5.1.2.1. Registering query expression evaluators

The query expression evaluators must be deployed as global resources under the query/eval/ subcontext in the JNDI configuration file.

Example:

<global>
  <resource name="query/eval/MyQueryExpressionEval"
            type="foo.bar.MyQueryExpressionEvaluator">
    <resourceParams>
      <parameter>
        <name>factory</name>
        <value>org.globus.wsrf.jndi.BeanFactory</value>
      </parameter>
    </resourceParams>
  </resource>
</global>

Where the <resource> attribute:

name Specifies the name of the evaluator in JNDI space. The name can be arbitrary as long as it is unique and is in the right subcontext as explained above.
type Specifies the class that implements the expression evaluator.
5.5.1.2.2. Registering topic expression evaluators

Topic expression evaluators must be deployed as global resources under the topic/eval/ subcontext in the JNDI configuration file.

Example:

<global>
  <resource name="topic/eval/MyTopicExpressionEval"
            type="foo.bar.MyTopicExpressionEvaluator">
    <resourceParams>
      <parameter>
        <name>factory</name>
        <value>org.globus.wsrf.jndi.BeanFactory</value>
      </parameter>
    </resourceParams>
  </resource>
</global>

Where the <resource> attribute:

name Specifies the name of the evaluator in JNDI space. The name can be arbitrary as long as it is unique and is in the right subcontext as explained above.
type Specifies the class that implements the expression evaluator.
5.5.1.3. Step 3: Register the serializer/deserializer for the evaluator

A serializer/deserializer must be registered for the dialect of the evaluator in order for the expression to be properly serialized and deserialized. The serializers/deserializers for the dialect are deployed as almost any other type mapping. In general, each type mapping specifies a type QName. For dialect serializers/deserializers, that type QName takes a slightly different name.

5.5.1.3.1. Specifying the QName for query expression evaluators

For query expression evaluators, that QName must have the local name part set to QueryExpressionDialect and namespace part set to the dialect of the query expression evaluator.

Example:

<typeMapping 
   encodingStyle="" 
   deserializer="org.apache.axis.encoding.ser.SimpleDeserializerFactory"
   serializer="org.apache.axis.encoding.ser.SimpleSerializerFactory"
   type="java:java.lang.String"
   qname="ns12:QueryExpressionDialect" 
   xmlns:ns12="http://foo.bar/MyQueryDialect"/>

[Note]Note

These type mappings must be deployed both on the client and the server.

5.5.1.3.2. Specifying the QName for topic expression evaluators

For topic expression evaluators, that QName must have the local name part set to TopicExpressionDialect and namespace part set to the dialect of the topic expression evaluator.

Example:

<typeMapping 
   encodingStyle="" 
   deserializer="org.apache.axis.encoding.ser.SimpleDeserializerFactory"
   serializer="org.apache.axis.encoding.ser.SimpleSerializerFactory"
   type="java:java.lang.String"
   qname="ns12:TopicExpressionDialect" 
   xmlns:ns12="http://foo.bar/MyTopicDialect"/>

[Note]Note

These type mappings must be deployed both on the client and the server.

5.5.1.4. Step 4: Configuring a helper serializer for GetCurrentMessageProvider

The standard GetCurrentMessageProvider might not know how to properly serialize the notification message currently associated with the specified topic. The GetCurrentMessageProvider can be configured to use a helper serializer for a given notification message type.

To configure such a helper serializer, define the following global resource in your deploy-jndi.xml configuration file:

<global>
  <resource
    name="providers/GetCurrentMessageProvider/foo.bar.MyNotificationMessage"
    type="foo.bar.MyMessageSerializer">
    <resourceParams>
      <parameter>
        <name>factory</name>
        <value>org.globus.wsrf.jndi.BeanFactory</value>
      </parameter>
    </resourceParams>
  </resource>
</global>

Where the <resource> attribute:

name Must start with providers/GetCurrentMessageProvider/ and must end with the full class name of the notification message.
type Specifies the class that implements the org.globus.wsrf.encoding.ObjectConverter interface and is responsible for serializing the notification message. The GetCurrentMessageProvider will use the type of the notification message to find the helper serializer.

7. Debugging

7.1. Logging

Logging in the Java WS Core is based on the Jakarta Commons Logging API. Commons Logging provides a consistent interface for instrumenting source code while at the same time allowing the user to plug-in a different logging implementation. Currently we use Log4j as a logging implementation. Log4j uses a separate configuration file to configure itself. Please see Log4j documentation for details on the configuration file format.

Java WS Core is deployed with two Log4j configuration files:

  • $GLOBUS_LOCATION/container-log4j.properties (configures logging for the standalone container)
  • $GLOBUS_LOCATION/log4j.properties (configures logging for everything else besides the standalone container)

7.2. Tracing SOAP messages

7.2.1. Using MessageLoggingHandler

The simplest method for logging SOAP messages is to add the org.globus.wsrf.handlers.MessageLoggingHandler to the request or response chain in the server-config.wsdd or client-config.wsdd files.

For example:

<requestFlow>  
  ...  
  <handler type="java:org.globus.wsrf.handlers.MessageLoggingHandler"/>   
  ...
</requestFlow>

Then you must enable logging for this handler class in the appropriate log4j.properties files and change the logging level to DEBUG:

log4j.category.org.globus.wsrf.handlers.MessageLoggingHandler=DEBUG

7.2.2. Enabling logging for Axis classes

Another method for tracing SOAP messages is to enable logging for selected Axis classes. Add the following lines to the appropriate log4j.properties files:

log4j.category.org.apache.client.Call=DEBUG
log4j.category.org.apache.axis.transport.http.HTTPSender=DEBUG
# enable the following logger for HTTPS/HTTPG transport handlers
log4j.category.org.globus.axis.axis.transport=DEBUG

This will log Axis client side calls and Axis HTTP messages.

7.2.3. Using TcpMonitor

To trace SOAP messages on the wire you can use TcpMon from Apache Axis. After setting the environment using $GLOBUS_LOCATION/etc/globus-dev-env.[sh|csh|bat] run:

$ java org.apache.axis.utils.tcpmon [listenPort targetHost targetPort]

If no arguments are used, you have to fill out these values in the GUI. Make sure to also start the standalone container with the proxy server port option set to the listenPort value.

7.3. Debugging Java process

JVM vendors provide useful tools and troubleshooting guides for debugging Java processes. Please use those guides for debugging your programs, for example:

7.4. Debugging hanged Java process

If a Java process appears to hang, for example in case of the standalone container, the list of deployed services is not shown after a while or all requests to the container time out, requesting the JVM thread dump might help diagnose the problem.

To request JVM thread dump run:

kill -QUIT <JVM process id>

If this command is successful, the thread dump information should be printed to the standard output or error of the Java process. Therefore, the thread dump information might appear on the console of that process or in a file to which the standard output/error of process is redirected to. Please also note that on certain JVMs the thread dump information might be put in a separate file.

When filing bugs of such nature please always include the JVM thread dump information.

7.5. Debugging Log4j

If you are having problems with configuring Log4j, you can enable internal Log4j debugging by adding -Dlog4j.debug=true option on the java command line or passing it via the GLOBUS_OPTIONS environment property.

8. Troubleshooting

8.1. No socket factory for 'https' protocol

When a client fails with the following exception:

java.io.IOException: No socket factory for 'https' protocol
    at org.apache.axis.transport.http.HTTPSender.getSocket(HTTPSender.java:179)
    at org.apache.axis.transport.http.HTTPSender.writeToSocket(HTTPSender.java:397)
    at org.apache.axis.transport.http.HTTPSender.invoke(HTTPSender.java:135)

add the following to the client:

import org.globus.axis.util.Util;
...
static {
    Util.registerTransport();
}
...

8.2. No client transport named 'https' found

When a client fails with the following exception:

No client transport named 'https' found
   at org.apache.axis.client.AxisClient.invoke(AxisClient.java:170)
   at org.apache.axis.client.Call.invokeEngine(Call.java:2726)

The client is most likely loading an incorrect client-config.wsdd configuration file. Ensure that the GT4 installation directory is listed as a first entry in the CLASSPATH of the client. For example:

CLASSPATH=/usr/local/globus-4.1.2.0:/foo/bar/others.jar:...

If you are seeing this problem in Tomcat, copy the client-config.wsdd from the GT4 installation directory to the Web application's WEB-INF/classes directory.

8.3. General troubleshooting information

In general, if you want to investigate a problem on your own please see Section 7, “Debugging” for details on how to turn on debugging. Also, please note that most of the command line clients have a -debug option that will display more detailed error messages, including the error stack traces. Also, searching the mailing lists such as gt-user@globus.org or jwscore-user@globus.org (before posting a message) can also be very fruitful. Finally, if you think you have found a bug please report it in our Bugzilla system. Please include as much as detail about the problem as possible.

9. Related Documentation

Overview material about WSRF and WSN and more information on the implemented WSDL and schema can be found here.

Information about ongoing standards work can be found here:

10. Appendices

10.1. Proxy support

A basic proxy support is provided. The org.globus.wsrf.proxy.port system property can be set to the port of the proxy server (the proxy server must run on the same machine as the container). This will make any code that uses the ServiceHost or AddressingUtils API return the address of the proxy server instead of the container. This could be useful, for example, for debugging purposes. The org.globus.wsrf.proxy.port system property can be passed to globus-start-container script via the GLOBUS_OPTIONS environment property. For example:

 $ setenv GLOBUS_OPTIONS="-Dorg.globus.wsrf.proxy.port=5555"
 $ globus-start-container

Please note that not all of the code will obey the proxy port setting.

10.2. Creating source and binary distributions

The wsrf/build.xml Ant script can be used generate Java WS Core source and/or binary distributions. Use the following syntax to create the distributions:

ant <distTask> -Dsrc.tag=<tag> -Dversion=<version>

The <distTask> must be one of:

  • distSource - create source code only distribution

  • distBinary - create binary only distribution

  • dist - create source and binary distributions

The -Dsrc.tag specifies the CVS tag and -Dversion specifies the version of the code. Make sure to execute this Ant script from within the wsrf/ directory. Each of these tasks checks out the source code from the CVS and uses it to create the distributions.

Source-only example:

ant distSource -Dsrc.tag=globus_4_0_3 -Dversion=4.0.3

The above command will create ws-core-<version>-src.zip and ws-core-<version>-src.tar.gz files under the wsrf/ directory.

Binary-only example:

ant distBinary -Dsrc.tag=globus_4_0_3 -Dversion=4.0.3

The above command will create ws-core-<version>-bin.zip and ws-core-<version>-bin.tar.gz files under the tmp/ws-core-<version>/ directory.

Source and binary example:

ant dist -Dsrc.tag=globus_4_0_3 -Dversion=4.0.3

The above command will generate both distributions at the same time.

10.2.1. In-place binary distributions

A binary distribution can also be created from the existing source code by running (in the wsrf/ directory):

ant distbin

The above command will create ws-core-<version>-bin.zip and ws-core-<version>-bin.tar.gz files in the current directory.