net.metanotion.web.servlets.SimpleRequestObject.java Source code

Java tutorial

Introduction

Here is the source code for net.metanotion.web.servlets.SimpleRequestObject.java

Source

/***************************************************************************
   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;
    }
}