/*
* This software is distributed under the terms of the FSF
* Gnu Lesser General Public License (see lgpl.txt).
*
* This program is distributed WITHOUT ANY WARRANTY. See the
* GNU General Public License for more details.
*/
package com.scooterframework.web.controller;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
import com.scooterframework.admin.ApplicationConfig;
import com.scooterframework.admin.Constants;
import com.scooterframework.admin.EnvConfig;
import com.scooterframework.admin.FilterManager;
import com.scooterframework.admin.FilterManagerFactory;
import com.scooterframework.common.logging.LogUtil;
import com.scooterframework.common.util.Converters;
import com.scooterframework.common.util.CurrentThreadCache;
import com.scooterframework.common.util.CurrentThreadCacheClient;
import com.scooterframework.common.util.DateUtil;
import com.scooterframework.common.util.Message;
import com.scooterframework.common.util.Util;
import com.scooterframework.common.validation.ValidationResults;
import com.scooterframework.orm.activerecord.ActiveRecord;
import com.scooterframework.orm.misc.JdbcPageListSource;
import com.scooterframework.orm.misc.Paginator;
/**
* <p>
* ActionControl class serves as a helper class for all controller classes.
* </p>
*
* @author (Fei) John Chen
*/
public class ActionControl {
private static LogUtil log = LogUtil.getLogger(ActionControl.class.getName());
/**
* Returns controller name which handles the request. The controller name
* is linked to key
* {@link com.scooterframework.admin.Constants#CONTROLLER}.
*/
public static String getController() {
return CurrentThreadCacheClient.controller();
}
/**
* Returns action name which handles the request. The action name
* is linked to key
* {@link com.scooterframework.admin.Constants#ACTION}.
*/
public static String getAction() {
return CurrentThreadCacheClient.action();
}
/**
* Returns model name.
*
* <p>
* Model name must be set in current thread cache before this method is
* called.
* </p>
*
* @return model name
*/
public static String getModel() {
String model = CurrentThreadCacheClient.model();
if (model == null) throw new IllegalArgumentException("Model name (Constants.MODEL: key.model) must be set first.");
return model;
}
/**
* Returns resource name which is linked to key
* {@link com.scooterframework.admin.Constants#RESOURCE}.
*/
public static String getResource() {
return CurrentThreadCacheClient.resource();
}
/**
* <p>
* Returns request extension which is linked to key
* {@link com.scooterframework.admin.Constants#FORMAT}.</p>
*
* </p>
* This is obtained from the request path. For example, if the request
* path is <tt>/blog/posts/1.xml</tt>, then the format is <tt>xml</tt>,
* which means the client wants the response be sent
* in <tt>xml</tt> format.</p>
*
* <p>
* The value of <tt>format()</tt> is what is used as a default format
* for response.</p>
*/
public static String format() {
return CurrentThreadCacheClient.format();
}
/**
* Returns URI path to a view template of the current controller's action.
* <pre>
* Examples:
* viewPath("show") : /WEB-INF/views/posts/show.jsp
* viewPath("index"): /WEB-INF/views/posts/index.jsp
* </pre>
* @param action action name
* @return path to a view file
*/
public static String viewPath(String action) {
return viewPath(getController(), action);
}
/**
* Returns URI path to a view template of a controller's action.
* <pre>
* Examples:
* viewPath("posts", "show") : /WEB-INF/views/posts/show.jsp
* viewPath("posts", "index"): /WEB-INF/views/posts/index.jsp
* </pre>
*
* @param controller controller name
* @param action action name
* @return path to a view file
*/
public static String viewPath(String controller, String action) {
return EnvConfig.getViewURI(controller, action);
}
/**
* Returns model class name related to the underlying controller class name
* based on naming convention.
*
* @return string model class name
*/
public static String getModelClassName(Class<?> controllerClass) {
return EnvConfig.getInstance().getModelClassNameFromControllerClassName(controllerClass.getName());
}
/**
* Returns a paginator for the underlying model.
*
* <p>
* Paging control options are constructed based on URL parameters.
* Therefore in URL, there must be parameters like <tt>limit</tt>,
* <tt>offset</tt>, <tt>npage</tt>, etc.</p>
*
* <p>
* For a complete list of paging options, see description of
* {@link com.scooterframework.orm.misc.Paginator} class.</p>
*
* @param modelClass the model class
* @return Paginator instance
*/
public static Paginator jdbcPaginator(Class<? extends ActiveRecord> modelClass) {
return jdbcPaginator(modelClass, (Map<String, String>)null, (Map<String, String>)null);
}
/**
* Returns a paginator for the underlying model.
*
* <p>
* For a complete list of paging options, see description of
* {@link com.scooterframework.orm.misc.Paginator} class.</p>
*
* @param modelClass the model class
* @param pagingOptions paging control options string
* @return Paginator instance
*/
public static Paginator jdbcPaginator(Class<? extends ActiveRecord> modelClass, String pagingOptions) {
return jdbcPaginator(modelClass, Converters.convertStringToMap(pagingOptions), (Map<String, String>)null);
}
/**
* Returns a paginator for the underlying model.
*
* <p>
* For a complete list of paging options, see description of
* {@link com.scooterframework.orm.misc.Paginator} class.</p>
*
* <p>
* For a complete list of SQL options, see description of <tt>options</tt>
* in {@link com.scooterframework.orm.activerecord.ActiveRecord}
* class.</p>
*
* @param modelClass the model class
* @param pagingOptions paging control options string
* @param sqlOptions SQL options string
* @return Paginator instance
*/
public static Paginator jdbcPaginator(Class<? extends ActiveRecord> modelClass, String pagingOptions, String sqlOptions) {
return jdbcPaginator(modelClass, Converters.convertStringToMap(pagingOptions), Converters.convertSqlOptionStringToMap(sqlOptions));
}
/**
* Returns a paginator for the underlying model.
*
* <p>
* For a complete list of paging options, see description of
* {@link com.scooterframework.orm.misc.Paginator} class.</p>
*
* <p>
* For a complete list of SQL options, see description of <tt>options</tt>
* in {@link com.scooterframework.orm.activerecord.ActiveRecord}
* class.</p>
*
* @param modelClass the model class
* @param pagingOptions paging control options string
* @param sqlOptions SQL options Map
* @return Paginator instance
*/
public static Paginator jdbcPaginator(Class<? extends ActiveRecord> modelClass, String pagingOptions, Map<String, String> sqlOptions) {
return jdbcPaginator(modelClass, Converters.convertStringToMap(pagingOptions), sqlOptions);
}
/**
* Returns a paginator for the underlying model.
*
* <p>
* For a complete list of paging options, see description of
* {@link com.scooterframework.orm.misc.Paginator} class.</p>
*
* @param modelClass the model class
* @param pagingOptions paging control options Map
* @return Paginator instance
*/
public static Paginator jdbcPaginator(Class<? extends ActiveRecord> modelClass, Map<String, ?> pagingOptions) {
return jdbcPaginator(modelClass, Converters.convertMapToMapSS(pagingOptions), (Map<String, String>)null);
}
/**
* Returns a paginator for the underlying model.
*
* <p>
* For a complete list of paging options, see description of
* {@link com.scooterframework.orm.misc.Paginator} class.</p>
*
* <p>
* For a complete list of SQL options, see description of <tt>options</tt>
* in {@link com.scooterframework.orm.activerecord.ActiveRecord}
* class.</p>
*
* @param modelClass the model class
* @param pagingOptions paging control options Map
* @param sqlOptions SQL options string
* @return Paginator instance
*/
public static Paginator jdbcPaginator(Class<? extends ActiveRecord> modelClass, Map<String, String> pagingOptions, String sqlOptions) {
return jdbcPaginator(modelClass, pagingOptions, Converters.convertSqlOptionStringToMap(sqlOptions));
}
/**
* Returns a paginator for the underlying model.
*
* <p>
* For a complete list of paging options, see description of
* {@link com.scooterframework.orm.misc.Paginator} class.</p>
*
* <p>
* For a complete list of SQL options, see description of <tt>options</tt>
* in {@link com.scooterframework.orm.activerecord.ActiveRecord}
* class.</p>
*
* @param modelClass the model class
* @param pagingOptions paging control options Map
* @param sqlOptions SQL options Map
* @return Paginator instance
*/
public static Paginator jdbcPaginator(Class<? extends ActiveRecord> modelClass, Map<String, String> pagingOptions, Map<String, String> sqlOptions) {
Map<String, String> pagingOptionsMerged = Converters.convertMapToMapSS(ACH.getAC().getParameterDataAsMap());
if (pagingOptions != null) {
pagingOptionsMerged.putAll(pagingOptions);
}
return new Paginator(new JdbcPageListSource(modelClass, sqlOptions), pagingOptionsMerged);
}
/**
* Returns all request parameters as a map. This includes data in both
* parameter scope and request scope.
*
* @return a map of all request parameters
*/
public static Map<String, Object> params() {
return ACH.getAC().getAllRequestDataAsMap();
}
/**
* Returns all request parameters as a map for those keys that have a
* prefix.
*
* @param keyPrefix
* @return a map of request parameters
*/
public static Map<String, Object> paramsWithPrefix(String keyPrefix) {
return ACH.getAC().getAllRequestDataAsMap(keyPrefix);
}
/**
* Returns a value corresponding to a key in request parameters or request
* attributes.
*
* @param key name of a key in the request parameters
* @return a value corresponding to a key in the request parameters
*/
public static String params(String key) {
Object o = ACH.getAC().getFromAllRequestData(key);
return Util.getSafeString(o);
}
/**
* Alias of method <tt>params(String key)</tt>.
*
* Returns a value corresponding to a key in request parameters or request
* attributes.
*
* @param key name of a key in the request parameters
* @return a value corresponding to a key in the request parameters
*/
public static String p(String key) {
return params(key);
}
/**
* Returns a string array corresponding to a key in request parameters or
* request attributes.
*
* @param key name of a key in the request parameters
* @return a string array corresponding to a key in the request parameters
*/
public static String[] pArray(String key) {
Object o = ACH.getAC().getFromAllRequestData(key);
if (o == null) return new String[0];
String[] valueAry = null;
if (o instanceof Object[]) {
valueAry = (String[])o;
}
else {
valueAry = new String[1];
valueAry[0] = (String)o;
}
return valueAry;
}
/**
* Returns a date instance corresponding to a key in request parameters or
* request attributes.
*
* @param key name of a key in the request parameters
* @return a date instance corresponding to a key in the request parameters
*/
public static Date pDate(String key) {
return DateUtil.parseDate(p(key));
}
/**
* Returns a date instance corresponding to a key in request parameters or
* request attributes.
*
* @param key name of a key in the request parameters
* @param pattern the pattern describing the date and time format
* @return a date instance corresponding to a key in the request parameters
*/
public static Date pDate(String key, String pattern) {
return DateUtil.parseDate(p(key), pattern);
}
/**
* Returns true or false corresponding to a key in request parameters or
* request attributes.
*
* Only string of "true"--regardless of case--would return <tt>true</tt>.
*
* @param key name of a key in the request parameters
* @return true or false corresponding to a key in the request parameters
*/
public static Boolean pBoolean(String key) {
return Boolean.valueOf(p(key));
}
/**
* Checks if this is an AJAX request.
*
* @return true if this is an AJAX request.
*/
public static boolean isAjaxRequest() {
return (params(Constants.AJAX_REQUEST) != null)?true:false;
}
/**
* Checks if this is a file-upload request or multipart request.
*
* @return true if this is a file-upload request or multipart request.
*/
public static boolean isFileUploadRequest() {
return ServletFileUpload.isMultipartContent(ACH.getWAC().getHttpServletRequest());
}
/**
* Alias of method <tt>getUploadedFile(String key)</tt>.
*
* Returns an upload file which is an instance of <tt>UploadFile</tt>.
*
* @return an instance of <tt>UploadFile</tt>
* @throws Exception
*/
public static UploadFile paramsFile(String key) throws Exception {
return getUploadFile(key);
}
/**
* Alias of method <tt>getUploadFiles()</tt>.
*
* Returns a list of upload files.
*
* @return a list of <tt>UploadFile</tt> instances
* @throws Exception
*/
public static List<UploadFile> paramsFiles() throws Exception {
return getUploadFiles();
}
/**
* Alias of method <tt>getUploadFilesMap()</tt>.
*
* Returns a map of upload files. In each key/value pair, the key is the
* field name in the HTTP form, and the value is a <tt>UploadFile</tt> instance.
*
* @return a map of field/upload file (UploadFile) pairs
* @throws Exception
*/
public static Map<String, UploadFile> paramsFilesMap() throws Exception {
return getUploadFilesMap();
}
/**
* Alias of method <tt>paramsFile(String key)</tt>.
*
* Returns an upload file which is an instance of <tt>UploadFile</tt>.
*
* @return an instance of <tt>UploadFile</tt>
* @throws Exception
*/
public static UploadFile pFile(String key) throws Exception {
return paramsFile(key);
}
/**
* Alias of method <tt>paramsFiles()</tt>.
* Returns a list of upload files.
*
* @return a list of <tt>UploadFile</tt> instances
* @throws Exception
*/
public static List<UploadFile> pFiles() throws Exception {
return paramsFiles();
}
/**
* Alias of method <tt>paramsFilesMap()</tt>.
*
* Returns a map of upload files. In each key/value pair, the key is the
* field name in the HTTP form, and the value is a <tt>UploadFile</tt> instance.
*
* @return a map of field/upload file (UploadFile) pairs
* @throws Exception
*/
public static Map<String, UploadFile> pFilesMap() throws Exception {
return paramsFilesMap();
}
/**
* Returns a value corresponding to a key in the request parameters. The
* case of the key string is ignored.
*
* @param key name of a key in the request parameters
* @return a value corresponding to a key in the request parameters
*/
public static String paramsIgnoreCase(String key) {
Object o = ACH.getAC().getFromParameterDataIgnoreCase(key);
return Util.getSafeString(o);
}
/**
* Returns field values of a route.
*
* @return field values of a route.
*/
public static Map<String, String> routeFieldValues() {
return CurrentThreadCacheClient.fieldValues();
}
/**
* Returns value of a field for route.
*
* @param field the field
* @return value of a field for route.
*/
public static String routeFieldValue(String field) {
return routeFieldValues().get(field);
}
public static Object getFromThreadData(String key) {
return ActionContext.getFromThreadData(key);
}
public static Object getFromParameterData(String key) {
return ACH.getAC().getFromParameterData(key);
}
public static Object getFromRequestData(String key) {
return ACH.getAC().getFromRequestData(key);
}
public static Object getFromSessionData(String key) {
return ACH.getAC().getFromSessionData(key);
}
public static Object getFromContextData(String key) {
return ACH.getAC().getFromContextData(key);
}
public static Object getFromGlobalData(String key) {
return ActionContext.getFromGlobalData(key);
}
public static void storeToThread(String key, Object obj) {
ActionContext.storeToThread(key, obj);
}
public static void storeToRequest(String key, Object obj) {
ACH.getAC().storeToRequest(key, obj);
}
public static void storeToSession(String key, Object obj) {
ACH.getAC().storeToSession(key, obj);
}
public static void storeToContext(String key, Object obj) {
ACH.getAC().storeToContext(key, obj);
}
public static void storeToGlobal(String key, Object obj) {
ActionContext.storeToGlobal(key, obj);
}
public static void removeFromThreadData(String key) {
ActionContext.removeFromThreadData(key);
}
public static void removeFromRequestData(String key) {
ACH.getAC().removeFromRequestData(key);
}
public static void removeFromSessionData(String key) {
ACH.getAC().removeFromSessionData(key);
}
public static void removeFromContextData(String key) {
ACH.getAC().removeFromContextData(key);
}
public static void removeFromGlobalData(String key) {
ActionContext.removeFromGlobalData(key);
}
public static void remove(String key) {
ACH.getAC().remove(key);
}
public static void removeAllSessionData() {
ACH.getAC().removeAllSessionData();
}
/**
* Binds data to view attribute for view rendering.
*
* @param key a string representing a place holder on view
* @param data the data value to be filled in the view
*/
public static void setViewData(String key, Object data) {
storeToRequest(key, data);
}
/**
* Returns the current HTTP Servlet request instance.
*/
public static HttpServletRequest getHttpServletRequest() {
return ACH.getWAC().getHttpServletRequest();
}
/**
* Returns the current HTTP Servlet response instance.
*/
public static HttpServletResponse getHttpServletResponse() {
return ACH.getWAC().getHttpServletResponse();
}
/**
* Returns the HTTP ServletContext instance.
*/
public static ServletContext getServletContext() {
return ACH.getWAC().getHttpServletRequest().getSession().getServletContext();
}
/**
* Returns content type of HTTP request.
*/
public static String getHttpRequestContentType() {
return ACH.getWAC().getHttpServletRequest().getContentType();
}
/**
* Records a flash message of a specific <tt>type</tt>. The message can be
* either a sentence or a message key in a messages.properties file.
*
* @param type flash message type
* @param message the message or message key
*/
public static void flash(String type, String message) {
Flash.flash(type, message);
}
/**
* Records a flash message of a specific <tt>type</tt>. The message can be
* either a sentence or a message key in a messages.properties file.
*
* @param type flash message type
* @param message the message or message key
* @param value a value that can be used in the message
*/
public static void flash(String type, String message, Object value) {
Flash.flash(type, message, value);
}
/**
* Records a flash message of a specific <tt>type</tt>. The message can be
* either a sentence or a message key in a messages.properties file.
*
* @param type flash message type
* @param message the message or message key
* @param value0 a value that can be used in the message
* @param value1 a value that can be used in the message
*/
public static void flash(String type, String message, Object value0, Object value1) {
Flash.flash(type, message, value0, value1);
}
/**
* Records a flash message of a specific <tt>type</tt>. The message can be
* either a sentence or a message key in a messages.properties file.
*
* @param type flash message type
* @param message the message or message key
* @param value0 a value that can be used in the message
* @param value1 a value that can be used in the message
* @param value2 a value that can be used in the message
*/
public static void flash(String type, String message, Object value0, Object value1, Object value2) {
Flash.flash(type, message, value0, value1, value2);
}
/**
* Records a flash message of a specific <tt>type</tt>. The <tt>message</tt>
* is of type {@link com.scooterframework.common.util.Message} or its subclass.
*
* @param type flash message type
* @param message a {@link com.scooterframework.common.util.Message} object
*/
public static void flash(String type, Message message) {
Flash.flash(type, message);
}
/**
* Records a flash message of <tt>error</tt> type. The message can be
* either a sentence or a message key in a messages.properties file.
*
* @param message the message or message key
*/
public static void flashError(String message) {
Flash.error(message);
}
/**
* Records a flash message of <tt>error</tt> type. The message can be
* either a sentence or a message key in a messages.properties file.
*
* @param message the message or message key
* @param value a value that can be used in the message
*/
public static void flashError(String message, Object value) {
Flash.error(message, value);
}
/**
* Records a flash message of <tt>error</tt> type. The message can be
* either a sentence or a message key in a messages.properties file.
*
* @param message the message or message key
* @param value0 a value that can be used in the message
* @param value1 a value that can be used in the message
*/
public static void flashError(String message, Object value0, Object value1) {
Flash.error(message, value0, value1);
}
/**
* Records a flash message of <tt>error</tt> type. The message can be
* either a sentence or a message key in a messages.properties file.
*
* @param message the message or message key
* @param value0 a value that can be used in the message
* @param value1 a value that can be used in the message
* @param value2 a value that can be used in the message
*/
public static void flashError(String message, Object value0, Object value1, Object value2) {
Flash.error(message, value0, value1, value2);
}
/**
* Records a flash message of <tt>error</tt> type. The <tt>message</tt>
* is of type {@link com.scooterframework.common.util.Message} or its subclass.
*
* @param message a {@link com.scooterframework.common.util.Message} object
*/
public static void flashError(Message message) {
Flash.error(message);
}
/**
* Records a flash message of <tt>notice</tt> type. The message can be
* either a sentence or a message key in a messages.properties file.
*
* @param message the message or message key
*/
public static void flashNotice(String message) {
Flash.notice(message);
}
/**
* Records a flash message of <tt>notice</tt> type. The message can be
* either a sentence or a message key in a messages.properties file.
*
* @param message the message or message key
* @param value a value that can be used in the message
*/
public static void flashNotice(String message, Object value) {
Flash.notice(message, value);
}
/**
* Records a flash message of <tt>notice</tt> type. The message can be
* either a sentence or a message key in a messages.properties file.
*
* @param message the message or message key
* @param value0 a value that can be used in the message
* @param value1 a value that can be used in the message
*/
public static void flashNotice(String message, Object value0, Object value1) {
Flash.notice(message, value0, value1);
}
/**
* Records a flash message of <tt>notice</tt> type. The message can be
* either a sentence or a message key in a messages.properties file.
*
* @param message the message or message key
* @param value0 a value that can be used in the message
* @param value1 a value that can be used in the message
* @param value2 a value that can be used in the message
*/
public static void flashNotice(String message, Object value0, Object value1, Object value2) {
Flash.notice(message, value0, value1, value2);
}
/**
* Records a flash message of <tt>notice</tt> type. The <tt>message</tt>
* is of type {@link com.scooterframework.common.util.Message} or its subclass.
*
* @param message a {@link com.scooterframework.common.util.Message} object
*/
public static void flashNotice(Message message) {
Flash.notice(message);
}
/**
* Returns an instance of Validators.
*
* <p>
* Subclass must override this method if a different validator is used.
* </p>
*
* @return Validators object
*/
public static ActionValidators validators() {
ActionValidators validators = (ActionValidators)CurrentThreadCache.get(KEY_Validators);
if (validators == null) {
validators = new ActionValidators();
CurrentThreadCache.set(KEY_Validators, validators);
}
return validators;
}
private static final String KEY_Validators = "key.Validators";
private static final String KEY_ValidationResults = "key.ValidationResults";
/**
* Returns controller validation results.
*/
public static ValidationResults currentValidationResults() {
ValidationResults vr = (ValidationResults)CurrentThreadCache.get(KEY_ValidationResults);
if (vr == null) {
vr = new ValidationResults();
CurrentThreadCache.set(KEY_ValidationResults, vr);
}
return vr;
}
/**
* Cleans up cached ValidationResults.
*/
public static void cleanupValidationResults() {
CurrentThreadCache.set(KEY_ValidationResults, null);
}
/**
* Checks if validation failed.
*
* @return true if validation is failed, false otherwise.
*/
public static boolean validationFailed() {
return currentValidationResults().failed();
}
/**
* Returns an error-tagged content string for error output.
*
* @param content the error content
* @return a error-tagged content string
*/
public static String error(String content) {
return ActionResult.error(content);
}
/**
* Returns a html-tagged content string for html output.
*
* @param content the html content
* @return a html-tagged content string
*/
public static String html(String content) {
return ActionResult.html(content);
}
/**
* Returns a text-tagged content string for plain-text output.
*
* @param content the text content
* @return a text-tagged content string
*/
public static String text(String content) {
return ActionResult.text(content);
}
/**
* Returns a xml-tagged content string for xml output.
*
* @param content the xml content
* @return a xml-tagged content string
*/
public static String xml(String content) {
return ActionResult.xml(content);
}
/**
* Returns a forward-tagged URI string with a query string. The query string is
* formed by listing all primary key and value pairs of the record instance.
*
* @param uri an URI string
* @param record an ActiveRecord instance
* @return a formatted forward-tagged URI string
*/
public static String forwardTo(String uri, ActiveRecord record) {
return ActionResult.forwardTo(uri, record);
}
/**
* Returns a forward-tagged URI string with a query string.
*
* @param uri an URI string
* @param nameValuePairs a map of name and value pairs as HTTP query string
* @return a formatted forward-tagged URI string
*/
public static String forwardTo(String uri, Map<String, Object> nameValuePairs) {
return ActionResult.forwardTo(uri, nameValuePairs);
}
/**
* Returns a forward-tagged URI string with a query string.
*
* @param uri an URI string
* @param nameValuePairs a string of name and value pairs as HTTP query string
* @return a formatted forward-tagged URI string
*/
public static String forwardTo(String uri, String nameValuePairs) {
return ActionResult.forwardTo(uri, nameValuePairs);
}
/**
* Returns a forward-tagged URI string.
*
* @param uri an URI string
* @return a formatted forward-tagged URI string
*/
public static String forwardTo(String uri) {
return ActionResult.forwardTo(uri);
}
/**
* Returns a redirect-tagged URI string with a query string. The query string is
* formed by listing all primary key and value pairs of the record instance.
*
* @param uri an URI string
* @param record an ActiveRecord instance
* @return a formatted redirect-tagged URI string
*/
public static String redirectTo(String uri, ActiveRecord record) {
return ActionResult.redirectTo(uri, record);
}
/**
* Returns a redirect-tagged URI string with a query string.
*
* @param uri an URI string
* @param nameValuePairs a map of name and value pairs as HTTP query string
* @return a formatted redirect-tagged URI string
*/
public static String redirectTo(String uri, Map<String, Object> nameValuePairs) {
return ActionResult.redirectTo(uri, nameValuePairs);
}
/**
* Returns a redirect-tagged URI string with a query string.
*
* @param uri an URI string
* @param nameValuePairs a string of name and value pairs as HTTP query string
* @return a formatted redirect-tagged URI string
*/
public static String redirectTo(String uri, String nameValuePairs) {
return ActionResult.redirectTo(uri, nameValuePairs);
}
/**
* Returns a redirect-tagged URI string.
*
* @param uri an URI string
* @return a formatted redirect-tagged URI string
*/
public static String redirectTo(String uri) {
return ActionResult.redirectTo(uri);
}
/**
* Returns root path to the application.
*/
public static String applicationPath() {
return ApplicationConfig.getInstance().getApplicationPath();
}
/**
* Returns a list of upload files. Each item in the list is an instance
* of <tt>java.io.File</tt> instance.
*
* @param fileRepository
* @return a list of upload files (File)
* @throws Exception
*/
public static List<File> getUploadFilesAsFiles(String fileRepository) throws Exception {
List<File> files = new ArrayList<File>();
List<FileItem> items = prepareFileItems();
for (FileItem item : items) {
if (!item.isFormField() && !"".equals(item.getName())) {
File f = new File(fileRepository + File.separator + item.getName());
files.add(f);
}
}
return files;
}
/**
* Returns a list of upload files. Each item in the list is an instance
* of <tt>UploadFile</tt> instance.
*
* @return a list of upload files (UploadFile)
* @throws Exception
*/
public static List<UploadFile> getUploadFiles() throws Exception {
List<UploadFile> files = new ArrayList<UploadFile>();
List<FileItem> items = prepareFileItems();
for (FileItem item : items) {
if (!item.isFormField() && !"".equals(item.getName())) {
files.add(new UploadFile(item));
}
}
return files;
}
/**
* Returns a map of upload files. In each key/value pair, the key is the
* field name in the HTTP form, and the value is a <tt>UploadFile</tt> instance.
*
* @return a map of field/upload file (UploadFile) pairs
* @throws Exception
*/
public static Map<String, UploadFile> getUploadFilesMap() throws Exception {
Map<String, UploadFile> files = new HashMap<String, UploadFile>();
List<FileItem> items = prepareFileItems();
for (FileItem item : items) {
if (!item.isFormField() && !"".equals(item.getName())) {
files.put(item.getFieldName(), new UploadFile(item));
}
}
return files;
}
/**
* Returns an upload file which is an instance of <tt>UploadFile</tt>.
*
* @return an instance of <tt>UploadFile</tt>
* @throws Exception
*/
public static UploadFile getUploadFile(String key) throws Exception {
return (UploadFile)getUploadFilesMap().get(key);
}
@SuppressWarnings("unchecked")
private static List<FileItem> prepareFileItems() throws Exception {
List<FileItem> files = (List<FileItem>)CurrentThreadCache.get(Constants.FILE_UPLOAD_REQUEST_FILES);
return files;
}
/**
* Renders content. The format of the response is derived from request
* extension. The default format is <tt>html</tt> which is defined by
* {@link com.scooterframework.admin.Constants#DEFAULT_RESPONSE_FORMAT}.
*
* @param content The content to be sent.
* @return a token of render
*/
public static String render(Object content) {
return render(content, format());
}
/**
* Renders content associated with a specific request format.
*
* The default format is <tt>html</tt> which is defined by
* {@link com.scooterframework.admin.Constants#DEFAULT_RESPONSE_FORMAT}.
*
* @param content The content to be sent.
* @param format The request format.
* @return a token of render
*/
public static String render(Object content, String format) {
if (content == null) {
log.warn("No rendering for a null content with format " + format);
getHttpServletRequest().setAttribute(Constants.REQUEST_RENDERED, "true");
return null;
}
if (format == null) {
format = Constants.DEFAULT_RESPONSE_FORMAT;
}
ContentHandler handler = ContentHandlerFactory.getContentHandler(format);
if (handler != null) {
try {
handler.handle(
getHttpServletRequest(),
getHttpServletResponse(),
content,
format);
getHttpServletRequest()
.setAttribute(Constants.REQUEST_RENDERED, "true");
} catch (Exception ex) {
String error = "Error in render() for format \""
+ format + "\", because " + ex.getMessage();
throw new RuntimeException(error);
}
}
else {
throw new IllegalArgumentException(
"There is no handler found for format \""
+ format
+ "\". You may create your own as a plugin by "
+ "extending the Plugin class and "
+ "implementing the ContentHandler interface.");
}
return ActionResult.TAG_RENDER;
}
/**
* Publishes a file for display.
*
* @param file The file to be published.
*/
public static void publishFile(File file) {
publishFile(file, file.getName(), false);
}
/**
* Publishes a file for display.
*
* @param file The file to be published.
* @param mimeType The content MIME type.
*/
public static void publishFile(File file, String mimeType) {
publishFile(file, file.getName(), mimeType, false);
}
/**
* Publishes a file. Its MIME type is detected from the file extension.
*
* <p>
* The file extension detection rule is as follows:
* 1. If the <tt>file</tt> input has an extension, use it. Otherwise,
* 2. If the <tt>displayableName</tt> input has an extension, use it.
*
* @param file The file to be published.
* @param displayableName The display name of the file in the download dialog.
* @param forDownload indicates whether this is for file download or display.
*/
public static void publishFile(File file, String displayableName, boolean forDownload) {
String extension = null;
String fn = file.getName();
if (fn != null && !"".equals(fn) && fn.indexOf('.') != -1) {
extension = fn.substring(fn.lastIndexOf('.') + 1);
}
else if (displayableName != null && displayableName.indexOf('.') != -1) {
extension = displayableName.substring(displayableName.lastIndexOf('.') + 1);
}
String mimeType = "";
if (extension != null && !"".equals(extension)) {
mimeType = EnvConfig.getInstance().getMimeType(extension);
}
publishFile(file, displayableName, mimeType, forDownload);
}
/**
* Publishes a file. If the <tt>mimeType</tt> is empty, it is default to
* <tt>application/octet-stream</tt>.
*
* @param file The file to be published.
* @param displayableName The display name of the file in the download dialog.
* @param mimeType The content MIME type.
* @param forDownload indicates whether this is for file download or display.
*/
public static void publishFile(File file, String displayableName, String mimeType, boolean forDownload) {
try {
ContentHandlerHelper.publish(ACH.getWAC().getHttpServletResponse(), file, displayableName, mimeType, forDownload);
ACH.getWAC().getHttpServletRequest()
.setAttribute(Constants.REQUEST_RENDERED, "true");
} catch (Exception ex) {
String error = "Error in publishFile() for file \""
+ file + "\", because " + ex.getMessage();
throw new RuntimeException(error);
}
}
/**
* Checks whether a file is a text file.
*
* @param file the file to check
* @return true if it is a text file.
*/
public static boolean isTextFile(File file) {
return EnvConfig.getInstance().isTextFile(file);
}
/**
* Returns the file extension.
*
* @param file the file to check
* @return file extension or null
*/
public static String getFileExtension(File file) {
String fName = file.getName();
int lastDot = fName.lastIndexOf('.');
return (lastDot != -1)?(fName.substring(lastDot + 1)):null;
}
/**
* Returns all view data as a map. This includes data in both
* parameter scope and request scope.
*
* @return a map of all view data
*/
public static Map<String, Object> getViewDataMap() {
return params();
}
/**
* Renders a view template file with all properties in the container.
*
* The file extension of the view template is used to look up related
* template engine. If the <tt>view</tt> does not have an extension
* specified, the default extension is defined by <tt>view.extension</tt>
* property in the <tt>environment.properties</tt> file.
*
* <p>Examples: </p>
* <pre>
* //render view file show.jsp
* renderView("show");
*
* //render view .../WEB-INF/views/products/show.jsp
* renderView("products/show");
*
* //render view file /home/foo/templates/show.st with StringTemplate engine
* renderView("/home/foo/templates/show.st");
*
* //render view file show.jsp and return result in text format
* renderView("show", "text");
* </pre>
*
* @param view The render template file
* @return rendered content
*/
public static String renderView(String view) {
return renderView(view, getViewDataMap());
}
/**
* Renders a view template file with all properties in the container.
*
* The file extension of the view template is used to look up related
* template engine. If the <tt>view</tt> does not have an extension
* specified, the default extension is defined by <tt>view.extension</tt>
* property in the <tt>environment.properties</tt> file.
*
* <p>Examples: </p>
* <pre>
* //render view file show.jsp
* renderView("show");
*
* //render view .../WEB-INF/views/products/show.jsp
* renderView("products/show");
*
* //render view file /home/foo/templates/show.st with StringTemplate engine
* renderView("/home/foo/templates/show.st");
*
* //render view file show.jsp and return result in text format
* renderView("show", "text");
* </pre>
*
* <p>The response <tt>format</tt> does not apply to <tt>jsp</tt> views.</p>
*
* @param view The render template file
* @param format the response format
* @return rendered content
*/
public static String renderView(String view, String format) {
return renderView(view, format, getViewDataMap());
}
/**
* Renders a view template file with view data in <tt>viewDataMap</tt>.
*
* The file extension of the view template is used to look up related
* template engine. If the <tt>view</tt> does not have an extension
* specified, the default extension is defined by <tt>view.extension</tt>
* property in the <tt>environment.properties</tt> file.
*
* @param view the render template file
* @param viewDataMap data (name/value pairs) to be passed to the view
* @return rendered content
*/
public static String renderView(String view, Map<String, Object> viewDataMap) {
return renderView(view, format(), viewDataMap);
}
/**
* Renders a view template file with view data in <tt>viewDataMap</tt>.
*
* The file extension of the view template is used to look up related
* template engine. If the <tt>view</tt> does not have an extension
* specified, the default extension is defined by <tt>view.extension</tt>
* property in the <tt>environment.properties</tt> file.
*
* <p>Examples:</p>
* <pre>
* //return a view coded in String Template as html
* return renderView("paged_list.st", "html", map);
*
* //return a view coded in FreeMarker Template as text
* return renderView("paged_list.ftl", "text", map);
* </pre>
*
* @param view the render template file
* @param format response format of the render template file
* @param viewDataMap data (name/value pairs) to be passed to the view
* @return rendered content
*/
public static String renderView(String view, String format, Map<String, Object> viewDataMap) {
if (view == null)
throw new IllegalArgumentException("View can not be null for rendering.");
if (format == null) {
format = Constants.DEFAULT_RESPONSE_FORMAT;
}
String viewPath = view;
String viewExtension = "";
String viewFile = "";
int lastDot = view.lastIndexOf('.');
if (lastDot != -1) {
viewExtension = view.substring(lastDot + 1);
}
else {
viewExtension = EnvConfig.getInstance().getViewExtension();
viewPath = (viewExtension.startsWith("."))?
(view + viewExtension):(view + '.' + viewExtension);
}
int lastSlash = viewPath.lastIndexOf('/');
if (lastSlash == -1 && File.separatorChar != '/') {
lastSlash = viewPath.lastIndexOf(File.separatorChar);
}
if (lastSlash != -1) {
File p = new File(viewPath);
if (!p.exists()) {
viewPath = viewPath("", viewPath);
viewFile = applicationPath() + viewPath;
}
}
else {
viewPath = viewPath(viewPath);
viewFile = applicationPath() + viewPath;
}
//handle view
try {
HttpServletRequest request = getHttpServletRequest();
HttpServletResponse response = getHttpServletResponse();
if (viewExtension.equalsIgnoreCase("jsp") || viewExtension.equalsIgnoreCase(".jsp")) {
doForward(viewPath, request, response);
}
else {
TemplateHandler handler =
TemplateHandlerFactory.getTemplateHandler(viewExtension);
if (handler == null)
throw new IllegalArgumentException("There is no " +
"template handler found for view template " +
"of type \"" + viewExtension + "\".");
String result = handler.handle(new File(viewFile), viewDataMap);
render(result, format);
}
request.setAttribute(Constants.REQUEST_RENDERED, "true");
} catch (Exception ex) {
String errorMessage = "Failed to render view \"" + viewFile + "\" because " + ex.getMessage();
log.error(errorMessage, ex);
throw new NoTemplateHandlerException(errorMessage, viewExtension);
}
return null;
}
/**
* <p>Do a forward to specified URI using a <tt>RequestDispatcher</tt>.
* This method is used by all methods needing to do a forward.</p>
*
* @param uri Context-relative URI to forward to
* @param request HTTP servlet request
* @param response HTTP servlet response
* @throws java.io.IOException
* @throws javax.servlet.ServletException
*/
public static void doForward(
String uri,
HttpServletRequest request,
HttpServletResponse response)
throws IOException, ServletException {
log.debug("doForward: " + uri);
if (uri == null) return;
if (uri != null && !uri.startsWith("/")) uri = "/" + uri;
RequestDispatcher rd = request.getSession().getServletContext().getRequestDispatcher(uri);
if (rd == null) {
uri = "/WEB-INF/views/404.jsp";
log.error("Unable to locate \"" + uri + "\", forward to " + uri);
rd = getServletContext().getRequestDispatcher(uri);
}
rd.forward(request, response);
}
/**
* Returns the FilterManager for a class type.
*/
public static FilterManager filterManagerFor(Class<?> clazz) {
return FilterManagerFactory.getInstance().getFilterManager(clazz);
}
/**
* Adds color to a string.
*/
public static String color(String s, String color) {
return String.format("<font color=\"%s\">%s</font>", color, s);
}
}