hk.hku.cecid.corvus.http.HttpSender.java Source code

Java tutorial

Introduction

Here is the source code for hk.hku.cecid.corvus.http.HttpSender.java

Source

/* 
 * Copyright(c) 2005 Center for E-Commerce Infrastructure Development, The
 * University of Hong Kong (HKU). All Rights Reserved.
 *
 * This software is licensed under the GNU GENERAL PUBLIC LICENSE Version 2.0 [1]
 * 
 * [1] http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt
 */

package hk.hku.cecid.corvus.http;

import java.net.MalformedURLException;
import java.net.URL;

import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.HttpMethod;
import org.apache.commons.httpclient.HttpStatus;
import org.apache.commons.httpclient.HttpException;
import org.apache.commons.httpclient.methods.PostMethod;
import org.apache.commons.httpclient.auth.AuthScope;
import org.apache.commons.httpclient.UsernamePasswordCredentials;

import hk.hku.cecid.piazza.commons.data.Data;
import hk.hku.cecid.piazza.commons.util.FileLogger;

/** 
 * The <code>HttpSender</code> is top base class for sending HTTP request.
 *  
 * TODO: javadoc
 *
 * @author    Twinsen Tsang
 * @version 1.0.0
 * @since   JDK5.0, H2O 0908
 */
public class HttpSender implements Runnable {
    /* The boolean flag indicating whether the HTTP request requires Authentication. */
    private boolean isAuthRequired;

    /* The Apache HTTP client for doing the real HTTP request/response job. */
    private HttpClient delegationClient;

    /* The Apache HTTP client object for configuring the HTTP request and reading the HTTP response. */
    private HttpMethod requestMethod;

    /**
     * The logger used for log message and exception
     * 
     * @see hk.hku.cecid.piazza.commons.util.FileLogger
     */
    protected FileLogger log;

    /* The data properties for this sender. */
    protected Data properties;

    /* Number of times to sent to Hermes2. */
    private int loopTimes = 1;

    /* The current looping times. */
    private int curTimes = 0;

    /* The custom user object for the call-back. */
    private Object userObj = null;

    /* The URL of service end point. */
    protected URL serviceEndPoint = null;

    /**
     * SPA Constructor.<br<br>
     * 
     * It is used when the HTTP Sender is a component in the SPA.
     */
    public HttpSender() {
        this(null, null);
    }

    /**
     * Explicit Constructor.
     * 
     * @param logger The logger used for log message and exception.
     * @param d The data used for sending HTTP request.
     */
    public HttpSender(FileLogger logger, Data d) {
        this.log = logger;
        if (d == null)
            throw new NullPointerException("Missing 'data properties' when constructing HTTP sender.");
        this.properties = d;
        this.delegationClient = new HttpClient();
    }

    /**
     * Explicit Constructor.
     * 
     * @param l         The logger used for log message and exception.
     * @param d The data used for sending HTTP request. 
     * @param endpoint   The URL of service end point.
     */
    public HttpSender(FileLogger l, Data d, String endpoint) {
        this(l, d);
        try {
            this.serviceEndPoint = new URL(endpoint);
        } catch (MalformedURLException mue) {
            this.onError(mue);
        }
    }

    /**
     * Explicit Constructor.
     * 
     * @param l         The logger used for log message and exception.
     * @param d       The data used for sending HTTP request. 
     * @param endpoint   The URL of service end point.
     */
    public HttpSender(FileLogger l, Data d, URL endpoint) {
        this(l, d);
        this.serviceEndPoint = endpoint;
    }

    /**
     * Implements this method if you want to send messages without much different between other message to sent.   
     */
    protected void initializeMessage() throws Exception {
        // Implement by sub-class.
    }

    /** 
     * [@EVENT] This method is invoked when the sender begins to execute the 
     * run method.<br/>  
     */
    protected void onStart() {
        this.curTimes = 0;
    }

    /** 
     * [@EVENT] This method is invoked when the sender is required to 
     * create a HTTP Request from configuration.
     * <br/><br/>
     * By default, this method return a PostMethod pointing to {@link #getServiceEndPoint()}.    
     * 
     * @throws Exception 
     *          Sub-class implementation-specific exception
     */
    protected HttpMethod onCreateRequest() throws Exception {
        return new PostMethod(this.serviceEndPoint.toExternalForm());
    }

    /**
     * [@EVENT] This method is invoked just before sending the request to HTTP service end-point.
     * 
     * @param client    The HTTP Connection used for sending SOAP request.
     * @param request   The request created by {@link #onCreateRequest()}. 
     * 
     * @throws Exception Any type of exception will be processed at onError(throwable t). 
     */
    protected void onBeforeRequest(final HttpClient client, final HttpMethod request) throws Exception {
        // Implement by sub-class
    }

