/* * 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(); } } }