/* * CDDL HEADER START * * The contents of this file are subject to the terms of the Common Development * and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at * src/com/vodafone360/people/VODAFONE.LICENSE.txt or * http://github.com/360/360-Engine-for-Android * See the License for the specific language governing permissions and * limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each file and * include the License file at src/com/vodafone360/people/VODAFONE.LICENSE.txt. * If applicable, add the following below this CDDL HEADER, with the fields * enclosed by brackets "[]" replaced with your own identifying information: * Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END * * Copyright 2010 Vodafone Sales & Services Ltd. All rights reserved. * Use is subject to license terms. */ package com.vodafone360.people.service.io; import java.io.IOException; import java.io.OutputStream; import java.util.Hashtable; import java.util.List; import java.util.Vector; import com.vodafone360.people.Settings; import com.vodafone360.people.SettingsManager; import com.vodafone360.people.datatypes.AuthSessionHolder; import com.vodafone360.people.engine.EngineManager.EngineId; import com.vodafone360.people.engine.login.LoginEngine; import com.vodafone360.people.service.io.rpg.RpgMessage; import com.vodafone360.people.service.io.rpg.RpgMessageTypes; import com.vodafone360.people.service.transport.http.HttpConnectionThread; import com.vodafone360.people.service.utils.AuthUtils; import com.vodafone360.people.service.utils.hessian.HessianEncoder; /** * Container class for Requests issued from client to People server via the * transport layer. A request consists of a payload, an identifier for the * engine responsible for handling this request, a request type and a request id * which is generated on creation of the request. */ public class Request { /** * Request types, these are based on the content requested from, or * delivered to, the server, and whether the returned response contains a * type identifier or not. */ public enum Type { COMMON, // Strongly typed ADD_CONTACT, TEXT_RESPONSE_ONLY, CONTACT_CHANGES_OR_UPDATES, CONTACT_DELETE, CONTACT_DETAIL_DELETE, FRIENDSHIP_REQUEST, USER_INVITATION, SIGN_UP, SIGN_IN, RETRIEVE_PUBLIC_KEY, EXPECTING_STATUS_ONLY, GROUP_LIST, STATUS_LIST, STATUS, CONTACT_GROUP_RELATION_LIST, CONTACT_GROUP_RELATIONS, DELETE_CONTACT_GROUP_RELATIONS, ITEM_LIST_OF_LONGS, PRESENCE_LIST, AVAILABILITY, CREATE_CONVERSATION, SEND_CHAT_MESSAGE, PUSH_MSG, EXTERNAL_RPG_RESPONSE, GET_MY_IDENTITIES, GET_AVAILABLE_IDENTITIES, DELETE_IDENTITY // response to external RPG request } /* * List of parameters which will be used to generate the AUTH parameter. */ private Hashtable<String, Object> mParameters = new Hashtable<String, Object>(); /** * Name of method on the backend. E.g. identities/getavailableidentities. */ private String mApiMethodName; /** RPG message payload (usually Hessian encoded message body). */ // private byte[] mPayload; /** RPG Message type - as defined above. */ public Type mType; /** Handle of Engine associated with this RPG message. */ public EngineId mEngineId = EngineId.UNDEFINED; // to be used to map request // to appropriate engine /** Whether we use RPG for this message. */ // public boolean mUseRpg = true; /** active flag - set to true once we have actually issued a request */ private boolean mIsActive = false; /** * The timeout set for the request. -1 if not set. */ private long mTimeout = -1; /** * The expiry date calculated when the request gets executed. */ private long mExpiryDate = -1; /** true if the request has expired */ public boolean expired; /** * The timestamp when the request's auth was calculated. */ private long mAuthTimestamp; /** * The timestamp when this request was created. */ private long mCreationTimestamp; /** * <p> * Represents the authentication type that needs to be taken into account * when executing this specific request. There are 3 types of authentication * types: * </p> * <ul> * <li>USE_API: needs the API. These requests are usually requests like * getSessionByCredentials that use application authentication.</li> * <li>USE_RPG: these requests should use the RPG and some of them MUST use * the RPG. Usually, all requests after the auth requests should use the * RPG.</li> * <li>USE_BOTH: some requests like the requests for getting terms and * conditions or privacy statements need to be able to use the RPG or API at * any time of the application lifecycle.</li> * </ul> */ private byte mAuthenticationType; public static final byte USE_API = 1, USE_RPG = 2, USE_BOTH = 3; /** * If true, this method is a fire and forget method and will not expect any * responses to come in. This fact can be used for removing requests from * the queue as soon as they have been sent. */ private boolean mIsFireAndForget; /** RPG message request id. */ private int mRequestId; /** * Constructor used for constructing internal (RPG/API) requests. * * @param apiMethodName The method name of the call, e.g. * "identities/getavailableidentities". * @param type RPG message type. * @param engId The engine ID. Will be used for routing the response to this * request back to the engine that can process it. * @param needsUserAuthentication If true we need to authenticate this * request by providing the session in the auth. This requires * the user to be logged in. * @param isFireAndForget True if the request is a fire and forget request. * @param timeout the timeout in milliseconds before the request throws a * timeout exception */ public Request(String apiMethodName, Type type, EngineId engineId, boolean isFireAndForget, long timeout) { mType = type; // TODO find out a type yourself? mEngineId = engineId; mApiMethodName = apiMethodName; mIsFireAndForget = isFireAndForget; mCreationTimestamp = System.currentTimeMillis(); mTimeout = timeout; if ((type == Type.RETRIEVE_PUBLIC_KEY) || (type == Type.SIGN_UP) || (type == Type.STATUS) || (type == Type.SIGN_IN)) { // we need to register, sign in, get t&c's etc. so the request needs // to happen without // user auth mAuthenticationType = USE_API; } else if (type == Type.TEXT_RESPONSE_ONLY) { // t&c or privacy mAuthenticationType = USE_BOTH; } else { // all other requests should use the RPG by default mAuthenticationType = USE_RPG; } } /** * Constructor used for constructing an external request used for fetching * e.g. images. * * @param externalUrl The external URL of the object to fetch. * @param urlParams THe parameters to add to the URL of this request. * @param engineId The ID of the engine that will be called back once the * response for this request comes in. */ public Request(String externalUrl, String urlParams, EngineId engineId) { mType = Type.EXTERNAL_RPG_RESPONSE; mEngineId = engineId; mApiMethodName = ""; mIsFireAndForget = false; mCreationTimestamp = System.currentTimeMillis(); mParameters = new Hashtable<String, Object>(); mParameters.put("method", "GET"); mParameters.put("url", externalUrl + urlParams); mAuthenticationType = USE_RPG; } /** * Is request active - i.e has it been issued * * @return true if request is active */ public boolean isActive() { return mIsActive; } /** * <p> * Sets whether this request is active or not. An active request is a * request that is currently being sent to the server and awaiting a * response. * </p> * <p> * The reason is that an active request should not be sent twice. * </p> * * @param isActive True if the request is active, false otherwise. */ public void setActive(boolean isActive) { mIsActive = isActive; } /** * Returns a description of the contents of this object * * @return The description */ @Override public String toString() { StringBuilder sb = new StringBuilder("Request [mEngineId="); sb.append(mEngineId); sb.append(", mIsActive="); sb.append(mIsActive); sb.append(", mTimeout="); sb.append(mTimeout); sb.append(", mReqId="); sb.append(mRequestId); sb.append(", mType="); sb.append(mType); sb.append(", mNeedsUserAuthentication="); sb.append(mAuthenticationType); sb.append(", mExpired="); sb.append(expired); sb.append(", mDate="); sb.append(mExpiryDate); sb.append("]\n"); sb.append(mParameters); return sb.toString(); } /** * Adds element to list of params * * @param name key name for object to be added to parameter list * @param nv object which will be added */ public void addData(String name, Hashtable<String, ?> nv) { mParameters.put(name, nv); } /** * Adds element to list of params * * @param name key name for object to be added to parameter list * @param value object which will be added */ public void addData(String name, Vector<Object> value) { mParameters.put(name, value); } /** * Adds element to list of params * * @param name key name for object to be added to parameter list * @param value object which will be added */ public void addData(String name, List<String> value) { mParameters.put(name, value); } /** * Adds byte array to parameter list * * @param varName name * @param value byte[] array to be added */ public void addData(String varName, byte[] value) { mParameters.put(varName, value); } /** * Create new NameValue and adds it to list of params * * @param varName name * @param value string value */ public void addData(String varName, String value) { mParameters.put(varName, value); } /** * Create new NameValue and adds it to list of params * * @param varName name * @param value Long value */ public void addData(String varName, Long value) { mParameters.put(varName, value); } /** * Create new NameValue and adds it to list of params * * @param varName name * @param value Integer value */ public void addData(String varName, Integer value) { mParameters.put(varName, value); } /** * Create new NameValue and adds it to list of params * * @param varName name * @param value Boolean value */ public void addData(String varName, Boolean value) { mParameters.put(varName, value); } /** * Returns a Hashtable containing current request parameter list. * * @return params The parameters that were added to this backend request. */ /* * public Hashtable<String, Object> getParameters() { return mParameters; } */ /** * Returns the authentication type of this request. This can be one of the * following: USE_API, which must be used for authentication requests that * need application authentication, USE_RPG, useful for requests against the * API that need user authentication and want to profit from the mobile * enhancements of the RPG, or USE_BOTH for requests that need to be able to * be used on the API or RPG. These requests are for example the Terms and * Conditions requests which need to be accessible from anywhere in the * client. * * @return True if this method requires user authentication or a valid * session to be more precise. False is returned if the method only * needs application authentication. */ public byte getAuthenticationType() { return mAuthenticationType; } /** * Gets the request ID of this request. * * @return The unique request ID for this request. */ public int getRequestId() { return mRequestId; } /** * Sets the request ID for this request. * * @param requestId The request ID to set. */ public void setRequestId(int requestId) { mRequestId = requestId; } /** * Gets the time stamp when this request's hash was calculated. * * @return The time stamp representing the creation date of this request. */ public long getAuthTimestamp() { return mAuthTimestamp; } /** * Gets the time stamp when this request was created. * * @return The time stamp representing the creation date of this request. */ public long getCreationTimestamp() { return mCreationTimestamp; } /** * Gets the set timeout. * * @return the timeout in milliseconds, -1 if not set. */ public long getTimeout() { return mTimeout; } /** * Gets the calculated expiry date. * * @return the expiry date in milliseconds, -1 if not set. */ public long getExpiryDate() { return mExpiryDate; } /** * Overwrites the timestamp if we need to wait one more second due to an * issue on the backend. * * @param timestamp The timestamp in milliseconds(!) to overwrite with. */ /* public void overwriteTimetampBecauseOfBadSessionErrorOnBackend(long timestamp) { mAuthTimestamp = timestamp; if (null != mParameters) { if (null != mParameters.get("timestamp")) { String ts = "" + (timestamp / 1000); mParameters.put("timestamp", ts); } } } */ /** * Returns the API call this request will use. * * @return The API method name this request will call. */ public String getApiMethodName() { return mApiMethodName; } /** * Returns true if the method is a fire and forget method. Theses methods * can be removed from the request queue as soon as they have been sent out. * * @return True if the request is fire and forget, false otherwise. */ public boolean isFireAndForget() { return mIsFireAndForget; } /** * Serializes the request's data structure to the passed output stream * enabling the connection to easily prepare one or multiple) requests. * * @param os The output stream to serialise this request to. * @param writeRpgHeader If true the RPG header is written. */ public void writeToOutputStream(OutputStream os, boolean writeRpgHeader) { if (null == os) { return; } byte[] body; calculateAuth(); try { body = makeBody(); if (!writeRpgHeader) { os.write(body); // writing to the api directly return; } } catch (IOException ioe) { HttpConnectionThread.logE("Request.writeToOutputStream()", "Failed writing standard API request: " + mRequestId, ioe); return; } int requestType = 0; if (mType == Request.Type.PRESENCE_LIST) { requestType = RpgMessageTypes.RPG_GET_PRESENCE; } else if (mType == Request.Type.AVAILABILITY) { requestType = RpgMessageTypes.RPG_SET_AVAILABILITY; } else if (mType == Request.Type.CREATE_CONVERSATION) { requestType = RpgMessageTypes.RPG_CREATE_CONV; } else if (mType == Request.Type.SEND_CHAT_MESSAGE) { requestType = RpgMessageTypes.RPG_SEND_IM; } else if (mType == Request.Type.EXTERNAL_RPG_RESPONSE) { requestType = RpgMessageTypes.RPG_EXT_REQ; } else { requestType = RpgMessageTypes.RPG_INT_REQ; } byte[] message = RpgMessage.createRpgMessage(body, requestType, mRequestId); try { os.write(message); } catch (IOException ioe) { HttpConnectionThread.logE("Request.writeToOutputStream()", "Failed writing RPG request: " + mRequestId, ioe); } } /** * Creates the body of the request using the parameter list. * * @return payload The hessian encoded payload of this request body. * @throws IOException Thrown if anything goes wrong using the hessian * encoder. */ private byte[] makeBody() throws IOException { // XXX this whole method needs to go or at least be changed into a // bytearray outputstream byte[] payload = HessianEncoder.createHessianByteArray(mApiMethodName, mParameters); if (payload == null) { return null; } payload[1] = (byte)1; // TODO we need to change this if we want to use a // baos payload[2] = (byte)0; return payload; } /** * Gets the auth of this request. Prior to the the * writeToOutputStream()-method must have been called. * * @return The auth of this request or null if it was not calculated before. */ public String getAuth() { return (String)mParameters.get("auth"); } /** * Calculate the Auth value for this Requester. TODO: Throttle by * timestamp/function to prevent automatic backend log out */ private void calculateAuth() { String ts = null; if (null != mParameters) { ts = (String)mParameters.get("timestamp"); } if (null == ts) { mAuthTimestamp = System.currentTimeMillis(); ts = "" + (mAuthTimestamp / 1000); } AuthSessionHolder session = LoginEngine.getSession(); if (session != null) { addData("auth", SettingsManager.getProperty(Settings.APP_KEY_ID) + "::" + session.sessionID + "::" + ts); } else { addData("auth", SettingsManager.getProperty(Settings.APP_KEY_ID) + "::" + ts); } addData("auth", AuthUtils.calculateAuth(mApiMethodName, mParameters, ts, session)); /** * if (mNeedsUserAuthentication) { addData("auth", * AuthUtils.calculateAuth(mApiMethodName, mParameters, ts, session)); } * else { // create the final auth without the session addData("auth", * AuthUtils.calculateAuth(mApiMethodName, mParameters, ts, null)); } */ } /** * Calculates the expiry date based on the timeout. TODO: should have * instead an execute() method to call when performing the request. it would * set the request to active, calculates the expiry date, etc... */ public void calculateExpiryDate() { if (mTimeout > 0) { mExpiryDate = System.currentTimeMillis() + mTimeout; } } }