    /**
     * [@EVENT] This method is invoked when received the reply HTTP  response from the server.
     * 
     * Developer can use {@link #getExecutedMethod()} to get
     * the HTTP method generated thru {@link #onCreateRequest()}
     * 
     * @throws Exception Any type of exception will be processed at onError(throwable t).
     */
    protected void onResponse() throws Exception {
        // Implement by sub-class
    }

    /**
     * [@EVENT] This method is invoked when the sending execution is ended. 
     */
    protected void onEnd() {
        // Implement by sub-class
    }

    /**
     * [@EVENT] This method is invoked when there is any exception thrown during web service call.
     * <br/><br/>
     * By default, it log the throw-able <code>t</code> to the instance logger.
     */
    protected void onError(Throwable t) {
        t.printStackTrace();
        if (this.log != null) {
            this.log.logStackTrace(t);
        }
    }

    /**
     * [@EVENT] This method is invoked when each loop iteration start.
     * <br/><br/> 
     * @throws Exception Any type of exception will be processed at onError(throw-able t).
     */
    protected void onEachLoopStart() throws Exception {
        // Implement by-sub-class
    }

    /**
     * Set how many times should the sender to be send.
     * 
     * @param loopTimes      the new loopTimes.
     */
    public void setLoopTimes(int loopTimes) {
        if (loopTimes > 0) {
            this.loopTimes = loopTimes;
        }
    }

    /**
     * Set a user object for call-back.
     * 
     * @param obj       The user object.
     */
    public void setUserObject(Object obj) {
        this.userObj = obj;
    }

    /**
     * Set the service end-point.
     * 
     * @param endpoint   The URL of the web service end-point. 
     */
    public void setServiceEndPoint(URL endpoint) {
        if (endpoint != null) {
            this.serviceEndPoint = endpoint;
        }
    }

    /**
     * Set the service end-point.
     * 
     * @param endpoint   The String of the web service end-point. 
     */
    public void setServiceEndPoint(String endpoint) {
        try {
            this.serviceEndPoint = new URL(endpoint);
        } catch (MalformedURLException mue) {
            if (this.log != null) {
                this.log.logStackTrace(mue);
            }
        }
    }

    /**
     * Set to use the basic authentication when calling the web service.
     * 
     * @param username The user-name for basic authentication. 
     * @param password The password for basic authentication.
     * 
     * @throws NullPointerException 
     *          When the user-name or password is null.         
     */
    public void setBasicAuthentication(final String username, final String password) {
        if (username == null || username.equals(""))
            throw new NullPointerException("Missing 'username' for authentication.");
        if (password == null)
            throw new NullPointerException("Missing 'password' for authentication.");

        this.delegationClient.getState().setCredentials(new AuthScope(AuthScope.ANY_HOST, AuthScope.ANY_PORT),
                new UsernamePasswordCredentials(username, password));

        // Use BASIC-Auth immediately.
        this.delegationClient.getParams().setAuthenticationPreemptive(true);
        this.isAuthRequired = true;
    }

    /** 
     * @return true if HTTP authentication is required. 
     */
    public boolean isAuthenticationRequired() {
        return this.isAuthRequired;
    }

    /**
     * Get how many times should the sender to be send.
     */
    public int getLoopTimes() {
        return this.loopTimes;
    }

    /**
     * Get what is the current loop times for looping 
     */
    public int getCurrentLoopTimes() {
        return this.curTimes;
    }

    /**
     * Get a user object. 
     */
    public Object getUserObject() {
        return this.userObj;
    }

    /**
     * Get the service end-point.
     * 
     * @return the service end-point URL.
     */
    public URL getServiceEndPoint() {
        return this.serviceEndPoint;
    }

    /**
     * Get the last executed HTTP method.
     * <br/><br/>
     * This method should be invoked during {@link #onResponse()}.   
     *  
     * @return the last executed HTTP method.
     */
    public HttpMethod getExecutedMethod() {
        return this.requestMethod;
    }

    /**
     * The thread execution method.
     */
    public void run() {
        // Signals a kick-off event.
        this.onStart();
        try {
            for (int i = 0; i < this.getLoopTimes(); i++) {
                this.curTimes = i;
                // Signals a loop start event.
                this.onEachLoopStart();
                // Asks child class for creating the request method;            
                this.requestMethod = this.onCreateRequest();
                if (this.isAuthRequired)
                    this.requestMethod.setDoAuthentication(true);

                this.onBeforeRequest(this.delegationClient, this.requestMethod);

                int responseCode = this.delegationClient.executeMethod(this.requestMethod);

                if (responseCode != HttpStatus.SC_OK)
                    throw new HttpException(this.requestMethod.getStatusLine().toString());

                this.onResponse();
            }
            this.onEnd();
        } catch (Exception e) {
            this.onError(e);
        } finally {
            if (this.requestMethod != null)
                this.requestMethod.releaseConnection();
        }
    }
}