/*
* Copyright 2004-2010 the Seasar Foundation and the Others.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific language
* governing permissions and limitations under the License.
*/
package org.slim3.controller;
import java.io.BufferedOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URLConnection;
import java.net.URLEncoder;
import java.text.DecimalFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import org.slim3.controller.upload.FileUpload;
import org.slim3.controller.validator.Errors;
import org.slim3.util.AppEngineUtil;
import org.slim3.util.BooleanUtil;
import org.slim3.util.DateUtil;
import org.slim3.util.DoubleUtil;
import org.slim3.util.FloatUtil;
import org.slim3.util.IntegerUtil;
import org.slim3.util.LongUtil;
import org.slim3.util.NumberUtil;
import org.slim3.util.RequestMap;
import org.slim3.util.ShortUtil;
import org.slim3.util.StringUtil;
import org.slim3.util.ThrowableUtil;
import com.google.appengine.api.datastore.Key;
import com.google.appengine.api.datastore.KeyFactory;
/**
* A base controller. This controller is created each request.
*
* @author higa
* @since 1.0.0
*
*/
public abstract class Controller {
/**
* The buffer size.
*/
protected static final int BUFFER_SIZE = 1024;
private static final Logger logger =
Logger.getLogger(Controller.class.getName());
/**
* The servlet context.
*/
protected ServletContext servletContext;
/**
* The request.
*/
protected HttpServletRequest request;
/**
* The response.
*/
protected HttpServletResponse response;
/**
* The base path.
*/
protected String basePath;
/**
* The error messages.
*/
protected Errors errors;
/**
* Runs the bare controller process.
*
* @return the navigation
* @throws Throwable
* if an {@link Throwable} occurred
*/
public Navigation runBare() throws Throwable {
Navigation navigation = null;
Throwable error = null;
navigation = setUp();
if (navigation != null) {
return navigation;
}
try {
navigation = run();
} catch (Throwable t) {
error = t;
} finally {
try {
tearDown();
} catch (Throwable t) {
if (logger.isLoggable(Level.WARNING)) {
logger.log(Level.WARNING, t.getMessage(), t);
}
}
}
if (error != null) {
navigation = handleError(error);
}
return navigation;
}
/**
* Sets up the this controller. This method is called before "run" method is
* called.
*
* @return the navigation
*/
protected Navigation setUp() {
return null;
}
/**
* Override to run this controller
*
* @return the navigation
* @throws Exception
* if an {@link Exception} occurred
*/
protected abstract Navigation run() throws Exception;
/**
* Tears down this controller. This method is called after "run" method is
* called.
*
*/
protected void tearDown() {
}
/**
* Handles the error.
*
* @param error
* the error
* @return the navigation.
* @throws Throwable
* if an {@link Throwable} occurred
*/
protected Navigation handleError(Throwable error) throws Throwable {
throw error;
}
/**
* Creates a new {@link Navigation} for "forward".
*
* @param path
* the controller-relative path
* @return a new {@link Navigation}
*/
protected Navigation forward(String path) {
return new Navigation(path, false);
}
/**
* Creates a new {@link Navigation} for "redirect".
*
* @param path
* the controller-relative path
* @return a new {@link Navigation}
*/
protected Navigation redirect(String path) {
return new Navigation(path, true);
}
/**
* Returns the request parameter.
*
* @param name
* the parameter name
* @return the parameter value
* @throws NullPointerException
* if the name parameter is null
*/
protected String param(CharSequence name) throws NullPointerException {
if (name == null) {
throw new NullPointerException(
"The name parameter must not be null.");
}
return request.getParameter(name.toString());
}
/**
* Returns the request parameter.
*
* @param name
* the parameter name
* @return the parameter value
* @throws NullPointerException
* if the name parameter is null
*/
protected String[] paramValues(CharSequence name)
throws NullPointerException {
if (name == null) {
throw new NullPointerException(
"The name parameter must not be null.");
}
return request.getParameterValues(name.toString());
}
/**
* Returns the request attribute.
*
* @param <T>
* the return type
* @param name
* the attribute name
* @return the request attribute
* @throws NullPointerException
* if the name parameter is null
*/
@SuppressWarnings("unchecked")
protected <T> T requestScope(CharSequence name) throws NullPointerException {
if (name == null) {
throw new NullPointerException(
"The name parameter must not be null.");
}
return (T) request.getAttribute(name.toString());
}
/**
* Returns the request attribute value as string.
*
* @param name
* the attribute name
* @return the string attribute value
* @throws NullPointerException
* if the name parameter is null
*/
protected String asString(CharSequence name) throws NullPointerException {
if (name == null) {
throw new NullPointerException(
"The name parameter must not be null.");
}
return StringUtil.toString(request.getAttribute(name.toString()));
}
/**
* Returns the request attribute value as short.
*
* @param name
* the attribute name
* @return the short attribute value
* @throws NullPointerException
* if the name parameter is null
*/
protected Short asShort(CharSequence name) throws NullPointerException {
if (name == null) {
throw new NullPointerException(
"The name parameter must not be null.");
}
return ShortUtil.toShort(request.getAttribute(name.toString()));
}
/**
* Returns the request attribute value as short.
*
* @param name
* the attribute name
* @param pattern
* the pattern for {@link DecimalFormat}
* @return the short attribute value
* @throws NullPointerException
* if the name parameter is null or if the pattern parameter is
* null
*/
protected Short asShort(CharSequence name, String pattern)
throws NullPointerException {
if (name == null) {
throw new NullPointerException(
"The name parameter must not be null.");
}
return ShortUtil.toShort(NumberUtil.toNumber(asString(name), pattern));
}
/**
* Returns the request attribute value as integer.
*
* @param name
* the attribute name
* @return the integer attribute value
* @throws NullPointerException
* if the name parameter is null
*/
protected Integer asInteger(CharSequence name) throws NullPointerException {
if (name == null) {
throw new NullPointerException(
"The name parameter must not be null.");
}
return IntegerUtil.toInteger(request.getAttribute(name.toString()));
}
/**
* Returns the request attribute value as integer.
*
* @param name
* the attribute name
* @param pattern
* the pattern for {@link DecimalFormat}
* @return the integer attribute value
* @throws NullPointerException
* if the name parameter is null or if the pattern parameter is
* null
*/
protected Integer asInteger(CharSequence name, String pattern)
throws NullPointerException {
return IntegerUtil.toInteger(NumberUtil.toNumber(
asString(name),
pattern));
}
/**
* Returns the request attribute value as long.
*
* @param name
* the attribute name
* @return the long attribute value
* @throws NullPointerException
* if the name parameter is null
*/
protected Long asLong(CharSequence name) throws NullPointerException {
if (name == null) {
throw new NullPointerException(
"The name parameter must not be null.");
}
return LongUtil.toLong(request.getAttribute(name.toString()));
}
/**
* Returns the request attribute value as long.
*
* @param name
* the attribute name
* @param pattern
* the pattern for {@link DecimalFormat}
* @return the long attribute value
* @throws NullPointerException
* if the name parameter is null or if the pattern parameter is
* null
*/
protected Long asLong(CharSequence name, String pattern)
throws NullPointerException {
return LongUtil.toLong(NumberUtil.toNumber(asString(name), pattern));
}
/**
* Returns the request attribute value as float.
*
* @param name
* the attribute name
* @return the float attribute value
* @throws NullPointerException
* if the name parameter is null
*/
protected Float asFloat(CharSequence name) throws NullPointerException {
if (name == null) {
throw new NullPointerException(
"The name parameter must not be null.");
}
return FloatUtil.toFloat(request.getAttribute(name.toString()));
}
/**
* Returns the request attribute value as float.
*
* @param name
* the attribute name
* @param pattern
* the pattern for {@link DecimalFormat}
* @return the float attribute value
* @throws NullPointerException
* if the name parameter is null or if the pattern parameter is
* null
*/
protected Float asFloat(CharSequence name, String pattern)
throws NullPointerException {
return FloatUtil.toFloat(NumberUtil.toNumber(asString(name), pattern));
}
/**
* Returns the request attribute value as double.
*
* @param name
* the attribute name
* @return the double attribute value
* @throws NullPointerException
* if the name parameter is null
*/
protected Double asDouble(CharSequence name) throws NullPointerException {
if (name == null) {
throw new NullPointerException(
"The name parameter must not be null.");
}
return DoubleUtil.toDouble(request.getAttribute(name.toString()));
}
/**
* Returns the request attribute value as double.
*
* @param name
* the attribute name
* @param pattern
* the pattern for {@link DecimalFormat}
* @return the double attribute value
* @throws NullPointerException
* if the name parameter is null or if the pattern parameter is
* null
*/
protected Double asDouble(CharSequence name, String pattern)
throws NullPointerException {
return DoubleUtil
.toDouble(NumberUtil.toNumber(asString(name), pattern));
}
/**
* Returns the request attribute value as boolean.
*
* @param name
* the attribute name
* @return the boolean attribute value
* @throws NullPointerException
* if the name parameter is null
*/
protected Boolean asBoolean(CharSequence name) throws NullPointerException {
if (name == null) {
throw new NullPointerException(
"The name parameter must not be null.");
}
return BooleanUtil.toBoolean(request.getAttribute(name.toString()));
}
/**
* Returns the request attribute value as date.
*
* @param name
* the attribute name
* @param pattern
* the pattern for {@link SimpleDateFormat}
* @return the date attribute value
* @throws NullPointerException
* if the name parameter is null or if the pattern parameter is
* null
*/
protected Date asDate(CharSequence name, String pattern)
throws NullPointerException {
return DateUtil.toDate(asString(name), pattern);
}
/**
* Returns the request attribute value as {@link Key}.
*
* @param name
* the attribute name
* @return the request attribute value as {@link Key}
* @throws NullPointerException
* if the name parameter is null
*/
protected Key asKey(CharSequence name) throws NullPointerException {
if (name == null) {
throw new NullPointerException(
"The name parameter must not be null.");
}
Object key = request.getAttribute(name.toString());
if (key == null) {
return null;
}
if (key instanceof Key) {
return (Key) key;
}
return KeyFactory.stringToKey(key.toString());
}
/**
* Returns the request attribute value as {@link Map}.
*
* @return the request attribute values as {@link Map}
*/
protected RequestMap asMap() {
return new RequestMap(request);
}
/**
* Sets the request attribute.
*
* @param name
* the attribute name
* @param value
* the attribute value
* @throws NullPointerException
* if the name parameter is null
*/
protected void requestScope(CharSequence name, Object value)
throws NullPointerException {
if (name == null) {
throw new NullPointerException(
"The name parameter must not be null.");
}
request.setAttribute(name.toString(), value);
}
/**
* Removes the request attribute.
*
* @param <T>
* the return type
* @param name
* the attribute name
* @return the removed value
* @throws NullPointerException
* if the name parameter is null
*/
@SuppressWarnings("unchecked")
protected <T> T removeRequestScope(CharSequence name)
throws NullPointerException {
if (name == null) {
throw new NullPointerException(
"The name parameter must not be null.");
}
T value = (T) request.getAttribute(name.toString());
request.removeAttribute(name.toString());
return value;
}
/**
* Returns the session attribute.
*
* @param <T>
* the return type
* @param name
* the attribute name
* @return the attribute value
* @throws NullPointerException
* if the name parameter is null
*/
@SuppressWarnings("unchecked")
protected <T> T sessionScope(CharSequence name) throws NullPointerException {
if (name == null) {
throw new NullPointerException(
"The name parameter must not be null.");
}
HttpSession session = request.getSession(false);
if (session == null) {
return null;
}
String n = name.toString();
T value = (T) session.getAttribute(n);
if (value != null) {
session.setAttribute(n, value);
}
return value;
}
/**
* Sets the session attribute.
*
* @param name
* the attribute name
* @param value
* the attribute value
* @throws NullPointerException
* if the name parameter is null
*/
protected void sessionScope(CharSequence name, Object value)
throws NullPointerException {
if (name == null) {
throw new NullPointerException(
"The name parameter must not be null.");
}
request.getSession().setAttribute(name.toString(), value);
}
/**
* Removes the session attribute.
*
* @param <T>
* the return type
* @param name
* the attribute name
* @return the removed value
* @throws NullPointerException
* if the name parameter is null
*/
@SuppressWarnings("unchecked")
protected <T> T removeSessionScope(CharSequence name)
throws NullPointerException {
if (name == null) {
throw new NullPointerException(
"The name parameter must not be null.");
}
HttpSession session = request.getSession(false);
if (session == null) {
return null;
}
T value = (T) session.getAttribute(name.toString());
session.removeAttribute(name.toString());
return value;
}
/**
* Returns the servlet context attribute.
*
* @param <T>
* the return type
* @param name
* the attribute name
* @return the attribute value
* @throws NullPointerException
* if the name parameter is null
*/
@SuppressWarnings("unchecked")
protected <T> T applicationScope(CharSequence name)
throws NullPointerException {
if (name == null) {
throw new NullPointerException(
"The name parameter must not be null.");
}
return (T) servletContext.getAttribute(name.toString());
}
/**
* Sets the servlet context attribute.
*
* @param name
* the attribute name
* @param value
* the attribute value
* @throws NullPointerException
* if the name parameter is null
*/
protected void applicationScope(CharSequence name, Object value)
throws NullPointerException {
if (name == null) {
throw new NullPointerException(
"The name parameter must not be null.");
}
servletContext.setAttribute(name.toString(), value);
}
/**
* Removes the servlet context attribute.
*
* @param <T>
* the return type
* @param name
* the attribute name
* @return the removed value
* @throws NullPointerException
* if the name parameter is null
*/
@SuppressWarnings("unchecked")
protected <T> T removeApplicationScope(CharSequence name)
throws NullPointerException {
if (name == null) {
throw new NullPointerException(
"The name parameter must not be null.");
}
T value = (T) servletContext.getAttribute(name.toString());
servletContext.removeAttribute(name.toString());
return value;
}
/**
* Determines if this application is running on development server.
*
* @return whether this application is running on development server
*/
protected boolean isDevelopment() {
return AppEngineUtil.isDevelopment();
}
/**
* Downloads the data.
*
* @param fileName
* the file name
* @param data
* the data
* @throws NullPointerException
* if the fileName parameter is null or if the data parameter is
* null
*/
protected void download(String fileName, byte[] data)
throws NullPointerException {
if (fileName == null) {
throw new NullPointerException(
"The fileName parameter must not be null.");
}
if (data == null) {
throw new NullPointerException(
"The data parameter must not be null.");
}
try {
response.setContentType("application/octet-stream");
response.setHeader("Content-disposition", "attachment; "
+ encodeFileName(fileName));
OutputStream out =
new BufferedOutputStream(response.getOutputStream());
try {
out.write(data);
} finally {
out.flush();
out.close();
}
} catch (IOException e) {
ThrowableUtil.wrapAndThrow(e);
}
}
/**
* Downloads the input stream data.
*
* @param fileName
* the file name
* @param in
* the input stream
* @throws NullPointerException
* if the fileName parameter is null or if the in parameter is
* null
*/
protected void download(String fileName, InputStream in) {
if (fileName == null) {
throw new NullPointerException(
"The fileName parameter must not be null.");
}
if (in == null) {
throw new NullPointerException("The in parameter must not be null.");
}
try {
response.setContentType("application/octet-stream");
response.setHeader("Content-disposition", "attachment; "
+ encodeFileName(fileName));
OutputStream out =
new BufferedOutputStream(response.getOutputStream());
try {
byte[] buf = new byte[BUFFER_SIZE];
int length;
while ((length = in.read(buf)) > 0) {
out.write(buf, 0, length);
}
} finally {
out.flush();
out.close();
}
} catch (IOException e) {
ThrowableUtil.wrapAndThrow(e);
}
}
/**
* Shows the data.
*
* @param fileName
* the file name
* @param data
* the data
* @throws NullPointerException
* if the fileName parameter is null or if the data parameter is
* null
*/
protected void show(String fileName, byte[] data)
throws NullPointerException {
if (fileName == null) {
throw new NullPointerException(
"The fileName parameter must not be null.");
}
if (data == null) {
throw new NullPointerException(
"The data parameter must not be null.");
}
try {
String contentType =
URLConnection.getFileNameMap().getContentTypeFor(fileName);
if (contentType != null) {
response.setContentType(contentType);
}
response.setHeader("Content-disposition", "inline; "
+ encodeFileName(fileName));
OutputStream out =
new BufferedOutputStream(response.getOutputStream());
try {
out.write(data);
} finally {
out.flush();
out.close();
}
} catch (IOException e) {
ThrowableUtil.wrapAndThrow(e);
}
}
/**
* Shoss the input stream data.
*
* @param fileName
* the file name
* @param in
* the input stream
* @throws NullPointerException
* if the fileName parameter is null or if the in parameter is
* null
*/
protected void show(String fileName, InputStream in) {
if (fileName == null) {
throw new NullPointerException(
"The fileName parameter must not be null.");
}
if (in == null) {
throw new NullPointerException("The in parameter must not be null.");
}
try {
String contentType =
URLConnection.getFileNameMap().getContentTypeFor(fileName);
if (contentType != null) {
response.setContentType(contentType);
}
response.setHeader("Content-disposition", "inline; "
+ encodeFileName(fileName));
OutputStream out =
new BufferedOutputStream(response.getOutputStream());
try {
byte[] buf = new byte[BUFFER_SIZE];
int length;
while ((length = in.read(buf)) > 0) {
out.write(buf, 0, length);
}
} finally {
out.flush();
out.close();
}
} catch (IOException e) {
ThrowableUtil.wrapAndThrow(e);
}
}
/**
* Encodes the string as "application/x-www-form-urlencoded".
*
* @param str
* the string
* @return encoded string
* @throws IOException
* if {@link IOException} occurred
*/
protected String encodeFileName(String str) throws IOException {
if (str == null) {
return null;
}
String encodedStr = URLEncoder.encode(str, "UTF-8");
String userAgent = request.getHeader("User-Agent");
if (userAgent != null && userAgent.indexOf("Opera") < 0) {
if (userAgent.indexOf("Firefox") >= 0) {
return "filename*=utf8'" + encodedStr;
} else if (userAgent.indexOf("MSIE") >= 0
|| userAgent.indexOf("Chrome") >= 0) {
return "filename=" + encodedStr;
}
}
String encoding = response.getCharacterEncoding();
if (encoding == null) {
encoding = "UTF-8";
}
return "filename=\""
+ new String(str.getBytes(encoding), "ISO-8859-1")
+ "\"";
}
/**
* Returns the path before forwarding.
*
* @return the path before forwarding
*/
protected String getForwardServletPath() {
return asString(ControllerConstants.FORWARD_SERVLET_PATH_KEY);
}
/**
* Determines if this request is get method.
*
* @return whether this request is get method
*/
protected boolean isGet() {
return "get".equalsIgnoreCase(request.getMethod());
}
/**
* Determines if this request is post method.
*
* @return whether this request is post method
*/
protected boolean isPost() {
return "post".equalsIgnoreCase(request.getMethod());
}
/**
* Determines if this request is put method.
*
* @return whether this request is put method
*/
protected boolean isPut() {
return "put".equalsIgnoreCase(request.getMethod());
}
/**
* Determines if this request is delete method.
*
* @return whether this request is delete method
*/
protected boolean isDelete() {
return "delete".equalsIgnoreCase(request.getMethod());
}
/**
* Creates a new request handler.
*
* @param request
* the request
* @return a new request handler
*
*/
protected RequestHandler createRequestHandler(HttpServletRequest request) {
if (FileUpload.isMultipartContent(request)) {
return new MultipartRequestHandler(request);
}
return new RequestHandler(request);
}
}