org.xins.common.service
Class ServiceCaller

java.lang.Object
  extended byorg.xins.common.service.ServiceCaller
Direct Known Subclasses:
HTTPServiceCaller, XINSServiceCaller

public abstract class ServiceCaller
extends Object

Abstraction of a service caller for a TCP-based service. Service caller implementations can be used to perform a call to a service, and potentially fail-over to other back-ends if one is not available.

Descriptors

A service caller has a link to a Descriptor instance, which describes which back-ends to call. A Descriptor describes either a single back-end or a group of back-ends. A single back-end is represented by a TargetDescriptor instance, while a groups of back-ends is represented by a GroupDescriptor instance. Both are subclasses of class Descriptor.

There is only one type of target descriptor, but there are different types of group descriptor:

Note that group descriptors may contain other group descriptors.

Time-outs

Target descriptors support three kinds of time-out:

Load-balancing and fail-over

Service callers can help in evenly distributing processing across available resources. This load-balancing is achieved by using a group descriptor which iterates over the underlying descriptors in a random order.

Unlike load-balancing, fail-over allows the detection of a failure and the migration of the processing to a similar, redundant back-end. This can be achieved using any type of group descriptor (either ordered or random).

Not all calls should be retried on a different back-end. For example, if a call fails because the back-end indicates the request is considered incorrect, then it may be considered unappropriate to try other back-ends. The shouldFailOver method determines whether a failed call will be retried.

Consider the following hypothetical scenario. A company has two data centers, a primary site and a secondary backup site. The primary site has 3 back-ends running an eshop service, while the hot backup site has only 2 such back-ends. The back-ends at the primary site should always be preferred over the back-ends at the backup site. At each site, load should be evenly distributed among the available back-ends within that site.

Such a scenario can be converted to a descriptor configuration such as the following:

Now if the service caller performs a call, it will first randomly select one of Main1, Main2 and Main3. If the call fails and fail-over is considered allowable, it will retry the call with one of the other back-ends in the MainSite group. If none of the back-ends in the MainSite group succeeds, it will randomly select back-ends from the BackupSite group until the call has succeeded or until all back-ends were tried.

Call configuration

Some aspects of a call can be configured using a CallConfig object. For example, the CallConfig base class indicates whether fail-over is unconditionally allowed. Like this, some aspects of the behaviour of the caller can be tweaked.

There are different places where a CallConfig can be applied:

First of all, each ServiceCaller instance will have a fall-back CallConfig.

Secondly, a CallRequest instance may have a CallConfig associated with it as well. If it does, then this overrides the one on the ServiceCaller instance.

Finally, a CallConfig can be passed as an argument to the call method. If it is, then this overrides any other settings.

Subclass implementations

This class is abstract and is intended to be have service-specific subclasses, e.g. for HTTP, FTP, JDBC, etc.

Normally, a subclass should be stick to the following rules:

  1. There should be a constructor that accepts only a Descriptor object. This constructor should call super(descriptor, null). This descriptor should document the same exceptions as the ServiceCaller(Descriptor,CallConfig) constructor.
  2. There should be a constructor that accepts both a Descriptor and a service-specific call config object (derived from CallConfig). This constructor should call super(descriptor, callConfig). This descriptor should document the same exceptions as the ServiceCaller(Descriptor,CallConfig) constructor.
  3. The method isProtocolSupportedImpl(String) should be implemented.
  4. There should be a call method that accepts only a service-specific request object (derived from CallRequest). It should call doCall(request, null).
  5. There should be a call method that accepts both a service-specific request object (derived from CallRequest). and a service-specific call config object (derived from CallConfig). It should call doCall(request, callConfig).
  6. The method doCallImpl(CallRequest,CallConfig,TargetDescriptor) must be implemented as specified.
  7. The createCallResult method must be implemented as specified.
  8. To control when fail-over is applied, the method shouldFailOver(CallRequest,CallConfig,CallExceptionList) may also be implemented. The implementation can assume that the passed CallRequest object is an instance of the service-specific call request class and that the passed CallConfig object is an instance of the service-specific call config class.

