org.xins.client.AbstractCAPI Maven / Gradle / Ivy
/*
* $Id: AbstractCAPI.java,v 1.82 2012/03/03 21:23:44 agoubard Exp $
*
* See the COPYRIGHT file for redistribution and use restrictions.
*/
package org.xins.client;
import java.net.MalformedURLException;
import java.net.URL;
import java.security.CodeSource;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import org.xins.common.MandatoryArgumentChecker;
import org.xins.common.collections.InvalidPropertyValueException;
import org.xins.common.collections.MissingRequiredPropertyException;
import org.xins.common.http.HTTPCallException;
import org.xins.common.service.Descriptor;
import org.xins.common.service.DescriptorBuilder;
import org.xins.common.service.GenericCallException;
import org.xins.common.service.TargetDescriptor;
import org.xins.common.service.UnsupportedProtocolException;
import org.xins.common.spec.APISpec;
import org.xins.common.spec.InvalidSpecificationException;
import org.xins.common.text.TextUtils;
/**
* Base class for generated Client-side Application Programming Interface
* (CAPI) classes.
*
* This class should not be derived from manually. This class is only
* intended to be used as a superclass of CAPI
classes generated
* by the XINS framework.
*
*
The constructors of this class are considered internal to XINS and
* should not be used directly. The behavior of the constructors may be
* changed in later releases of XINS or they may even be removed.
*
* @version $Revision: 1.82 $ $Date: 2012/03/03 21:23:44 $
* @author Ernst de Haan
* @author Anthony Goubard
*
* @since XINS 1.0.0
*/
public abstract class AbstractCAPI {
/**
* Set of all CAPI classes for which the XINS version at build-time has
* already been checked against the XINS version at run-time. Never
* null
.
*/
private static final Set> VERSION_COMPARISIONS_DONE = new HashSet>();
/**
* The name of the API. This field cannot be null
.
*/
private final String _apiName;
/**
* The XINS service caller to use. This field cannot be null
.
*/
private final XINSServiceCaller _caller;
/**
* The API specification. This field is lazily initialized by
* {@link #getAPISpecification()}.
*/
private APISpec _apiSpecification;
/**
* Creates a new AbstractCAPI
object, using the specified
* XINSServiceCaller
.
*
* This constructor is considered internal to XINS. Do not use it
* directly.
*
* @param descriptor
* the descriptor for the service(s), cannot be null
.
*
* @param callConfig
* fallback configuration for the calls, or null
if a
* default should be used.
*
* @throws IllegalArgumentException
* if descriptor == null
.
*
* @throws UnsupportedProtocolException
* if any of the target descriptors in descriptor
specifies
* an unsupported protocol.
*
* @since XINS 1.1.0
*/
protected AbstractCAPI(Descriptor descriptor, XINSCallConfig callConfig)
throws IllegalArgumentException, UnsupportedProtocolException {
// Check preconditions
MandatoryArgumentChecker.check("descriptor", descriptor);
// Create and store service caller
_caller = new XINSServiceCaller(descriptor, callConfig);
_caller.setCAPI(this);
// Determine the API name
_apiName = determineAPIName();
// Compare the XINS version at build- and run-time
checkXINSVersion();
}
/**
* Creates a new AbstractCAPI
object, using the specified
* service descriptor.
*
*
A default XINS call configuration will be used.
*
*
This constructor is considered internal to XINS. Do not use it
* directly.
*
* @param descriptor
* the descriptor for the service(s), cannot be null
.
*
* @throws IllegalArgumentException
* if descriptor == null
.
*
* @throws UnsupportedProtocolException
* if any of the target descriptors in descriptor
specifies
* an unsupported protocol (since XINS 1.1.0).
*/
protected AbstractCAPI(Descriptor descriptor)
throws IllegalArgumentException, UnsupportedProtocolException {
this(descriptor, null);
}
/**
* Creates a new AbstractCAPI
object based on the specified
* set of properties and the specified name.
*
*
A default XINS call configuration will be used.
*
*
This constructor is considered internal to XINS. Do not use it
* directly.
*
* @param properties
* the properties to read from, cannot be null
.
*
* @param apiName
* the name of the API, cannot be null
and must be a valid
* API name.
*
* @throws IllegalArgumentException
* if properties == null || apiName == null
or if
* apiName
is not considered to be a valid API name.
*
* @throws MissingRequiredPropertyException
* if a required property is missing in the specified properties set.
*
* @throws InvalidPropertyValueException
* if one of the properties in the specified properties set is used to
* create a CAPI
instance but its value is considered
* invalid.
*
* @since XINS 1.2.0
*/
protected AbstractCAPI(Map properties, String apiName)
throws IllegalArgumentException,
MissingRequiredPropertyException,
InvalidPropertyValueException {
// Check arguments
MandatoryArgumentChecker.check("properties", properties,
"apiName", apiName);
// Determine property name
String propName = "capis." + apiName;
// Construct a XINS caller object
_caller = new XINSServiceCaller();
// Build a descriptor from the properties
Descriptor descriptor = DescriptorBuilder.build(_caller,
properties,
propName);
// Associate caller with descriptor
_caller.setDescriptor(descriptor);
// Associate caller with this CAPI object
_caller.setCAPI(this);
// Determine the API name
_apiName = determineAPIName();
// Compare the XINS version at build- and run-time
checkXINSVersion();
}
/**
* Retrieves the name of the API (wrapper method).
*
* @return
* the name of the API, or null
if the name cannot be
* determined.
*
* @since XINS 1.2.0
*/
private String determineAPIName() {
String apiName = getAPINameImpl();
if (! TextUtils.isEmpty(apiName)) {
return apiName;
}
// Subclass did not return anything, determine based on package name
String className = getClass().getName();
int index = className.lastIndexOf(".capi.");
if (index > 0) {
String s = className.substring(0, index);
index = s.lastIndexOf('.');
s = s.substring(index + 1);
if (! TextUtils.isEmpty(s)) {
return s;
}
}
return null;
}
/**
* Determines the name of the API.
*
* @return
* the name of the API, or a special indication (e.g.
* "<unknown>"
) if the name cannot be determined;
* never null
.
*
* @since XINS 1.2.0
*/
public final String getAPIName() {
if (_apiName == null) {
return "";
} else {
return _apiName;
}
}
/**
* Returns true
of the error code is a functional error code.
* If unknown, false is returned.
*
* @param errorCode
* the error code to check, cannot be null
.
*
* @return
* true
if the error code is functional, false
* if the error code is technical.
*/
protected boolean isFunctionalError(String errorCode) {
return false;
}
/**
* Retrieves the name of the API (implementation method).
*
* The implementation of this method in class AbstractCAPI
* returns null
.
*
* @return
* the name of the API, or null
if unknown.
*
* @since XINS 1.2.0
*/
protected String getAPINameImpl() {
// NOTE: This method is not abstract, since that would make this class
// incompatible with CAPI classes generated with older versions of
// XINS (before 1.2.0)
return null;
}
/**
* Get the specification of the API.
*
* @return
* the {@link APISpec} specification object.
*
* @throws InvalidSpecificationException
* if the specification cannot be found or is invalid.
*
* @since XINS 1.3.0
*/
public final APISpec getAPISpecification()
throws InvalidSpecificationException {
// Lazily initialize _apiSpecification
if (_apiSpecification == null) {
URL specsURL;
CodeSource source = getClass().getProtectionDomain().getCodeSource();
if (source != null) {
URL sourceURL = source.getLocation();
try {
if (sourceURL.getPath().endsWith(".jar")) {
specsURL = new URL("jar:" + sourceURL.toExternalForm() + "!/specs/");
} else {
specsURL = new URL(sourceURL.toExternalForm() + "/specs/");
}
} catch (MalformedURLException murlex) {
Log.log_2116(murlex, getAPIName());
specsURL = getClass().getResource("/specs/");
}
} else {
specsURL = getClass().getResource("/specs/");
}
_apiSpecification = new APISpec(getClass(), specsURL.toExternalForm());
}
return _apiSpecification;
}
/**
* Assigns the specified call configuration to this CAPI object.
*
* @param config
* the call configuration to apply when executing a call with this CAPI
* object, or null
if no specific call configuration should be
* associated with CAPI object; note that the call configuration can be
* overridden by the request, see
* {@link AbstractCAPICallRequest#configure(XINSCallConfig)}.
*
* @since XINS 1.2.0
*/
public final void setXINSCallConfig(XINSCallConfig config) {
_caller.setXINSCallConfig(config);
}
/**
* Retrieves the call configuration currently associated with this CAPI
* object.
*
* @return
* the call configuration currently associated with this CAPI object, or
* null
if no specific call configuration is associated
* with this cAPI object; note that the call configuration can be
* overridden by the request, see
* {@link AbstractCAPICallRequest#configuration()}.
*
* @since XINS 1.2.0
*/
public final XINSCallConfig getXINSCallConfig() {
return _caller.getXINSCallConfig();
}
/**
* Returns the XINS service caller to use.
*
*
This method is considered internal to XINS. It should not be
* called directly, nor overridden.
*
*
This method is expected to be marked final
in XINS
* 2.0. This is not done yet to remain fully compatible with XINS 1.x.
*
* @return
* the {@link XINSServiceCaller} to use, never null
.
*/
protected final XINSServiceCaller getCaller() {
return _caller;
}
/**
* Checks if the XINS version used to build this CAPI class equals the
* current XINS version. If not, a warning is logged.
*/
private void checkXINSVersion() {
Class extends AbstractCAPI> clazz = getClass();
if (! VERSION_COMPARISIONS_DONE.contains(clazz)) {
// Compare build- and run-time version of XINS
String buildVersion = getXINSVersion();
String runtimeVersion = Library.getVersion();
if (! buildVersion.equals(runtimeVersion)) {
Log.log_2114(_apiName, buildVersion, runtimeVersion);
}
// Never check this CAPI class again
VERSION_COMPARISIONS_DONE.add(clazz);
}
}
/**
* Returns the version of XINS used to build this CAPI class.
*
* @return
* the version as a {@link String}, cannot be null
.
*/
public abstract String getXINSVersion();
/**
* Executes the specified call request.
*
*
This method is provided for CAPI subclasses.
*
* @param request
* the call request to execute, cannot be null
.
*
* @return
* the result, not null
.
*
* @throws IllegalArgumentException
* if request == null
.
*
* @throws UnacceptableRequestException
* if the request is considered to be unacceptable; this is determined
* by calling
* request.
{@link AbstractCAPICallRequest#checkParameters() checkParameters()}.
*
* @throws GenericCallException
* if the first call attempt failed due to a generic reason and all the
* other call attempts (if any) failed as well.
*
* @throws HTTPCallException
* if the first call attempt failed due to an HTTP-related reason and
* all the other call attempts (if any) failed as well.
*
* @throws XINSCallException
* if the first call attempt failed due to a XINS-related reason and
* all the other call attempts (if any) failed as well.
*
* @since XINS 1.2.0
*/
protected final XINSCallResult callImpl(AbstractCAPICallRequest request)
throws IllegalArgumentException,
UnacceptableRequestException,
GenericCallException,
HTTPCallException,
XINSCallException {
// Check preconditions
MandatoryArgumentChecker.check("request", request);
// Check whether request is acceptable
UnacceptableRequestException unacceptable = request.checkParameters();
if (unacceptable != null) {
throw unacceptable;
}
// Execute the call request
return _caller.call(request.xinsCallRequest());
}
/**
* Creates an AbstractCAPIErrorCodeException
for the specified
* error code. If the specified error code is not recognized, then
* null
is returned.
*
* @param request
* the original request, should not be null
.
*
* @param target
* descriptor for the target that was attempted to be called, should not
* be null
.
*
* @param duration
* the call duration in milliseconds, should be >= 0.
*
* @param resultData
* the result data, should not be null
and should have an
* error code set.
*
* @return
* if the error code is recognized, then a matching
* {@link AbstractCAPIErrorCodeException} instance, otherwise
* null
.
*
* @throws IllegalArgumentException
* if request == null
* || target == null
* || duration < 0
* || resultData == null
* || resultData.getErrorCode() == null
.
*
* @throws UnacceptableErrorCodeXINSCallException
* if the specified error code is recognized but is considered
* unacceptable for the function specified in the request.
*
* @since XINS 1.2.0
*/
protected AbstractCAPIErrorCodeException
createErrorCodeException(XINSCallRequest request,
TargetDescriptor target,
long duration,
XINSCallResultData resultData)
throws IllegalArgumentException,
UnacceptableErrorCodeXINSCallException {
// By default return nothing
return null;
}
}