package fi.internetix.smvc.controllers; import java.io.UnsupportedEncodingException; import java.math.BigDecimal; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Currency; import java.util.Date; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Locale; import java.util.Map; import javax.servlet.ServletContext; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import org.apache.commons.fileupload.FileItem; import org.apache.commons.fileupload.FileUploadException; import org.apache.commons.fileupload.disk.DiskFileItemFactory; import org.apache.commons.fileupload.servlet.ServletFileUpload; import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.math.NumberUtils; import fi.internetix.smvc.Severity; import fi.internetix.smvc.SmvcMessage; import fi.internetix.smvc.SmvcRuntimeException; import fi.internetix.smvc.StatusCode; import fi.internetix.smvc.dispatcher.RequestDispatchContext; import fi.internetix.smvc.i18n.Messages; /** * An abstract request context associated with each request made to the application. * <p/> * Wraps in both the HTTP servlet request and response objects, as well as packs some logic for * error handling and tracking the currently logged in user. */ public abstract class RequestContext { /** * The constructor specifying the HTTP servlet request and response objects associated with the * context. * * @param servletRequest The HTTP servlet request * @param servletResponse The HTTP servlet response * @throws FileUploadException */ protected RequestContext(RequestDispatchContext requestDispatchContext, HttpServletRequest servletRequest, HttpServletResponse servletResponse, ServletContext servletContext) { this.servletRequest = servletRequest; this.servletResponse = servletResponse; this.requestDispatchContext = requestDispatchContext; this.servletContext = servletContext; if (ServletFileUpload.isMultipartContent(servletRequest)) { multipartFields = new HashMap<>(); ServletFileUpload servletFileUpload = new ServletFileUpload(new DiskFileItemFactory()); servletFileUpload.setHeaderEncoding("UTF-8"); String fileName = null; try { List<FileItem> items = servletFileUpload.parseRequest(servletRequest); Iterator<FileItem> iter = items.iterator(); while (iter.hasNext()) { FileItem item = iter.next(); fileName = item.getName(); String fieldName = item.getFieldName(); multipartFields.put(fieldName, item); } } catch (FileUploadException fue) { Messages messages = Messages.getInstance(); Locale locale = servletRequest.getLocale(); throw new SmvcRuntimeException(StatusCode.FILE_HANDLING_FAILURE, messages.getText(locale, "exception.104.fileHandlingFailure", new String[] {fileName}), fue); } } } /** * Returns the HTTP servlet request associated with this context. * * @return The HTTP servlet request associated with this context */ public HttpServletRequest getRequest() { return servletRequest; } /** * Returns the HTTP servlet response associated with this context. * * @return The HTTP servlet response associated with this context */ public HttpServletResponse getResponse() { return servletResponse; } public ServletContext getServletContext() { return servletContext; } /** * Returns whether this context is associated with a logged in user. * * @return <code>true</code> if a logged in user exists in this context, otherwise * <code>false/code> */ public boolean isLoggedIn() { return getLoggedUserId() != null; } /** * Returns the identifier of the logged in user. If the user hasn't logged in, returns * <code>null</code>. * * @return The identifier of the logged in user, or <code>null</code> if the user hasn't logged in */ public Long getLoggedUserId() { HttpSession session = getRequest().getSession(false); return session == null ? null : (Long) session.getAttribute("loggedUserId"); } /** * Returns the redirect URL stored to this context. If one hasn't been specified, returns * <code>null</code>. * * @return The redirect URL stored to this context, or <code>null</code> if one hasn't been set */ public String getRedirectURL() { return redirectURL; } /** * Sets the redirect URL of this context. * * @param redirectURL The redirect URL of this context */ public void setRedirectURL(String redirectURL) { this.redirectURL = redirectURL; } public String getReferer(boolean returnAnchor) { StringBuilder refererBuilder = new StringBuilder(servletRequest.getHeader("Referer")); if (returnAnchor) { String refererAnchor = getRefererAnchor(); if (!StringUtils.isBlank(refererAnchor)) { refererBuilder.append('#').append(refererAnchor); } } return refererBuilder.toString(); } public String getRefererAnchor() { return getString("__refererAnchor"); } public void addMessage(Severity severity, String message) { messages.add(new SmvcMessage(severity, message)); } public void addMessage(SmvcMessage message) { messages.add(message); } /** * Returns the messages of this context. * * @return The messages of this context */ protected List<SmvcMessage> getMessages() { return messages; } public Boolean getBoolean(String paramName) { return Boolean.valueOf(getString(paramName)); } public Date getDate(String paramName) { Long value = getLong(paramName); return value == null ? null : new Date(value); } public Date getDateByFormat(String paramName, String format) { Date date = null; SimpleDateFormat sdf = new SimpleDateFormat(format); String dateStr = getString(paramName, true); if (dateStr != null) { try { date = sdf.parse(dateStr); } catch (ParseException e) { e.printStackTrace(); } } return date; } public Double getDouble(String paramName) { String value = getString(paramName); // Since '.' is a universal decimal separator in Java, let's be lenient with ',' as well if (value != null && value.indexOf(',') >= 0) { value = value.replace(',', '.'); } // TODO Invalid value as a runtime exception? return NumberUtils.isNumber(value) ? NumberUtils.toDouble(value) : null; } public Integer getInteger(String paramName) { String value = getString(paramName); return NumberUtils.isNumber(value) ? NumberUtils.toInt(value) : null; } public Long getLong(String paramName) { String value = getString(paramName); return NumberUtils.isNumber(value) ? NumberUtils.toLong(value) : null; } public BigDecimal getBigDecimal(String paramName) { String value = getString(paramName); // Since '.' is a universal decimal separator in Java, let's be lenient with ',' as well if (value != null && value.indexOf(',') >= 0) { value = value.replace(',', '.'); } return NumberUtils.isNumber(value) ? NumberUtils.createBigDecimal(value) : null; } public Currency getCurrency(String paramName) { String value = getString(paramName); if (StringUtils.isNotBlank(value)) { return Currency.getInstance(value); } return null; } @SuppressWarnings({ "rawtypes", "unchecked" }) public Enum getEnum(String paramName, Class enumClass) { String name = getString(paramName); if (name != null) return Enum.valueOf(enumClass, name); else return null; } public String getLowercaseString(String paramName) { return getLowercaseString(paramName, true); } public String getLowercaseString(String paramName, boolean emptyAsNull) { String str = getString(paramName, emptyAsNull); return str == null ? null : str.toLowerCase(); } public String getString(String paramName) { return getString(paramName, true); } public String[] getStrings(String paramName) { return requestDispatchContext.getParameterHandler().getParameters(paramName); } public String getString(String paramName, boolean emptyAsNull) { String value = null; if (multipartFields != null) { FileItem fileItem = multipartFields.get(paramName); if (fileItem != null) { try { value = fileItem.getString("UTF-8"); } catch (UnsupportedEncodingException uee) { } } } else { value = requestDispatchContext.getParameterHandler().getParameter(paramName); } return emptyAsNull && StringUtils.isBlank(value) ? null : value; } public FileItem getFile(String paramName) { FileItem fileItem = null; if (multipartFields != null) { fileItem = multipartFields.get(paramName); } return fileItem; } /** * Once a page request has been processed, the context is responsible of writing it back to the * user via this method. This response is written BEFORE committing (or rolling back) the database * session associated with request. * * @param statusCode the status code of the request * * @throws Exception If writing the response fails for any reason */ public abstract void writePreCommitResponse(int statusCode) throws Exception; /** * Once a page request has been processed, the context is responsible of writing it back to the * user via this method. This response is written AFTER committing (or rolling back) the database * session associated with the request. * * @param statusCode the status code of the request * * @throws Exception If writing the response fails for any reason */ public abstract void writePostCommitResponse(int statusCode) throws Exception; private RequestDispatchContext requestDispatchContext; /** * The HTTP servlet request of this context. */ private HttpServletRequest servletRequest; /** * The HTTP servlet response of this context. */ private HttpServletResponse servletResponse; private final ServletContext servletContext; private Map<String, FileItem> multipartFields; /** * The redirect URL of this context. */ private String redirectURL; /** * The messages of this context. */ private List<SmvcMessage> messages = new ArrayList<>(); }