org.xins.client.XINSCallRequest Maven / Gradle / Ivy
/*
* $Id: XINSCallRequest.java,v 1.50 2006/08/28 09:12:31 agoubard Exp $
*
* Copyright 2003-2006 Orange Nederland Breedband B.V.
* See the COPYRIGHT file for redistribution and use restrictions.
*/
package org.xins.client;
import java.util.Iterator;
import org.apache.log4j.NDC;
import org.apache.oro.text.regex.MalformedPatternException;
import org.apache.oro.text.regex.Pattern;
import org.apache.oro.text.regex.Perl5Compiler;
import org.apache.oro.text.regex.Perl5Matcher;
import org.xins.common.MandatoryArgumentChecker;
import org.xins.common.Utils;
import org.xins.common.xml.Element;
import org.xins.common.xml.ElementSerializer;
import org.xins.common.collections.PropertyReader;
import org.xins.common.collections.PropertyReaderUtils;
import org.xins.common.collections.ProtectedPropertyReader;
import org.xins.common.http.HTTPCallConfig;
import org.xins.common.http.HTTPCallRequest;
import org.xins.common.http.HTTPMethod;
import org.xins.common.service.CallRequest;
import org.xins.common.text.FastStringBuffer;
import org.xins.common.text.TextUtils;
/**
* Abstraction of a XINS request.
*
* Note that instances of this class are not thread-safe.
*
* @version $Revision: 1.50 $ $Date: 2006/08/28 09:12:31 $
* @author Ernst de Haan
*
* @since XINS 1.0.0
*
* @see XINSServiceCaller
*/
public final class XINSCallRequest extends CallRequest {
//-------------------------------------------------------------------------
// Class fields
//-------------------------------------------------------------------------
/**
* Fully-qualified name of this class.
*/
private static final String CLASSNAME = XINSCallRequest.class.getName();
/**
* HTTP status code verifier that will only approve 2xx codes.
*/
private static final HTTPStatusCodeVerifier HTTP_STATUS_CODE_VERIFIER = new HTTPStatusCodeVerifier();
/**
* Perl 5 pattern compiler.
*/
private static final Perl5Compiler PATTERN_COMPILER = new Perl5Compiler();
/**
* The pattern for a parameter name, as a character string.
*/
public static final String PARAMETER_NAME_PATTERN_STRING = "[a-zA-Z][a-zA-Z0-9_\\-\\.]*";
/**
* The pattern for a parameter name.
*/
private static final Pattern PARAMETER_NAME_PATTERN;
/**
* The name of the HTTP parameter that specifies the diagnostic context
* identifier.
*/
private static final String CONTEXT_ID_HTTP_PARAMETER_NAME = "_context";
/**
* The number of instances of this class. Initially zero.
*/
private static int INSTANCE_COUNT;
/**
* Secret key used to set the HTTP parameters.
*/
private static final Object SECRET_KEY = new Object();
//-------------------------------------------------------------------------
// Class functions
//-------------------------------------------------------------------------
/**
* Initializes this class. This function compiles
* {@link #PARAMETER_NAME_PATTERN_STRING} to a {@link Pattern} and then
* stores that in {@link #PARAMETER_NAME_PATTERN}.
*/
static {
final String THIS_METHOD = "()";
try {
PARAMETER_NAME_PATTERN = PATTERN_COMPILER.compile(PARAMETER_NAME_PATTERN_STRING, Perl5Compiler.READ_ONLY_MASK);
} catch (MalformedPatternException mpe) {
final String DETAIL = "The pattern \""
+ PARAMETER_NAME_PATTERN_STRING
+ "\" is malformed.";
throw Utils.logProgrammingError(
CLASSNAME, THIS_METHOD,
CLASSNAME, THIS_METHOD,
DETAIL, mpe);
}
}
//-------------------------------------------------------------------------
// Constructors
//-------------------------------------------------------------------------
/**
* Constructs a new XINSCallRequest
for the specified function
* with no parameters, disallowing fail-over unless the request was
* definitely not (yet) accepted by the service.
*
* @param functionName
* the name of the function to call, cannot be null
.
*
* @throws IllegalArgumentException
* if functionName == null
.
*/
public XINSCallRequest(String functionName)
throws IllegalArgumentException {
this(functionName, null, null);
}
/**
* Constructs a new XINSCallRequest
for the specified function
* and parameters, disallowing fail-over unless the request was definitely
* not (yet) accepted by the service.
*
* @param functionName
* the name of the function to call, cannot be null
.
*
* @param parameters
* the input parameters, if any, can be null
if there are
* none.
*
* @throws IllegalArgumentException
* if functionName == null
.
*/
public XINSCallRequest(String functionName, PropertyReader parameters)
throws IllegalArgumentException {
this(functionName, parameters, null);
}
/**
* Constructs a new XINSCallRequest
for the specified function
* and parameters, disallowing fail-over unless the request was definitely
* not (yet) accepted by the service.
*
* @param functionName
* the name of the function to call, cannot be null
.
*
* @param parameters
* the input parameters, if any, can be null
if there are
* none.
*
* @param dataSection
* the data section for the input, if any, can be null
if
* there are none.
*
* @throws IllegalArgumentException
* if functionName == null
.
*
* @since XINS 1.1.0
*/
public XINSCallRequest(String functionName,
PropertyReader parameters,
Element dataSection)
throws IllegalArgumentException {
// Determine instance number first
_instanceNumber = ++INSTANCE_COUNT;
// Check preconditions
MandatoryArgumentChecker.check("functionName", functionName);
// Store function name, parameters and data section
_functionName = functionName;
_parameters = new ProtectedPropertyReader(SECRET_KEY);
_httpParams = new ProtectedPropertyReader(SECRET_KEY);
setParameters(parameters);
setDataSection(dataSection);
// Note that _asString is lazily initialized.
}
/**
* Constructs a new XINSCallRequest
for the specified function
* and parameters, possibly allowing fail-over even if the request was
* possibly already received by a target service.
*
* @param functionName
* the name of the function to call, cannot be null
.
*
* @param parameters
* the input parameters, if any, can be null
if there are
* none.
*
* @param failOverAllowed
* flag that indicates whether fail-over is in principle allowed, even
* if the request was already sent to the other end.
*
* @throws IllegalArgumentException
* if functionName == null
.
*
* @deprecated
* Deprecated since XINS 1.1.0.
* Use {@link #XINSCallRequest(String,PropertyReader)} in combination
* with {@link #setXINSCallConfig(XINSCallConfig)} instead.
* This constructor is guaranteed not to be removed before XINS 2.0.0.
*/
public XINSCallRequest(String functionName,
PropertyReader parameters,
boolean failOverAllowed)
throws IllegalArgumentException {
this(functionName, parameters, failOverAllowed, null);
}
/**
* Constructs a new XINSCallRequest
for the specified function
* and parameters, possibly allowing fail-over, optionally specifying the
* HTTP method to use.
*
* @param functionName
* the name of the function to call, cannot be null
.
*
* @param parameters
* the input parameters, if any, can be null
if there are
* none.
*
* @param failOverAllowed
* flag that indicates whether fail-over is in principle allowed, even
* if the request was already sent to the other end.
*
* @param method
* the HTTP method to use, or null
if a default should be
* used.
*
* @throws IllegalArgumentException
* if functionName == null
or if parameters
* contains a name that does not match the constraints for a parameter
* name, see {@link #PARAMETER_NAME_PATTERN_STRING} or if it equals
* "function"
, which is currently still reserved.
*
* @deprecated
* Deprecated since XINS 1.1.0.
* Use {@link #XINSCallRequest(String,PropertyReader)} in combination
* with {@link #setXINSCallConfig(XINSCallConfig)} instead.
* This constructor is guaranteed not to be removed before XINS 2.0.0.
*/
public XINSCallRequest(String functionName,
PropertyReader parameters,
boolean failOverAllowed,
HTTPMethod method)
throws IllegalArgumentException {
this(functionName, parameters);
// Create an associated XINSCallConfig object
XINSCallConfig callConfig = new XINSCallConfig();
// Configure fail-over
callConfig.setFailOverAllowed(failOverAllowed);
// Configure the HTTP method
if (method != null) {
callConfig.setHTTPMethod(method);
}
// Apply the configuration
setXINSCallConfig(callConfig);
}
//-------------------------------------------------------------------------
// Fields
//-------------------------------------------------------------------------
/**
* The 1-based sequence number of this instance. Since this number is
* 1-based, the first instance of this class will have instance number 1
* assigned to it.
*/
private final int _instanceNumber;
/**
* Description of this XINS call request. This field cannot be
* null
, it is initialized during construction.
*/
private String _asString;
/**
* The name of the function to call. This field cannot be
* null
.
*/
private final String _functionName;
/**
* The parameters to pass in the request, and their respective values. This
* field can be null
.
*/
private final ProtectedPropertyReader _parameters;
/**
* The data section to pass in the request. This field can be
* null
.
*/
private Element _dataSection;
/**
* The parameters to send with the HTTP request. Cannot be
* null
.
*/
private final ProtectedPropertyReader _httpParams;
/**
* Pattern matcher.
*/
private final Perl5Matcher _patternMatcher = new Perl5Matcher();
//-------------------------------------------------------------------------
// Methods
//-------------------------------------------------------------------------
/**
* Describes this request.
*
* @return
* the description of this request, never null
.
*/
public String describe() {
// Lazily initialize the description of this call request object
if (_asString == null) {
FastStringBuffer buffer = new FastStringBuffer(208, "XINS HTTP request #");
// Request number
buffer.append(_instanceNumber);
// HTTP method
buffer.append(" [config=");
buffer.append(TextUtils.quote(getCallConfig()));
// Function name
buffer.append("; function=\"");
buffer.append(_functionName);
// Parameters
if (_parameters == null || _parameters.size() < 1) {
buffer.append("\"; parameters=(null); contextID=");
} else {
buffer.append("\"; parameters=\"");
PropertyReaderUtils.serialize(_parameters, buffer, "-");
buffer.append("\"; contextID=");
}
// Diagnostic context identifier
String contextID = _httpParams.get(CONTEXT_ID_HTTP_PARAMETER_NAME);
if (contextID == null || contextID.length() < 1) {
buffer.append("(null)]");
} else {
buffer.append('"');
buffer.append(contextID);
buffer.append("\"]");
}
_asString = buffer.toString();
}
return _asString;
}
/**
* Returns the XINS call configuration.
*
* @return
* the XINS call configuration object, or null
.
*
* @since XINS 1.1.0
*/
public XINSCallConfig getXINSCallConfig() {
return (XINSCallConfig) getCallConfig();
}
/**
* Sets the associated XINS call configuration.
*
* @param callConfig
* the XINS call configuration object to associate with this request, or
* null
.
*
* @since XINS 1.1.0
*/
public void setXINSCallConfig(XINSCallConfig callConfig) {
setCallConfig(callConfig);
}
/**
* Returns the name of the function to call.
*
* @return
* the name of the function to call, never null
.
*/
public String getFunctionName() {
return _functionName;
}
/**
* Initializes the set of parameters. The implementation of this method
* first removes all parameters and then adds the standard parameters.
*/
private void initParameters() {
// Remove all existing parameters
_parameters.clear(SECRET_KEY);
_httpParams.clear(SECRET_KEY);
// Since XINS 1.0.1: Use XINS 1.0 standard calling convention
_httpParams.set(SECRET_KEY, "_convention", "_xins-std");
// TODO: Get convention parameter name from a class in XINS/Java Common Library
// TODO: Get convention name from a class in XINS/Java Common Library
// Add the diagnostic context ID to the parameter list, if there is one
String contextID = NDC.peek();
if (contextID != null) {
_httpParams.set(SECRET_KEY, CONTEXT_ID_HTTP_PARAMETER_NAME, contextID);
}
// Add the function to the parameter list
_httpParams.set(SECRET_KEY, "_function", _functionName);
// XXX: For backwards compatibility, also add the parameter "function"
// to the list of HTTP parameters. This is, however, very likely to
// change in the future.
_httpParams.set(SECRET_KEY, "function", _functionName);
// Reset _asString so it will be re-initialized as necessary
_asString = null;
}
/**
* Sets the parameters for this function, replacing any existing
* parameters. First the existing parameters are cleaned and then all
* the specified parameters are copied to the internal set one-by-one. If
* any of the parameters has an invalid name, then the internal parameter
* set is cleaned and then an exception is thrown.
*
* @param parameters
* the input parameters, if any, can be null
if there are
* none.
*
* @throws IllegalArgumentException
* if parameters
contains a name that does not match the
* constraints for a parameter name, see
* {@link #PARAMETER_NAME_PATTERN_STRING} or if it equals
* "function"
, which is currently still reserved.
*
* @since XINS 1.1.0
*/
public void setParameters(PropertyReader parameters)
throws IllegalArgumentException {
// Clear the parameters
initParameters();
// Check and copy all parameters
if (parameters != null) {
Iterator names = parameters.getNames();
while (names.hasNext()) {
// Get the name and value
String name = (String) names.next();
String value = parameters.get(name);
// Set the combination (this may fail)
setParameter(name, value);
}
}
// Add the function to the parameter list
_httpParams.set(SECRET_KEY, "_function", _functionName);
// XXX: For backwards compatibility, also add the parameter "function"
// to the list of HTTP parameters. This is, however, very likely to
// change in the future.
_httpParams.set(SECRET_KEY, "function", _functionName);
// Reset _asString so it will be re-initialized as necessary
_asString = null;
}
/**
* Sets the parameter with the specified name.
*
* @param name
* the parameter name, cannot be null
.
*
* @param value
* the new value for the parameter, can be null
.
*
* @throws IllegalArgumentException
* if name
does not match the constraints for a parameter
* name, see {@link #PARAMETER_NAME_PATTERN_STRING} or if it equals
* "function"
, which is currently still reserved.
*
* @since XINS 1.2.0
*/
public void setParameter(String name, String value)
throws IllegalArgumentException {
// Check preconditions
MandatoryArgumentChecker.check("name", name);
// Name cannot violate the pattern
if (! _patternMatcher.matches(name, PARAMETER_NAME_PATTERN)) {
// XXX: Consider using a different kind of exception for this
// specific case. For backwards compatibility, this exception
// class must be converted to an IllegalArgumentException in
// some cases or otherwise it should subclass
// IllegalArgumentException.
FastStringBuffer buffer = new FastStringBuffer(121, "The parameter name \"");
buffer.append(name);
buffer.append("\" does not match the pattern \"");
buffer.append(PARAMETER_NAME_PATTERN_STRING);
buffer.append("\".");
throw new IllegalArgumentException(buffer.toString());
// Name cannot be "function"
} else if ("function".equals(name)) {
throw new IllegalArgumentException("Parameter name \"function\" is reserved.");
// Name is considered valid, store it
} else {
_parameters.set(SECRET_KEY, name, value);
_httpParams.set(SECRET_KEY, name, value);
}
}
/**
* Gets all parameters to pass with the call, with their respective values.
*
* @return
* the parameters, or null
if there are none.
*/
public PropertyReader getParameters() {
return _parameters;
}
/**
* Gets the value of the specified parameter.
*
* @param name
* the parameter name, not null
.
*
* @return
* string containing the value of the parameter, not null
.
*
* @throws IllegalArgumentException
* if name == null
.
*/
public String getParameter(String name)
throws IllegalArgumentException {
// Check preconditions
MandatoryArgumentChecker.check("name", name);
return (_parameters == null) ? null : _parameters.get(name);
}
/**
* Sets the data section for the input.
*
* @param dataSection
* the data section for the input, or null
if there is
* none.
*
* @since XINS 1.1.0
*/
public void setDataSection(Element dataSection) {
// Store the data section
_dataSection = dataSection;
// Add the data section to the HTTP parameter list
if (dataSection == null) {
_httpParams.set(SECRET_KEY, "_data", null);
} else {
// TODO: Do not recreate ElementSerializer each time
ElementSerializer serializer = new ElementSerializer();
String xmlDataSection = serializer.serialize(dataSection);
_httpParams.set(SECRET_KEY, "_data", xmlDataSection);
}
}
/**
* Retrieves the data section for the input.
*
* @return
* the data section for the input, or null
if there is
* none.
*
* @since XINS 1.1.0
*/
public Element getDataSection() {
return _dataSection;
}
/**
* Determines whether fail-over is unconditionally allowed.
*
* @return
* true
if fail-over is unconditionally allowed, even if the
* request was already received or even processed by the other end,
* false
otherwise.
*
* @deprecated
* Deprecated since XINS 1.1.0.
* Call {@link #getXINSCallConfig()} instead and then call
* {@link XINSCallConfig#isFailOverAllowed() isFailOverAllowed()} on the
* returned call configuration object.
* This method is guaranteed not to be removed before XINS 2.0.0.
*/
public boolean isFailOverAllowed() {
XINSCallConfig callConfig = getXINSCallConfig();
if (callConfig == null) {
return false;
} else {
return getXINSCallConfig().isFailOverAllowed();
}
}
/**
* Returns an HTTPCallRequest
that can be used to execute this
* XINS request.
*
* @return
* this request converted to an {@link HTTPCallRequest}, never
* null
.
*/
HTTPCallRequest getHTTPCallRequest() {
// Construct an HTTP call request
HTTPCallRequest httpRequest = new HTTPCallRequest(_httpParams,
HTTP_STATUS_CODE_VERIFIER);
// If there is a XINS call config, create an HTTP call config
XINSCallConfig xinsConfig = getXINSCallConfig();
if (xinsConfig != null) {
HTTPCallConfig httpConfig = new HTTPCallConfig();
httpConfig.setFailOverAllowed(xinsConfig.isFailOverAllowed());
httpConfig.setMethod(xinsConfig.getHTTPMethod());
httpRequest.setHTTPCallConfig(httpConfig);
}
return httpRequest;
}
//-------------------------------------------------------------------------
// Inner classes
//-------------------------------------------------------------------------
/**
* HTTP status code verifier that will only approve 2xx codes.
*
* @version $Revision: 1.50 $ $Date: 2006/08/28 09:12:31 $
* @author Ernst de Haan
*
* @since XINS 1.0.0
*/
private static final class HTTPStatusCodeVerifier
extends Object
implements org.xins.common.http.HTTPStatusCodeVerifier {
//----------------------------------------------------------------------
// Constructors
//----------------------------------------------------------------------
/**
* Constructs a new HTTPStatusCodeVerifier
.
*/
private HTTPStatusCodeVerifier() {
// empty
}
//----------------------------------------------------------------------
// Fields
//----------------------------------------------------------------------
//----------------------------------------------------------------------
// Methods
//----------------------------------------------------------------------
/**
* Checks if the specified HTTP status code is considered acceptable or
* unacceptable.
*
* The implementation of this method in class
* {@link XINSCallRequest.HTTPStatusCodeVerifier} returns
* true
only for 2xx status codes.
*
* @param code
* the HTTP status code to check.
*
* @return
* true
if code >= 200 && code <=
* 299
.
*/
public boolean isAcceptable(int code) {
return (code >= 200) && (code <= 299);
}
}
}