org.xins.client.FileServiceCaller Maven / Gradle / Ivy
/*
* $Id: FileServiceCaller.java,v 1.17 2007/05/29 12:22:30 agoubard Exp $
*
* Copyright 2003-2007 Orange Nederland Breedband B.V.
* See the COPYRIGHT file for redistribution and use restrictions.
*/
package org.xins.client;
import java.io.File;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.HashMap;
import java.util.Iterator;
import javax.servlet.ServletException;
import org.xins.common.FormattedParameters;
import org.xins.common.MandatoryArgumentChecker;
import org.xins.common.Utils;
import org.xins.common.collections.PropertyReader;
import org.xins.common.collections.PropertyReaderUtils;
import org.xins.common.http.HTTPCallConfig;
import org.xins.common.http.HTTPCallException;
import org.xins.common.http.HTTPCallRequest;
import org.xins.common.http.HTTPCallResult;
import org.xins.common.http.HTTPCallResultData;
import org.xins.common.http.HTTPStatusCodeVerifier;
import org.xins.common.http.StatusCodeHTTPCallException;
import org.xins.common.service.CallConfig;
import org.xins.common.service.CallException;
import org.xins.common.service.CallExceptionList;
import org.xins.common.service.CallRequest;
import org.xins.common.service.CallResult;
import org.xins.common.service.Descriptor;
import org.xins.common.service.GenericCallException;
import org.xins.common.service.IOCallException;
import org.xins.common.service.ServiceCaller;
import org.xins.common.service.TargetDescriptor;
import org.xins.common.service.UnsupportedProtocolException;
import org.xins.common.servlet.container.LocalServletHandler;
import org.xins.common.servlet.container.XINSServletResponse;
import org.xins.common.text.URLEncoding;
/**
* Call a XINS API using the internal Servlet container. This service caller
* doesn't send data over the network but directly invoke the Servlet method.
*
* @version $Revision: 1.17 $ $Date: 2007/05/29 12:22:30 $
* @author Anthony Goubard
*
* @since XINS 1.5.0
*/
class FileServiceCaller extends ServiceCaller {
/**
* The pool of the loaded XINS APIs. The key is the location of the WAR
* file, as a {@link TargetDescriptor}, the value is the {@link LocalServletHandler}.
*/
private static HashMap SERVLETS = new HashMap();
/**
* Constructs a new HTTPServiceCaller
object with the
* specified descriptor and call configuration.
*
* @param descriptor
* the descriptor of the service, cannot be null
.
*
* @param callConfig
* the call configuration, or null
if a default one should
* be used.
*
* @throws IllegalArgumentException
* if descriptor == null
.
*
* @throws UnsupportedProtocolException
* if descriptor
is or contains a {@link TargetDescriptor}
* with an unsupported protocol.
*/
public FileServiceCaller(Descriptor descriptor,
HTTPCallConfig callConfig)
throws IllegalArgumentException, UnsupportedProtocolException {
// Call superclass constructor
super(descriptor, callConfig);
}
/**
* Constructs a new FileServiceCaller
object with the
* specified descriptor and call configuration.
*
* @param descriptor
* the descriptor of the service, cannot be null
.
*
* @throws IllegalArgumentException
* if descriptor == null
.
*
* @throws UnsupportedProtocolException
* if descriptor
is or contains a {@link TargetDescriptor}
* with an unsupported protocol.
*/
public FileServiceCaller(Descriptor descriptor)
throws IllegalArgumentException, UnsupportedProtocolException {
this(descriptor, (HTTPCallConfig) null);
}
/**
* Returns a default CallConfig
object. This method is called
* by the ServiceCaller
constructor if no
* CallConfig
object was given.
*
* The implementation of this method in class {@link FileServiceCaller}
* returns a standard {@link HTTPCallConfig}.
*
* @return
* a new {@link HTTPCallConfig} instance, never null
.
*/
protected CallConfig getDefaultCallConfig() {
return new HTTPCallConfig();
}
protected CallResult createCallResult(CallRequest request, TargetDescriptor succeededTarget,
long duration, CallExceptionList exceptions, Object result) throws ClassCastException {
return new HTTPCallResult((HTTPCallRequest) request,
succeededTarget,
duration,
exceptions,
(HTTPCallResultData) result);
}
protected boolean isProtocolSupportedImpl(String protocol) {
return "file".equalsIgnoreCase(protocol);
}
/**
* Executes a request towards the specified target. If the call succeeds,
* then a {@link HTTPCallResult} object is returned, otherwise a
* {@link CallException} is thrown.
*
*
The implementation of this method in class
* HTTPServiceCaller
delegates to
* {@link #call(HTTPCallRequest,HTTPCallConfig)}.
*
* @param request
* the call request to be executed, must be an instance of class
* {@link HTTPCallRequest}, cannot be null
.
*
* @param callConfig
* the call configuration, never null
and should always be
* an instance of class {@link HTTPCallConfig}.
*
* @param target
* the target to call, cannot be null
.
*
* @return
* the result, if and only if the call succeeded, always an instance of
* class {@link HTTPCallResult}, never null
.
*
* @throws ClassCastException
* if the specified request
object is not null
* and not an instance of class {@link HTTPCallRequest}.
*
* @throws IllegalArgumentException
* if target == null || request == null
.
*
* @throws CallException
* if the call to the specified target failed.
*/
public Object doCallImpl(CallRequest request,
CallConfig callConfig,
TargetDescriptor target)
throws ClassCastException, IllegalArgumentException, CallException {
long start = System.currentTimeMillis();
long duration;
LocalServletHandler servletHandler = (LocalServletHandler) SERVLETS.get(target);
if (servletHandler == null) {
String fileLocation = target.getURL();
File warFile = new File(fileLocation.substring(7).replace('/', File.separatorChar));
try {
servletHandler = new LocalServletHandler(warFile);
SERVLETS.put(target, servletHandler);
} catch (ServletException sex) {
Log.log_2117(sex);
}
}
PropertyReader parameters = ((HTTPCallRequest) request).getParameters();
// Get the parameters for logging
FormattedParameters params = new FormattedParameters(parameters, null, "", "?", 160);
// Get URL value
String url = target.getURL();
// Loop through the parameters
StringBuffer query = new StringBuffer(255);
Iterator keys = parameters.getNames();
while (keys.hasNext()) {
// Get the parameter key
String key = (String) keys.next();
// Get the value
String value = parameters.get(key);
if (value == null) {
value = "";
}
// Add this parameter key/value combination.
if (key != null) {
if (query.length() > 0) {
query.append("&");
}
query.append(URLEncoding.encode(key));
query.append("=");
query.append(URLEncoding.encode(value));
}
}
XINSServletResponse response;
try {
response = servletHandler.query(query.toString());
} catch (IOException exception) {
duration = System.currentTimeMillis() - start;
org.xins.common.Log.log_1109(exception, url, params, duration);
throw new IOCallException(request, target, duration, exception);
}
// Retrieve the data returned from the call
HTTPCallResultData data;
try {
String result = response.getResult();
byte[] resultData = null;
if (result != null) {
resultData = result.getBytes(response.getCharacterEncoding());
}
data = new HTTPCallResultDataHandler(response.getStatus(), resultData);
} catch (UnsupportedEncodingException ueex) {
throw Utils.logProgrammingError(ueex);
}
// Determine the HTTP status code
int code = data.getStatusCode();
duration = System.currentTimeMillis() - start;
HTTPStatusCodeVerifier verifier = ((HTTPCallRequest)request).getStatusCodeVerifier();
// Status code is considered acceptable
if (verifier == null || verifier.isAcceptable(code)) {
org.xins.common.Log.log_1107(url, params, duration, code);
// Status code is considered unacceptable
} else {
org.xins.common.Log.log_1108(url, params, duration, code);
throw new StatusCodeHTTPCallException((HTTPCallRequest) request, target, duration, code);
}
return new HTTPCallResult((HTTPCallRequest) request, target, duration, null, data);
}
/**
* Performs the specified request towards the HTTP service. If the call
* succeeds with one of the targets, then a {@link HTTPCallResult} object
* is returned, that combines the HTTP status code and the data returned.
* Otherwise, if none of the targets could successfully be called, a
* {@link CallException} is thrown.
*
* @param request
* the call request, not null
.
*
* @param callConfig
* the call configuration to use, or null
.
*
* @return
* the result of the call, cannot be null
.
*
* @throws IllegalArgumentException
* if request == null
.
*
* @throws GenericCallException
* if the first call attempt failed due to a generic reason and all the
* other call attempts failed as well.
*
* @throws HTTPCallException
* if the first call attempt failed due to an HTTP-related reason and
* all the other call attempts failed as well.
*/
public HTTPCallResult call(HTTPCallRequest request,
HTTPCallConfig callConfig)
throws IllegalArgumentException,
GenericCallException,
HTTPCallException {
// Check preconditions
MandatoryArgumentChecker.check("request", request);
// Perform the call
CallResult callResult;
try {
callResult = doCall(request, callConfig);
// Allow GenericCallException, HTTPCallException and Error to proceed,
// but block other kinds of exceptions and throw an Error instead.
} catch (GenericCallException exception) {
throw exception;
} catch (HTTPCallException exception) {
throw exception;
} catch (Exception exception) {
throw Utils.logProgrammingError(exception);
}
return (HTTPCallResult) callResult;
}
/**
* Performs the specified request towards the HTTP service. If the call
* succeeds with one of the targets, then a {@link HTTPCallResult} object
* is returned, that combines the HTTP status code and the data returned.
* Otherwise, if none of the targets could successfully be called, a
* {@link CallException} is thrown.
*
* @param request
* the call request, not null
.
*
* @return
* the result of the call, cannot be null
.
*
* @throws IllegalArgumentException
* if request == null
.
*
* @throws GenericCallException
* if the first call attempt failed due to a generic reason and all the
* other call attempts failed as well.
*
* @throws HTTPCallException
* if the first call attempt failed due to an HTTP-related reason and
* all the other call attempts failed as well.
*/
public HTTPCallResult call(HTTPCallRequest request)
throws IllegalArgumentException,
GenericCallException,
HTTPCallException {
return call(request, (HTTPCallConfig) null);
}
/**
* Container of the data part of an HTTP call result.
*
* @version $Revision: 1.17 $ $Date: 2007/05/29 12:22:30 $
* @author Anthony Goubard
*
* @since XINS 1.5.0
*/
private static final class HTTPCallResultDataHandler
implements HTTPCallResultData {
/**
* The HTTP status code.
*/
private final int _code;
/**
* The data returned.
*/
private final byte[] _data;
/**
* Constructs a new HTTPCallResultDataHandler
object.
*
* @param code
* the HTTP status code.
*
* @param data
* the data returned from the call, as a set of bytes.
*/
HTTPCallResultDataHandler(int code, byte[] data) {
_code = code;
_data = data;
}
/**
* Returns the HTTP status code.
*
* @return
* the HTTP status code.
*/
public int getStatusCode() {
return _code;
}
/**
* Returns the result data as a byte array. Note that this is not a copy or
* clone of the internal data structure, but it is a link to the actual
* data structure itself.
*
* @return
* a byte array of the result data, never null
.
*/
public byte[] getData() {
return _data;
}
}
}