Java tutorial
/*************************************************************************** Copyright 2008 Emily Estes 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 net.metanotion.web.servlets; import java.io.File; import java.io.InputStream; import java.io.IOException; import java.io.Reader; import java.util.Arrays; import java.util.ArrayList; import java.util.Date; import java.util.Enumeration; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import javax.servlet.http.HttpServletRequest; 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 net.metanotion.web.Cookie; import net.metanotion.web.FileUpload; import net.metanotion.web.HttpMethod; /** This implementation of RequestObject is designed to adapt/wrap an {@link javax.servlet.http.HttpServletRequest} so it can be used by the web framework. In addition, file uploads are parsed using the <a href="http://commons.apache.org/proper/commons-fileupload/">Apache commons file upload</a> library. This implementation is not thread safe. It could be made thread safe though. */ final class SimpleRequestObject implements ServletRequestObject { /** The default maximum file size for file uploads. */ private static final long DEFAULT_MAX_FILE_SIZE_IN_BYTES = 1048576; /** The HTTP method for this request. */ private final HttpMethod method; /** The java Servlet Request object backing the request. */ private final HttpServletRequest req; /** Map of cookie names to cookie values. */ private final Map<String, Cookie> cookies = new HashMap<>(); /** Commons FileUpload FileItem wrapper implementation of FileUpload interface. */ private static final class FileUploadCommonsFileItem implements FileUpload { /** The Commons FileItem backing this file upload object. */ private final FileItem file; /** Create a new file upload object instance backed by a Commons FileUpload file. @param file The Commons FileUpload FileItem to wrap. */ public FileUploadCommonsFileItem(final FileItem file) { this.file = file; } @Override public String getMIMEType() { return file.getContentType(); } @Override public String getClientFilename() { return file.getName(); } @Override public long getLength() { return file.getSize(); } @Override public void delete() { file.delete(); } @Override public void writeToFile(final File destination) { try { file.write(destination); } catch (Exception e) { throw new RuntimeException(e); } } @Override public InputStream getInputStream() { try { return file.getInputStream(); } catch (IOException ioe) { throw new RuntimeException(ioe); } } } /** Cookie implementation wrapping a Servlet Cookie object. */ private static final class HttpCookie implements Cookie { /** The Java Servlet cookie backing this Cookie instance. */ private final javax.servlet.http.Cookie c; /** Create a new Cookie instance backed by a servlet cookie. @param c The Java Servlet cookie backing this instance. */ public HttpCookie(final javax.servlet.http.Cookie c) { this.c = c; } @Override public String getComment() { return c.getComment(); } @Override public String getDomain() { return c.getDomain(); } @Override public int getMaxAge() { return c.getMaxAge(); } @Override public String getName() { return c.getName(); } @Override public String getPath() { return c.getPath(); } @Override public Cookie.Security getSecure() { return c.getSecure() ? Cookie.Security.SSL : Cookie.Security.NONE; } @Override public Cookie.Access getHttpOnly() { return c.isHttpOnly() ? Cookie.Access.HTTP : Cookie.Access.ALL; } @Override public String getValue() { return c.getValue(); } @Override public Cookie.Version getVersion() { return (c.getVersion() == 0) ? Cookie.Version.NETSCAPE : Cookie.Version.RFC2109; } } /** This instance of VarReq exists to parse file uploads using the commons file upload library. */ private final class VRFile implements VarReq { /** Map of variable names to the file upload object instances backing the variable. */ private final Map<String, FileUpload> fileVars = new HashMap<>(); /** Map of the rest of the variable names to the values behind them. */ private final Map<String, Object> variables = new HashMap<>(); /** The factory object for processing the file uploads. */ private final DiskFileItemFactory factory = new DiskFileItemFactory(); /** The servlet file upload object to walk the request. Initialized only when an actual request on the variables is required. */ private ServletFileUpload upload = null; /** The maximum file size. */ private long maxFileSize = DEFAULT_MAX_FILE_SIZE_IN_BYTES; /** Whether we're allowed to cache large files on the disk. */ private boolean allowDiskCache = true; /** We have to parse the content of the HTTP request before any variable access attempts can be made, but this is a potentially expensive operation if there are large files in the upload, so we want to delay that until an attempt to access a variable is made. All the methods of this class that would cause a variable access simply call this method before doing so, if the request has already been processed, it simply does nothing. */ private void init() { if (upload != null) { return; } this.upload = new ServletFileUpload(factory); upload.setSizeMax(allowDiskCache ? maxFileSize : factory.getSizeThreshold()); try { final List<FileItem> items = upload.parseRequest(req); for (final FileItem i : items) { if (i.isFormField()) { final Object v = variables.get(i.getFieldName()); if (v == null) { variables.put(i.getFieldName(), i.getString()); } else { if (v instanceof ArrayList) { ((ArrayList) v).add(i.getString()); } else { final ArrayList<String> arr = new ArrayList<>(); arr.add((String) v); arr.add(i.getString()); variables.put(i.getFieldName(), arr); } } } else { fileVars.put(i.getFieldName(), new FileUploadCommonsFileItem(i)); } } } catch (FileUploadException fue) { throw new RuntimeException(fue); } } @Override public Object getVariable(final String name) { init(); final FileUpload val = fileVars.get(name); if (val != null) { return val; } final Object v = variables.get(name); if (v != null) { return v; } return req.getParameter(name); } @Override public void setFileTempDirectory(final File tempDir) { factory.setRepository(tempDir); } @Override public void setMaxFileUploadSize(final long size) { maxFileSize = size; } @Override public void setMaxMemFileSize(final int size) { factory.setSizeThreshold(size); } @Override public void allowFilesOnDisk(final boolean allowDisk) { this.allowDiskCache = allowDisk; } } /** This instance of VarReq is a normal one backed by the Java Servlet object. */ private final class VRNormal implements VarReq { @Override public Object getVariable(final String name) { final String[] values = req.getParameterValues(name); if (values == null) { return null; } if (values.length == 1) { return values[0]; } return Arrays.asList(values); } @Override public void setFileTempDirectory(final File tempDir) { } @Override public void setMaxFileUploadSize(final long size) { } @Override public void setMaxMemFileSize(final int size) { } @Override public void allowFilesOnDisk(final boolean allowDiskCache) { } } /** Due to the requirements of the Commons File Upload library, we have to make sure to choose the "correct" way to access request variables/form variables BEFORE any access attempts, so this interface abstracts the choice and a instance of it is chosen during the call to the constructor so that all variable access calls go through the proper method. */ private interface VarReq { public Object getVariable(String name); public void setFileTempDirectory(File tempDir); public void setMaxFileUploadSize(long size); public void setMaxMemFileSize(int size); public void allowFilesOnDisk(boolean allowDiskCache); } private final VarReq variableRequestHandler; public SimpleRequestObject(final HttpMethod method, final HttpServletRequest req, final File tempFolder, final long maxDiskFile, final int maxMemFile, final boolean allowDiskCache) { this.method = method; this.req = req; if (ServletFileUpload.isMultipartContent(req)) { this.variableRequestHandler = new VRFile(); } else { this.variableRequestHandler = new VRNormal(); } this.variableRequestHandler.setFileTempDirectory(tempFolder); this.variableRequestHandler.setMaxFileUploadSize(maxDiskFile); this.variableRequestHandler.setMaxMemFileSize(maxMemFile); this.variableRequestHandler.allowFilesOnDisk(allowDiskCache); final javax.servlet.http.Cookie[] cs = this.req.getCookies(); if (cs != null) { for (final javax.servlet.http.Cookie c : cs) { cookies.put(c.getName(), new HttpCookie(c)); } } } // RequestObject @Override public HttpMethod getMethod() { return this.method; } @Override public String getServer() { return this.req.getServerName(); } @Override public String getResource() { return this.req.getRequestURI(); } @Override public String getRawResource() { return this.req.getRequestURI(); } @Override public String getQuery() { return this.req.getQueryString(); } @Override public Reader getRawPost() { try { return this.req.getReader(); } catch (java.io.IOException ioe) { throw new RuntimeException(ioe); } } @Override public InputStream getByteStream() { try { return this.req.getInputStream(); } catch (java.io.IOException ioe) { throw new RuntimeException(ioe); } } @Override public String getRemoteAddr() { return this.req.getRemoteAddr(); } @Override public String getRemoteHost() { return this.req.getRemoteHost(); } @Override public String getHeader(final String name) { return this.req.getHeader(name); } @Override public Iterable<String> getHeaders() { return new Iterable<String>() { @Override public Iterator<String> iterator() { final Enumeration<String> e = req.getHeaderNames(); if (e == null) { return new Iterator<String>() { @Override public boolean hasNext() { return false; } @Override public String next() { throw new java.util.NoSuchElementException(); } @Override public void remove() { throw new UnsupportedOperationException(); } }; } else { return new Iterator<String>() { @Override public boolean hasNext() { return e.hasMoreElements(); } @Override public String next() { return e.nextElement(); } @Override public void remove() { throw new UnsupportedOperationException(); } }; } } }; } @Override public Date getDateHeader(final String name) { if (this.req.getHeader(name) == null) { return null; } return new Date(this.req.getDateHeader(name)); } @Override public Cookie getCookie(final String name) { return this.cookies.get(name); } @Override public Iterable<String> getCookies() { return this.cookies.keySet(); } @Override public Object get(final String name) { return this.variableRequestHandler.getVariable(name); } // ServletRequestObject @Override public HttpServletRequest getServletRequest() { return this.req; } }