Since:
XINS 1.0.0
Version:
$Revision: 1.79 $ $Date: 2010/03/20 13:35:58 $
Author:
Ernst de Haan

Constructor Summary
protected ServiceCaller(Descriptor descriptor, CallConfig callConfig)
          Constructs a new ServiceCaller with the specified CallConfig.
 
Method Summary
protected  void controlTimeOut(Runnable task, TargetDescriptor descriptor)
          Runs the specified task.
protected abstract  CallResult createCallResult(CallRequest request, TargetDescriptor succeededTarget, long duration, CallExceptionList exceptions, Object result)
          Constructs an appropriate CallResult object for a successful call attempt.
protected  CallResult doCall(CallRequest request, CallConfig callConfig)
          Attempts to execute the specified call request on one of the target services, with the specified call configuration.
abstract  Object doCallImpl(CallRequest request, CallConfig callConfig, TargetDescriptor target)
          Calls the specified target using the specified subject.
 CallConfig getCallConfig()
          Returns the CallConfig associated with this service caller.
protected abstract  CallConfig getDefaultCallConfig()
          Returns a default CallConfig object.
 Descriptor getDescriptor()
          Returns the descriptor.
 boolean isProtocolSupported(String protocol)
          Checks if the specified protocol is supported (wrapper method).
protected  boolean isProtocolSupportedImpl(String protocol)
          Checks if the specified protocol is supported (implementation method).
protected  void setCallConfig(CallConfig config)
          Sets the CallConfig associated with this service caller.
 void setDescriptor(Descriptor descriptor)
          Sets the descriptor.
protected  boolean shouldFailOver(CallRequest request, CallConfig callConfig, CallExceptionList exceptions)
          Determines whether a call should fail-over to the next selected target based on a request, call configuration and exception list.
 void testTargetDescriptor(TargetDescriptor target)
          Asserts that the specified target descriptor is considered acceptable for this service caller.
 
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
 

Constructor Detail

ServiceCaller

protected ServiceCaller(Descriptor descriptor,
                        CallConfig callConfig)
                 throws UnsupportedProtocolException
Constructs a new ServiceCaller with the specified CallConfig.

The descriptor is not mandatory. However, no calls can be made with this service caller until the descriptor is set.

Parameters:
descriptor - the descriptor of the service, or null.
callConfig - the CallConfig object, or null if the default should be used.
Throws:
UnsupportedProtocolException - if descriptor is or contains a TargetDescriptor with an unsupported protocol (since XINS 1.2.0).
Since:
XINS 1.1.0
Method Detail

testTargetDescriptor

public final void testTargetDescriptor(TargetDescriptor target)
                                throws IllegalArgumentException,
                                       UnsupportedProtocolException
Asserts that the specified target descriptor is considered acceptable for this service caller. If not, an exception is thrown.

Parameters:
target - the TargetDescriptor to test, should not be null.
Throws:
IllegalArgumentException - if target == null.
UnsupportedProtocolException - if the protocol in the target descriptor is unsupported.
Since:
XINS 1.2.0

isProtocolSupported

public final boolean isProtocolSupported(String protocol)
                                  throws IllegalArgumentException,
                                         UnsupportedOperationException
Checks if the specified protocol is supported (wrapper method). The protocol is the part in a URL before the string "://").

For example:

This method first checks the argument. If it is null, then an exception is thrown. Otherwise, the result of a call to isProtocolSupportedImpl(String) is returned, passing the supplied protocol, but in lowercase. This method may then throw an UnsupportedOperationException if it is not implemented (default behavior).

