Software Links
Getting Started
- A Globus Primer
- Globus Is Modular!
- Quickstart
- Installing GT
- Platform Notes
- GT Developer's Guide
- GT User's Guide
- Migrating Guides
Reference
Manuals
Common Runtime
Security
- GSI C
- GSI Java
- Java WS A&A
- C WS A&A (coming soon)
- CAS
- Delegation Service
- MyProxy
- GSI-OpenSSH
- SimpleCA
Data Mgt
WS MDS
Execution Mgt
Table of Contents
- 1. Service Scope and Lifetime
- 2. Operation Providers
- 3. Connecting to Services
- 4. Recipes about working with Endpoint Addressing
- 5. Object Serialization
- 6. Notifications
- 7. JNDI, Resources and Objects
- 8. Asynchronous Work and Threads
- 9. Querying Properties
- 10. Build Recipes
- 11. Controlling client connection settings
- 12. Writing and running tests
- 13. Miscellaneous Recipes
By default, a service is initialized on the first invocation of a method in that service. However, there are frequently times when a service needs to run some time-consuming, global initialization. In this situation, it is better to have the service initialize when the container starts, so as to not impact the performance of the first method invocation. To have the service initialize on container starup, add the following to your deploy-service.wsdd descriptor:
<parameter name="loadOnStartup" value="true"/>
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. (See next question)
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.
The scope setting of the service dictates when and how service
instances are created. It is generally recommended that services use the Application scope (even
though it is not currently the default) because it associates the security credentials with the
thread. To set the scope, you add the following parameters to your deploy-server.wsdd:
<parameter name="scope" value="Application"/>
<parameter name="handlerClass" value="org.globus.axis.providers.RPCProvider"/>
This will specify to use the globus supplied RPCProvider and to set the scope to Application. This is the recommended setting for most services. If you have any question about what to use, set this.
There are two scope options:
Table 1. Service Scopes
| Application Scope |
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 “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 Scope |
One instance is created per invocation. This is the default (if scope parameter is not set in the deployment descriptor). Service/Provider instances are created per invocation, ServiceLifecycle methods called right before and after service method invocation, no JAAS credentials during ServiceLifecycle methods |
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:
- Change the value of the
providerattribute toHandler. - Add a
handleClassparameter with a value oforg.globus.axis.providers.RPCProvider. Specify providers in the
providersparameter.The value of the parameter is a space-separated list of either provider names or class names.
- Add or change the value of the
scopeparameter toApplicationorRequest.
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 |
|---|---|
The operations defined in the |
Any service can provide an object for other service to include as a operation provider. The
consuming service simply needs to add the fully qualified classname (FQCN) to the list of providers.
However, if you wish to allow users to refer to your class by a friendly name instead of FQCN, you
need to add an entry to the container's main deployment file 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>
These recipes are all about connecting to services running in a Java WS Core container.
There are several ways to connect to a service. The simplest is to use the AddressingLocator that is created as part of the WSDL2Java execution. The AddressingLocator class can be used to get a stub for a service by passing the Apache Addressing EndpointReferenceType parameter. Each service has it's own AddressingLocator which can be used to acquire a port for the remote service. The port can then be used to access the service. If an EndpointReferenceType is returned from a method on that service, it can be used to acquire the port from the AddressingLocator and will be associated with the resource context created by the service.
//Specify the service address
String url = "http://localhost:8080/wsrf/services/Version";
//Create the EndpointReferenceType
EndpointReferenceType epr = new EndpointReferenceType();
epr.setAddress(new Address(url));
//Create the AddressingLocator for the service
VersionServiceAddressingLocator locator = new VersionServiceAddressingLocator();
//Get the port for the service
VersionServicePortType port = locator.getVersionPort(epr);
port.getVersion();
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:
Create a service URL with "local" protocol:
URL url = new URL("local:///wsrf/services/MyService");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();
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.
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 |
|---|---|
The |
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[]);
In the WS-RF and WS-N specifications, the WS-Addressing
ReferenceParameters are used to carry resource identity
information. In previous versions of the WSRF/WSN specification, ReferenceProperties was used for
the same purpose. The resource identity can be anything as long as it serializes as a XML element.
The ReferenceParameters 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 parameters.
For example, create ReferenceParametersType 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);
ReferenceParametersType props = new ReferenceParametersType();
// 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.setReferenceParameters(props); // pass the addressinginfo 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.setParameters(props);
VersionServiceAddressingLocator locator = new VersionServiceAddressingLocator();
VerionServicePortType port = locator.getVersionPort(epr);
port.getVersion();
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);
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)
{} } } There are a few steps involved in setting up and receiving notifications:
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 |
|---|---|
The |
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:
import org.globus.wsrf.encoding.DeserializationException;
import org.globus.wsrf.utils.NotificationUtil;
import org.oasis.wsrf.properties.ResourcePropertyValueChangeNotificationType;
...
// Notification callback
public void deliver(List topicPath, EndpointReferenceType producer, Object message) {
ResourcePropertyValueChangeNotificationType changeMessage = null;
try {
changeMessage = NotificationUtil.getRPValueChangeNotification(message);
} catch (DeserializationException e) {
// handle exception
}
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 Section 5.1, “How can I handle information defined as xsd:any in my service?”
section for more information on how to deal with such types.
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.
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 |
|---|---|
On the client when the On the server when the |
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 {
....
}
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;
...
Subscribe request = new Subscribe();
request.setConsumerReference(consumerEPR);
TopicExpressionType topicExpression = new TopicExpressionType();
topicExpression.setDialect(WSNConstants.SIMPLE_TOPIC_DIALECT);
topicExpression.setValue(Counter.VALUE);
MessageElement element = (MessageElement)ObjectSerializer.toSOAPElement(topicExpression, WSNConstants.TOPIC_EXPRESSION);
FilterType filter = new FilterType();
filter.set_any(new MessageElement[] { element });
request.setFilter(filter);
port.subscribe(request);
...
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) {
//Handle Exception
}
}
}
While the typical usage of WS-Notification within WSRF is to notify subscribers when a
ResourceProperty changes, this is not a requirement. In fact, it is easy to set up a WS-Notification
topic to support general messages. The Resource which will handle your topic should implement
org.globus.wsrf.TopicListAccessor. This means your Resource
needs to implement the TopicList getTopicList() method. The
simplest way to do this, is to create an instance of SimpleTopicList in your resource like such:
public class MyResource implements ResourceProperties, TopicListAccessor{
private TopicList topicList;
private ResourcePropertySet propertySet;
private QName qname = new QName("http://www.globus.org/MyService", "MyResource");
public MyResource(){
topicList = new SimpleTopicList(this);
propertySet = new ResourcePropertySet(qname);
}
public TopicList getTopicList(){
return topicList;
}
public ResourcePropertySet getResourcePropertySet(){
return propertySet;
}
}
Once you have this, your resource is ready to work with topics. To create a new
Topic in thisresource, you need to define the
Topic and add it to your
TopicList like so (this is in the same class as above):
import org.globus.wsrf.impl.SimpleTopic;
. . .
private static final QName topicName = new QName("http://www.globus.org/MyService", "MyTopic");
private void init(){
Topic myTopic = new SimpleTopic(topicName);
this.getTopicList().addTopic(myTopic);
}
There is nothing magical about the names or what method this occurs in. For simplicity's sake
let's just assume that our Resource has an init method that will set up the
Topic. This creates a new Topic
with the supplied identifier and adds it to our list of Topics. Ok, our setup is done.
Java Core has provided a helper class to assist in subscribing to a
Topic from a service. The
SubscribeHelper provides a simple way for you service to subscribe
a client to a Topic. You can use it like this:
private EndpointReferenceType subscribe(QName topicName, ResourceKey key, Resource resource,
EndpointReferenceType consumerAddr) throws ServiceException, RemoteException {
Subscribe request = new Subscribe();
request.setConsumerReference(consumerAddr);
try {
TopicExpressionType topicExpression = new TopicExpressionType();
topicExpression.setDialect(WSNConstants.SIMPLE_TOPIC_DIALECT);
topicExpression.setValue(topicName);
MessageElement element =
(MessageElement) ObjectSerializer.toSOAPElement(topicExpression, WSNConstants.TOPIC_EXPRESSION);
FilterType filter = new FilterType();
filter.set_any(new MessageElement[]{element});
request.setFilter(filter);
ResourceContext ctx = ResourceContext.getResourceContext();
String homeLocation = ((ResourceContextImpl) ctx).getResourceHomeLocation();
return new SubscribeHelper(
ctx, resource, key, homeLocation, request).subscribe();
} catch (Exception e) {
throw new RemoteException("Error creating Subscription", e);
}
}
This is a little ugly, but it is boilerplate code. Specify your topicName, resourceKey, resource, and the EndpointReferenceType of the client you are subscribing, and this will handle the subscription for you. You return the EndpointReferenceType of the subscription, so that the client can unsubscribe when they are complete.
Finally, once you have all of this set up the last piece is to actually send a message on your topic. This is pretty easy:
LinkedList topicPath = new LinkedList();
topicPath.add(myTopicQName);
Topic myTopic = this.getTopicList().getTopic(topicPath);
myTopic.notify(myMessage);
This will send the notification to the Topic and, as a result, to
all consumers for that topic.
It is not strictly necessary to use all of the ResourceProperties
and TopicListAccessor Interfaces as shown above, but this is the
scenario for which Java WS Core provides explicit support.
These recipes all relate to using JNDI to store resources and objects to be used by your service
Services should avoid creating and destroying resources that live beyond the lifetime of a resource. However, this does not mean that shouldn't use objects that have a longer lifetime. Java WS Core uses JNDI to support these objects. Several objects already exist in JNDI, having been put there by Java WS Core.
commonj.work.WorkManager -- Constants.DEFAULT_WORK_MANAGER
This is a WorkManager for handling asynchronous work
commonj.timers.TimerManager -- Constants.DEFAULT_TIMER
This a timer for scheduling future or recurring asynchronous work
org.globus.wsrf.query.QueryEngine -- Constants.DEFAULT_QUERY_ENGINE
This is used to map queries on the service data of a service to the appropriate ExpressionEvaluators and then return the result.
org.globus.wsrf.topicexpression.TopicExpressionEngine -- Constants.DEFAULT_TOPIC_EXPRESSION_ENGINE
The TopicExpressionEngine interface is used to map queries on a topic list to the appropriate TopicExpressionEvaluators and then return the result.
javax.sql.DataSource -- Constants.DEFAULT_DATABASE
This is an in-memory base (Apache Derby), which can be used by services
To access these objects from JNDI, you can use a convenience method provided by Java WS Core.
import org.globus.wsrf.jndi.JNDIUtils;
import org.globus.wsrf.Constants;
...
WorkManager wm = (WorkManager) JNDIUtils.getInitialContext().lookup(Constants.DEFAULT_WORK_MANAGER);
Note, that you need to cast whatever you lookup to the proper type, which is, in this case, WorkManager. The org.globus.wsrf.Constants class has constant fields for looking up each of standard resources provided by Java WS Core
As stated before, services should avoid creating objects and resources that have a lifetime that extends beyond a single request directly, so service developers should create and store those objects in JNDI. Any object can be stored in JNDI. The simplest way to implement an object as a JavaBean, i.e., having properties with corresponding get/set methods. Let's assume we have a bean that has a timeout property. We can configure this to be put in JNDI by putting the following information in our jndi-config-deploy.xml file.
<resource name="MyBean" type="org.mypackage.MyBean">
<resourceParams>
<parameter>
<name>timeout</name>
<value>10000</value>
</parameter>
</resourceParams>
</resource>
This would set the timeout property to be 10000. All properties which follow the JavaBean pattern can be set this way.
If you want to work with a class that does not follow the JavaBean pattern, or that requires a greater level of set up, you can implement your own factory to be used by JNDI. The interface for ObjectFactory only has one method to be implemented
Object getObjectInstance(Object obj, Name name, Context nameCtx, Hashtable environment)
The parameters to this method provide all of the information necessary to create your object. In most situations, you will only work with the first parameter.
if (obj instanceof ResourceRef) {
Reference ref = (Reference) obj;
int timeout = ref.get("timeout");
MyBean bean = new MyBean();
bean.setTimeout(timeout);
return bean;
}else{
return null;
}
This is the most common way of creating an object factory. Assuming the first parameter is an instance of ResourceRef, we have a simple way of getting the parameters defined in the configuration. The get() method of ResourceRef allows us to access those parameters. Once you have your parameters, create and configure your object, then return it. Now you can use this object just like you could any of the resources that Java WS Core supplies for you. The lookup name is specicified by the name attribute of the resource element in your configuration. To lookup an object you have supplied, you still use the JNDIUtils as before, specifying the proper lookup path.
MyBean bean = (MyBean) JNDIUtils.getInitialContext().lookup(Constants.DEFAULT_WSRF_LOCATION + "MyService/MyBean");
Any object you define in your jndi-config-deploy.xml file can be found at the default wsrf location followed by your service name and the name you specified in the jndi-config-deploy.xml file.
You can use the container to maintain your threads for you. This is considered the best approach
towards dealing with concurrency. The container's thread management is handled by the WorkManager class. The WorkManager is abstraction on top of how a task (or Work) is
executed. It also assures that the execution context will be set correctly for the task. The
abstract nature of the WorkManager provides a lot of flexibility to do things such as execute work
in a pool, or on a remote cluster To use the WorkManager, you need to construct a Work object. Work
just extends the Runnable interface to supply a method which allows the Work to gracefully end its
processing. Basically, though, you only need to implement a run()
method, just like you normally would. One advantage the WorkManager provides is that the caller can
get information about the progress of Work which has been submitted to the WorkManager. This
includes the state, as well as any information regarding exceptions that might have been thrown. The
WorkManager works in a similar way to the ExecutorServices.
import commonj.work.WorkManager;
...
WorkManager manager = ... //acquired from JNDI
manager.scheduleWork(myWork);
The above code will execute the myWork asynchronously in a separate
thread. It is also possible to run work synchronously in a separate thread:
import commonj.work.WorkManager;
...
WorkManager manager = ... //acquired from JNDI
manager.doWork(myWork);
This will also execute the myWork in a separate thread, but it will
block until the job has completed. The doWork method should be used
sparingly.
Java WS Core sets up a default WorkManager which can be used by any service. The default Workmanager can be found in JNDI by doing the following:
WorkManager manager = (WorkManager) JNDIUtils.getInitialContext().lookup(Constants.DEFAULT_WORK_MANAGER);
(See the section on JNDI Objects for details about looking up the WorkManager)
However, the job queue and thread pool for the default WorkManager is shared amongst all of the services, and, as such, may not be appropriate for time-sensitive jobs. In addition, the default WorkManager has a simple default configuration, which may not meed the needs of a service. Most services will want to configure one or more WorkManagers for use by their service. To configure a WorkManager for your service, you will need to add a resource to your jndi-deploy-config.xml file (see the section on configuring JNDI for an overview)
There is only one parameter currently used for the WorkManager (more will be coming soon). This is the maximumPoolSize. This allows you to set the maximum number of threads that will be running at any given time. To configure a WorkManager for your service, you can specify the following:
<resource name="wm/WorkManager1" type="org.globus.wsrf.impl.work.WorkManagerImpl">
<resourceParams>
<parameter>
<name>maximumPoolSize</name>
<value>5</value>
</parameter>
</resourceParams>
</resource>
This will create a WorkManager that has a maximum size of 5 threads. Note that this is only the maximum number of threads. The WorkManager can still accept an unlimited number of jobs, but they will be queued until a thread is available to service them. This WorkManager can be acquired using the following code:
WorkManager WorkManager1 = (WorkManager) JNDIUtils.getInitialContext().lookup(Constants.DEFAULT_WSRF_LOCATION + "/MyService/wm/WorkManager1");
Now the WorkManager can service your Work objects. Your service can use multiple WorkManagers. In fact, it is often advisable to have more than one WorkManager with a fairly small pool of threads (5-10) where each WorkManager processes a particular stage of your job. That way, long running tasks won't block fast tasks from executing.
Sometimes a service will need to run a process that lasts for a very long time (for example, for
the lifetime of the container). A typical example, would be a monitoring process that is constantly
listening for some event to occur. Clearly, the service won't want one of the Threads from its
WorkManager pool to be occupied with this permanent task. To resolve this, you can specify your Work
object as a Daemon which will launch it in a new Thread automatically. You should still use the
WorkManager for this task, since it sets the context for you and can reuse threads when a Daemon has
completed. The only difference is that when you create your Work object, you will return
true for the isDaemon()
method.
Creating and using Java threads is a complicated task because of classloader, security and transction contexts. As such, it is strongly recommended that service developers not launch their own threads. If you, as a developer, feel you must launch your own thread, do so at your own risk. Here are a couple of pieces of advice, however if you are creating your own threads:
Use the java.utils.concurrent constructs
Java 5 has introduced a whole suite of concurrency tools which hide the details of handling threads. You should take advantage of these, specifically, the ExecutorService framework. This provides a way of abstracting thread pools and other thread handling.
import java.util.concurrent.Executors; import java.util.concurrent.ThreadPoolExecutor; ... //Create the Executor ExecutorService executor = Executors.newFixedThreadPool(numOfThreads); ... //Now use the executor executor.submit(myRunnable);This assumes that
myRunnableis an instance of a class that implements Runnable. The above code will submit the Runnable to the executor, and when a thread becomes available, execute the Runnable. There are other options for creating your Executor service and if you have other needs, you should read the javadocs. If you are going to use an ExecutorService, you should either set your service scope to "application", store the ExecutorService in JNDI or both. Otherwise, the ExecutorService will be created everytime your service has a request.Use the ClassLoaderUtils from CoG
It is difficult to keep track of classloaders when you are working in threads. If you need to load a class using a method like
Class.forName(name)or load a resourse likegetClass().getResource(resource)you must take special care to ensure that you are using the correct classloader. CoG provides a utility to help ensure that you are using the correct classloader. Instead of calling the above methods, useClassLoaderUtils.forName(name)orClassLoaderUtils.getResource(resource). The ClassLoaderUtils has other helper methods as well. The documentation can be found here
Even with these tools, it is possible you may still run into problems with Threads, but these should help get you on the way.
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.
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);
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:
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.
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. |
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. |
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.
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 |
|---|---|
These type mappings must be deployed both on the client and the server. |
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 |
|---|---|
These type mappings must be deployed both on the client and the server. |
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. |
Java WS Core supplies some helper build files to simplify the task of processing wsdl. In order to use the helper build files, you should tell ant about GLOBUS_LOCATION. You can set an Ant property to do this:
<property environment="env"/>
<property name="globus_location" value="${env.GLOBUS_LOCATION}">
The first property statement creates a property which holds all of the system
environment variables and the second assigns the GLOBUS_LOCATION environment variable to the
property globus_location. Obviously, this means you need to have
set the GLOBUS_LOCATION variable in your environment. Once you have set this, you can use this
property in your target.
If you have used the wsdlpp:extends (where wsdlpp is
http://www.globus.org/namespaces/2004/10/WSDLPreprocessor)
property in your wsdl, the first step to preparing your stubs is to flatten your wsdl file. To run
this task, use this:
<ant antfile="${build.stubs}" target="flatten">
<property name="source.flatten.dir"
location="${schema.src}/myservice"/>
<property name="target.flatten.dir"
location="${schema.dest}/myservice"/>
<property name="wsdl.source"
value="myservice_port.wsdl"/>
<property name="wsdl.target"
value="myservice_flattened.wsdl"/>
<property name="wsdl.porttype" value="MyServicePortType"/>
</ant>
The arguments here are fairly self-explanatory, but just to clarify the
source.flatten.dir represents the directory that your wsdl file
is in, the target.flatten.dir is the directory that you want your
processed wsdl file to be written to, wsdl.source is the name of
the wsdl file that you are processing, wsdl.target is the name that
you want the new wsdl file to be called, and wsdl.porttype is the
port type that you are processing (as defined in your wsdl file. You'll notice that there are
references to properties such as ${build.stubs},
${schema.src}, etc. These are just properties I set up. You can
set these up to whatever you wish (or just use the paths directly instead of using properties).
The next step is to generate the bindings for your service. This is very similar to the previous step:
<target name="binding" depends="flatten">
<ant antfile="${build.stubs}" target="generateBinding">
<property name="source.binding.dir"
location="${schema.dest}/myservice"/>
<property name="target.binding.dir" location="${schema.dest}/myservice"/>
<property name="binding.root" value="myservice"/>
<property name="porttype.wsdl"
value="myservice_flattened.wsdl"/>
</ant>
</target>
Again, the arguments should be fairly clear. The
source.binding.dir is the directory where the wsdl that you want to
bind resides (this will almost always be the target.flatten.dir
from the previous step). The target.binding.dir is the directory
where the bindings will be written. The binding.root is the base
name of the outputted wsdl files, and the porttype.wsdl is the name
of the wsdl file to bind (This will almost always be the name of the
wsdl.target from the previous step).
The final step to prepare your wsdl is to generate the stub classes. These are the java classes to which you will refer in your service and your clients. Similar to the previous two steps:
<target name="stubs" depends="binding">
<ant antfile="${build.stubs}" target="mergePackageMapping">
<property name="mapping.src" value="${basedir}/namespace2package.properties"/>
<property name="mapping.dst" value="${build.dir}/namespace2package.properties"/>
</ant>
<ant antfile="${build.stubs}" target="generateStubs">
<property name="source.stubs.dir"
location="${schema.dest}/test/performance"/>
<property name="target.stubs.dir" location="${stubs.src}"/>
<property name="wsdl.file"
value="performance_test_service.wsdl"/>
<property name="mapping.file" value="${build.dir}/namespace2package.properties"/>
</ant>
</target>
There are actually two Globus-provided tasks in this target. The first one is optional. If you
have custom mappings to map namespaces to Java package names, you can create a properties file that
lists those mappings. Then use the mergePackageMapping task to
merge your mappings with the default mappings. The parameters are:
mapping.src -- where your custom mappings live and
mapping.dst -- where you want the merged file to be written.
The final task here (generateStubs) actually generates the stub
classes. The parameter names here are not entirely intuitive.
source.stubs.dir is the directory where your wsdl's live.
target.stubs.dir is where you want the Java source files to be
written. wsdl.file is the name of your wsdl file, and
mapping.file is the location of the (optional)
namespace-package mapping file.
Once you have run all of these steps, you should generated all of the java stubs and bindings that you need to build your service.
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.
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.
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="$FOO"${DELIM}-DBAR="$BAR"/>
</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).
There is no automated process for this, but there are a few simple steps that can provide the pieces that you need. (Note, this is one possible approach. Other methods can work too.) First, when you are building your service, you need to generate your stubs in their own directory. From Ant, this would look like this (from the CounterService example):
<target name="stubs" unless="stubs.present" depends="init">
<ant antfile="${build.stubs}" target="generateStubs">
<property name="source.stubs.dir"
location="${schema.dest}/core/samples/counter"/>
<property name="target.stubs.dir" location="${stubs.src}"/>
<property name="wsdl.file"
value="counter_service.wsdl"/>
</ant>
</target>
The source.stubs.dir determines where the stub classes are
created. This, in turn, will allow us to compile those classes into their own compile directory like
this (also from the CounterService example):
<target name="compileStubs" depends="stubs">
<javac srcdir="${stubs.src}" destdir="${stubs.dest}"
debug="${java.debug}">
<include name="**/*.java"/>
<classpath>
<fileset dir="${deploy.dir}/lib">
<include name="common/*.jar"/>
<include name="*.jar"/>
<exclude name="${stubs.jar.name}"/>
<exclude name="${jar.name}"/>
</fileset>
</classpath>
</javac>
<copy todir="${build.dest}" >
<fileset dir="src" includes="**/*.properties" />
<fileset dir="src" includes="**/*.xml" />
</copy>
</target>
So, now we have our stubs compiled into their own directory and copied all resource files into the same directory. The final step in preparing the stubs is to jar them up:
<target name="jarStubs" depends="compileStubs">
<jar destfile="${build.lib.dir}/${stubs.jar.name}"
basedir="${stubs.dest}"/>
</target>
At this point, we have a jar which contains all of our stubs. This will be used on both the server
and by the clients. Now, you need to write your client code (probably in it's own src dirctory),
compile it and create a jar for it. These two jars are the only jars you need to produce. In
addition you need to make sure that you have an installation of Java WS Core available. The root
directory of your installation should have both a
client-config.wsdd and a
log4j.properties file in it. You can combine this with the launcher
example above to build a launcher
for the client jars which you have just generated.
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 |
|---|---|
Please note that once a connection property is set on a given Stub, it is applied to ALL calls made using that Stub. |
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();
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();
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();
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();
One of the most important tasks in developing services is to create tests to assure that your code functions correctly. The following recipes discuss creating tests for Java WS Core.
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.
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 ofjunit.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.
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 athttp://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.
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 athttp://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"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
orplain).
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"
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());
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.
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.
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 |
|---|---|
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:
Also, with wrapped/literal mode, the element name had to match the operation name in wsdl. This is not necessary with document/literal mode. |
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.
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 although it is extremely rare. If you aren't sure that you need to do this, you likely do not. 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 |
|---|---|
Specifying parameters using WSDD files depends on Axis and will likely not be supported in future versions of the toolkit. |
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.
![[Note]](/docbook-images/note.gif)
![[Important]](/docbook-images/important.gif)