/* * 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.io.InputStream; import java.util.ArrayList; import java.util.Date; import java.util.List; import java.util.Map; import java.util.Iterator; import org.apache.commons.httpclient.HttpMethod; import org.apache.commons.httpclient.methods.PostMethod; import org.apache.commons.httpclient.methods.multipart.ByteArrayPartSource; import org.apache.commons.httpclient.methods.multipart.FilePart; import org.apache.commons.httpclient.methods.multipart.MultipartRequestEntity; import org.apache.commons.httpclient.methods.multipart.Part; import org.apache.commons.httpclient.methods.multipart.PartSource; import org.apache.commons.httpclient.methods.multipart.StringPart; import hk.hku.cecid.corvus.ws.data.KVPairData; import hk.hku.cecid.piazza.commons.util.FileLogger; /** * The <code>PartnershipSender</code> is abstract base class for sending HTTP remote request * to H2O for executing partnership maintenance operation. * <br/><br/> * * @author Twinsen Tsang * @version 1.0.0 $STABLE$ * @since H2O 0908 */ public abstract class PartnershipSender extends HttpSender implements PartnershipOp { // The operation for creating partnership. private int pOp = PartnershipOp.ADD; /* The result status line representing the operation result. */ private String resultStatus = "Not yet run."; /** * Get the mapping of the partnership operation from integer to words. * <br/><br/> * By default, it is recommended to return a HashMap(Integer, String) with 3 mappings. * <br><br/> * HashMap.get(0) = A word representing the add partnership action. * HashMap.get(1) = A word representing the delete partnership action. * HashMap.get(2) = A word representing the update partnership action. * * @return The mapping of the partnership operation from integer to words. */ public abstract Map getPartnershipOperationMapping(); /** * Get the mapping of the partnership data key to HTTP form parameter name. * <br/><br/> * For example, if there are 3 data (with keys) in your partnership data and * they are named as "dataKey0", "dataKey1" and "dataKey2", and you want the * HTTP request going to execute containing multi-part parameters "formParam0", * "formParam1" and "formParam2" with the value equal to the data value * from "dataKey0", "dataKey1", "dataKey2" respectively, Then * you should return the Map listed below: * <br/><br/> * <pre> * Map m = new HashMap(); // Or LinkedHashMap() if you want to preserve the order. * m.put("dataKey0", "fromParam0"); * m.put("dataKey1", "fromParam1"); * m.put("dataKey2", "fromParam2"); * return m; * </pre> * * @return The mapping of the partnership data key to HTTP form parameter name. */ public abstract Map getPartnershipMapping(); /** * Explicit Constructor. Create an instance of <code>PartnershipSender</code> * * @param logger The logger for log the sending process. * @param d The data used for generate HTTP multi-part request. It must be a kind of partnership data. */ protected PartnershipSender(FileLogger logger, KVPairData d) { super(logger, d); } /* (non-Javadoc) * @see hk.hku.cecid.corvus.http.PartnershipOperation#setExecuteOperation(int) */ public void setExecuteOperation(int pOp){ if (pOp < 0 || pOp >= PartnershipOp.OP_LEN) throw new IllegalArgumentException("Expected operation value : 0, 1, 2"); this.pOp = pOp; } /* (non-Javadoc) * @see hk.hku.cecid.corvus.http.PartnershipOperation#getExecuteOperation() */ public int getExecuteOperation(){ return this.pOp; } /** * Get the last status result description after executing the operation. * <br/><br/> * If the sender has not been invoked by other to execute partnership operation, * It returns "Not yet run". * * @return the last status result description after executing the operation. */ public String getStatus(){ return this.resultStatus; } /** * [@EVENT] The method <code>onStart</code> log all new configuration. */ protected void onStart() { // Get the data, KVPairData data = (KVPairData) this.properties; super.onStart(); if (this.log != null){ FileLogger l = this.log; // Log all information for this sender. l.log("Partnership HTTP Client init at " + new Date().toString()); l.log(""); l.log("Sending Partnership HTTP Request with following configuration"); l.log("------------------------------------------------------------------"); l.log("Partnership Operation : " + this.getPartnershipOperationMapping().get(new Integer(pOp))); if (data != null) l.log(data.toString()); l.log("------------------------------------------------------------------"); l.log(""); } } /** * [@EVENT] This method is invoked when the sender is required to create a HTTP Request from configuration. * <br/><br/> * It generates a multi-part content embedded in the HTTP POST request. The multi-part content * contains all partnership data with the parameter name retrieved from the partnership mapping. * {@link #getPartnershipMapping()}. Also the type of partnership operation is appended * at the end of multi-part with parameter name equal to 'request_action' and it's value * is extracted thru {@link #getPartnershipOperationMapping()}. */ protected HttpMethod onCreateRequest() throws Exception { // Validate main parameter. Map data = ((KVPairData)this.properties).getProperties(); Map data2webFormName = this.getPartnershipMapping(); if (data2webFormName == null) throw new NullPointerException("Missing partnership mapping for creating HTTP request"); // Create the HTTP POST method targeted to service end-point. PostMethod post = new PostMethod(this.getServiceEndPoint().toExternalForm()); List parts = new ArrayList(); // An array for multi-part; /* * For each data key in the partnership data, create a String multi-part * with the request parameter name equal to the mapping from the data key. * * For the field like verification / encryption certificates, it creates * a byte array multi-part source. */ Iterator itr = data2webFormName.entrySet().iterator(); Map.Entry e; // an entry representing the partnership data to web form name mapping. String formParamName; // a temporary pointer pointing to the value in the entry. Object dataValue; // a temporary pointer pointing to the value in the partnership data. Part newPart; // a temporary pointer pointing to a multi-part part. while (itr.hasNext()){ e = (Map.Entry) itr.next(); formParamName = (String) e.getValue(); // Add new part if the mapped key is not null. if (e.getValue() != null){ dataValue = data.get(e.getKey()); if (dataValue == null) // Use empty string when the key is not filled. dataValue = ""; if (dataValue instanceof String){ // Create literal part newPart = new StringPart(formParamName, (String)dataValue); } else if (dataValue instanceof byte[]){ // Create streaming multi-part PartSource source = new ByteArrayPartSource((String)e.getKey(), (byte[])dataValue); newPart = new FilePart (formParamName, source); } else if (dataValue instanceof Boolean){ newPart = new StringPart(formParamName, String.valueOf((Boolean)dataValue)); } else { newPart = new StringPart(formParamName, dataValue.toString()); } // Add the new part. parts.add(newPart); } } Map partnershipOpMap = this.getPartnershipOperationMapping(); /* Add HTTP request action to the web form parameter. */ parts.add(new StringPart("request_action", (String)partnershipOpMap.get(new Integer(this.pOp)))); MultipartRequestEntity multipartRequest = new MultipartRequestEntity( (Part[])parts.toArray(new Part[]{}), post.getParams()); post.setRequestEntity(multipartRequest); return post; } /** * [@EVENT] This method is invoked when receivedas2 the reply HTTP response from the server. * <br/><br/> * Verify the HTTP response (expected a HTML content) by PartnershipOpVerifer to check * whether the partnership operation execute successfully or not. * * @throws SAXException * When fail to verify by PartnershipOpVerifer. */ protected void onResponse() throws Exception { try{ HttpMethod method = this.getExecutedMethod(); InputStream ins = method.getResponseBodyAsStream(); new PartnershipOpVerifer().validate(ins); ins.close(); this.resultStatus = "Operation executed successfully."; } catch(Exception ex){ this.resultStatus = "ERROR: " + ex.getMessage(); throw ex; // Re-throw } } }