Parameters:
protocol - the protocol, should not be null.
Returns:
true if the specified protocol is supported, or false if it is not.
Throws:
IllegalArgumentException - if protocol == null.
UnsupportedOperationException - if this method is not implemented (probably because this ServiceCaller implementation was originally written with XINS 1.0.x or XINS 1.1.x)
Since:
XINS 1.2.0

isProtocolSupportedImpl

protected boolean isProtocolSupportedImpl(String protocol)
                                   throws UnsupportedOperationException
Checks if the specified protocol is supported (implementation method). The protocol is the part in a URL before the string "://").

This method should only ever be called from the isProtocolSupported(String) method.

The implementation of this method in class ServiceCaller throws an UnsupportedOperationException.

Parameters:
protocol - the protocol, guaranteed not to be null and guaranteed to be in lower case.
Returns:
true if the specified protocol is supported, or false if it is not.
Throws:
UnsupportedOperationException - if this method is not implemented (probably because this ServiceCaller implementation was originally written with XINS 1.0.x or XINS 1.1.x)
Since:
XINS 1.2.0

setDescriptor

public void setDescriptor(Descriptor descriptor)
                   throws UnsupportedProtocolException
Sets the descriptor.

Parameters:
descriptor - the descriptor for this service, or null.
Throws:
UnsupportedProtocolException - if descriptor is or contains a TargetDescriptor with an unsupported protocol.
Since:
XINS 1.2.0

getDescriptor

public final Descriptor getDescriptor()
Returns the descriptor. If the descriptor is currently unset, then null is returned.

Since XINS 1.2.0, this method may return null.

Returns:
the descriptor for this service, or null if it is currently unset.

setCallConfig

protected final void setCallConfig(CallConfig config)
                            throws IllegalArgumentException
Sets the CallConfig associated with this service caller.

This method should only be called on new-style (XINS 1.1) service callers that used the ServiceCaller(Descriptor,CallConfig) constructor.

Parameters:
config - the fall-back CallConfig object for this service caller, cannot be null.
Throws:
IllegalArgumentException - if config == null.
Since:
XINS 1.2.0

getCallConfig

public final CallConfig getCallConfig()
Returns the CallConfig associated with this service caller.

Returns:
the fall-back CallConfig object for this service caller, never null.
Since:
XINS 1.1.0

getDefaultCallConfig

protected abstract CallConfig getDefaultCallConfig()
Returns a default CallConfig object. This method is called by the ServiceCaller constructor if no CallConfig object was given.

Subclasses that support the new service calling framework (introduced in XINS 1.1.0) must override this method to return a more suitable CallConfig instance.

This method should never be called by subclasses.

Returns:
a new, appropriate, CallConfig instance, never null.
Since:
XINS 1.1.0

doCall

protected final CallResult doCall(CallRequest request,
                                  CallConfig callConfig)
                           throws IllegalArgumentException,
                                  IllegalStateException,
                                  CallException
Attempts to execute the specified call request on one of the target services, with the specified call configuration. During the execution, Target descriptors will be picked and passed to doCallImpl(CallRequest,CallConfig,TargetDescriptor) until there is one that succeeds, as long as fail-over can be done (according to shouldFailOver(CallRequest,CallConfig,CallExceptionList)).

If one of the calls succeeds, then the result is returned. If none succeeds or if fail-over should not be done, then a CallException is thrown.

Subclasses that want to use this method must implement doCallImpl(CallRequest,CallConfig,TargetDescriptor). That method is called for each call attempt to a specific service target (represented by a TargetDescriptor).

Parameters:
request - the call request, not null.
callConfig - the call configuration, or null if the one defined for the call request should be used if specified, or otherwise the fall-back call configuration associated with this ServiceCaller (see getCallConfig()).
Returns:
a combination of the call result and a link to the target that returned this result, if and only if one of the calls succeeded, could be null.
Throws:
IllegalArgumentException - if request == null.
IllegalStateException - if the descriptor is currently unset (since XINS 1.2.0).
CallException - if all call attempts failed.
Since:
XINS 1.1.0

