/** * */ package com.trendrr.strest.server; import java.nio.charset.Charset; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jboss.netty.handler.codec.http.HttpRequest; import org.jboss.netty.handler.codec.http.HttpResponse; import org.jboss.netty.handler.codec.http.HttpResponseStatus; import com.trendrr.oss.DynMap; import com.trendrr.oss.Reflection; import com.trendrr.oss.TypeCast; import com.trendrr.strest.ContentTypes; import com.trendrr.strest.StrestHttpException; import com.trendrr.strest.annotations.AnnotationHelper; import com.trendrr.strest.annotations.Strest; import com.trendrr.strest.server.connections.StrestConnectionChannel; import com.trendrr.strest.server.connections.StrestNettyConnectionChannel; import com.trendrr.strest.server.connections.StrestConnectionTxn; import com.trendrr.strest.server.v2.models.StrestRequest; import com.trendrr.strest.server.v2.models.StrestResponse; /** * * Base controller class. * * * * @author Dustin Norlander * @created Jan 13, 2011 * */ public abstract class StrestController { protected static Log log = LogFactory.getLog(StrestController.class); protected StrestRequest request; protected StrestResponse response = null; protected DynMap params = new DynMap(); protected DynMap paramsGET = new DynMap(); protected DynMap paramsPOST = new DynMap(); protected StrestRouter router = null; protected boolean skipExecution = false; /** * Should we skip running the GET POST ect methods? * @return */ public boolean isSkipExecution() { return skipExecution; } /** * This flag will indicate that the get,post,delete,put methods should NOT be executed. * * This is useful for filters to indicate that no error has occurred but the business logic should * not be run (i.e. for caching implementations). * * @param skipExecution */ public void setSkipExecution(boolean skipExecution) { this.skipExecution = skipExecution; } /** * returns the config params for the server. typically this is the parsed yaml config file. */ public DynMap getServerConfig() { return this.getRouter().getServer().getConfig(); } /** * This is the StrestRouter instance that initialized the controller. * * This is only useful in rare instances. * * @return */ public StrestRouter getRouter() { return router; } public void setRouter(StrestRouter router) { this.router = router; } /** * These params appear in the URI. I.E regular GET string params. * * These params also appear in the params that are passed to the controller method, so unless you * need to know that specific params are GET vs POST then use the regular params. * * @return the GET params */ public DynMap getParamsGET() { return paramsGET; } public void setParamsGET(DynMap paramsGET) { this.paramsGET = paramsGET; } /** * The params from a url encoded POST. * * These params also appear in the params that are passed to the controller method, so unless you * need to know that specific params are GET vs POST then use the regular params. * * @return the POST params */ public DynMap getParamsPOST() { return paramsPOST; } public void setParamsPOST(DynMap paramsPOST) { this.paramsPOST = paramsPOST; } protected boolean sendResponse = true; /** * If false then the this.response item will not automatically be sent to the client * @return */ public boolean isSendResponse() { return sendResponse; } /** * set to false if you would like to send responses outside the normal request response * * @param sendResponse */ public void setSendResponse(boolean sendResponse) { this.sendResponse = sendResponse; } /** * these are the params, either parsed from the get string, or from a form encoded post. * will also include any named params from the url string. * * @return */ public DynMap getParams() { return params; } public void setParams(DynMap params) { this.params = params; } protected boolean strest = false; protected String strestTxnId = null; /** * default constructor is manditory. Other constructors will not be used. */ public StrestController() { } public StrestConnectionChannel getChannelConnection() { return this.request.getConnectionChannel(); } public StrestConnectionTxn getTxnConnection() { return this.request.getConnectionChannel().getTxnConnection(this.strestTxnId); } /** * Txn storage is a way to keep state associated with a specific transaction. * The underlying map is threadsafe. All data will be deleted as soon the txn is complete. * * * @return */ private ConcurrentHashMap<String,Object> nonstrestTxnStorage = new ConcurrentHashMap<String,Object>(); public Map<String,Object> getTxnStorage() { if (!this.isStrest()) return this.nonstrestTxnStorage; try { return this.getChannelConnection().getTxnConnection(this.strestTxnId).getStorage(); } catch (NullPointerException x) { log.warn("Unable to get txn storage. The connection was likely broken, returning dummy"); return new HashMap<String,Object>(); } } /** * Connection storage is a way to keep any state associated to the connection. * * The underlying map is threadsafe. All data will be deleted as soon the connection is complete. * @return */ public Map<String,Object> getConnectionStorage() { return this.getChannelConnection().getStorage(); } /** * gets the session storage. * sessions must be enabled for http connections. otherwise this is * similar to connection storage or txn storage. * * This is not threadsafe, and should not be used in STREST connections if avoidable. * @deprecated functionality moved to cheshire html controller. * @return */ @Deprecated public Map<String,Object> getSessionStorage() { Map<String, Object> session = (Map<String, Object>)this.getConnectionStorage().get("session"); if (session == null) { session = new HashMap<String,Object>(); this.getConnectionStorage().put("session", session); } return session; } /** * deletes the session (if sessions are not enabled, does nothing) */ @Deprecated public void destroySession() { this.getConnectionStorage().put("session_destroy", true); } public void handleGET(DynMap params) throws Exception { throw StrestHttpException.METHOD_NOT_ALLOWED(); } public void handlePOST(DynMap params) throws Exception { throw StrestHttpException.METHOD_NOT_ALLOWED(); } /** * overrideable for the lesser used methods * @param params * @throws Exception */ public void handlePUT(DynMap params) throws Exception { throw StrestHttpException.METHOD_NOT_ALLOWED(); } /** * overrideable for the lesser used methods * @param params * @throws Exception */ public void handleDELETE(DynMap params) throws Exception { throw StrestHttpException.METHOD_NOT_ALLOWED(); } public StrestRequest getRequest() { return request; } public void setRequest(StrestRequest request) { this.request = request; } public StrestResponse getResponse() { return response; } public ResponseBuilder getResponseAsBuilder() { return ResponseBuilder.instance(this.getResponse()); } public void setResponse(StrestResponse response) { this.response = response; } public boolean isStrest() { return strest; } public void setResponseStatus(int code, String message){ this.response.setStatus(code, message); } public void setStrest(boolean strest) { this.strest = strest; } public String getStrestTxnId() { return strestTxnId; } public void setStrestTxnId(String strestTxnId) { this.strestTxnId = strestTxnId; } public void setResponseBytes(String mimeType, byte[] bytes) { ResponseBuilder.instance(this.response).content(mimeType, bytes); } public void setResponseUTF8(String mimeType, String val) { ResponseBuilder.instance(this.response).contentUTF8(mimeType, val); } public void setResponseJSON(String json) { ResponseBuilder.instance(this.response).contentUTF8(ContentTypes.JSON, json); } public void setResponseJSON(DynMap json) { ResponseBuilder.instance(this.response).contentJSON(json); } /** * returns the content of the request as a UTF8 encoded string * @return */ public String getRequestContentUTF8() { return request.getContent().toString(); } public String[] routes() { if (this.isAnnotationPresent()) { return this.getAnnotationVal(String[].class, "route"); } return null; } /** * gets any additional filters that should run for this controller. * can be override by subclass, else takes the 'filters' value from the * annotation. * @return */ public Class[] filters() { if (this.isAnnotationPresent()) { return this.getAnnotationVal(Class[].class, "filters"); } return null; } public String[] requiredParams() { if (this.isAnnotationPresent()) { return this.getAnnotationVal(String[].class, "requiredParams"); } return null; } /** * gets the namespace for the filters in * @return */ public String getControllerNamespace() { return "default"; } /** * gets a value from the annotation * @param cls * @param name * @return */ protected <T> T getAnnotationVal(Class<T> cls, String name) { return AnnotationHelper.getAnnotationVal(this.getAnnotationClass(), this, cls, name); } protected Class getAnnotationClass() { return Strest.class; } protected boolean isAnnotationPresent() { return this.getClass().isAnnotationPresent(this.getAnnotationClass()); } }