doCallImpl

public abstract Object doCallImpl(CallRequest request,
                                  CallConfig callConfig,
                                  TargetDescriptor target)
                           throws ClassCastException,
                                  IllegalArgumentException,
                                  CallException
Calls the specified target using the specified subject. This method must be implemented by subclasses. It is called as soon as a target is selected to be called. If the call fails, then a CallException should be thrown. If the call succeeds, then the call result should be returned from this method.

Subclasses that want to use doCall(CallRequest,CallConfig) must implement this method.

Parameters:
request - the call request to be executed, never null.
callConfig - the call config to be used, never null; this is determined by doCall(CallRequest,CallConfig) and is guaranteed not to be null.
target - the target to call, cannot be null.
Returns:
the result, if and only if the call succeeded, could be null.
Throws:
ClassCastException - if the specified request object is not null and not an instance of an expected subclass of class CallRequest.
IllegalArgumentException - if target == null || request == null.
CallException - if the call to the specified target failed.
Since:
XINS 1.1.0

createCallResult

protected abstract CallResult createCallResult(CallRequest request,
                                               TargetDescriptor succeededTarget,
                                               long duration,
                                               CallExceptionList exceptions,
                                               Object result)
                                        throws ClassCastException
Constructs an appropriate CallResult object for a successful call attempt. This method is called from doCall(CallRequest,CallConfig).

Parameters:
request - the CallRequest that was to be executed, never null when called from doCall(CallRequest,CallConfig).
succeededTarget - the TargetDescriptor for the service that was successfully called, never null when called from doCall(CallRequest,CallConfig).
duration - the call duration in milliseconds, guaranteed to be a non-negative number when called from doCall(CallRequest,CallConfig).
exceptions - the list of CallException instances, or null if there were no call failures.
result - the result from the call, which is the object returned by doCallImpl(CallRequest,CallConfig,TargetDescriptor), can be null.
Returns:
a CallResult instance, never null.
Throws:
ClassCastException - if request and/or result are not of the correct class.

controlTimeOut

protected final void controlTimeOut(Runnable task,
                                    TargetDescriptor descriptor)
                             throws IllegalArgumentException,
                                    IllegalThreadStateException,
                                    SecurityException,
                                    TimeOutException
Runs the specified task. If the task does not finish within the total time-out period, then the thread executing it is interrupted using the Thread.interrupt() method and a TimeOutException is thrown.

Parameters:
task - the task to run, cannot be null.
descriptor - the descriptor for the target on which the task is executed, cannot be null.
Throws:
IllegalArgumentException - if task == null || descriptor == null.
IllegalThreadStateException - if descriptor.getTotalTimeOut() > 0 and the task is a Thread which is already started.
SecurityException - if the task did not finish within the total time-out period, but the interruption of the thread was disallowed (see Thread.interrupt()).
TimeOutException - if the task did not finish within the total time-out period and was interrupted.

shouldFailOver

protected boolean shouldFailOver(CallRequest request,
                                 CallConfig callConfig,
                                 CallExceptionList exceptions)
Determines whether a call should fail-over to the next selected target based on a request, call configuration and exception list. This method should only be called from doCall(CallRequest,CallConfig).

This method is typically overridden by subclasses. Usually, a subclass first calls this method in the superclass, and if that returns false it does some additional checks, otherwise true is immediately returned.

The implementation of this method in class ServiceCaller returns true if and only if at least one of the following conditions is true:

Parameters:
request - the request for the call, as passed to doCall(CallRequest,CallConfig), should not be null.
callConfig - the call config that is currently in use, never null.
exceptions - the current list of CallExceptions; never null; get the most recent one by calling exceptions.last().
Returns:
true if the call should fail-over to the next target, or false if it should not.
Since:
XINS 1.1.0


See http://www.xins.org/.