com.hypersocket.server.handlers.impl.ContentHandlerImpl.java Source code

Java tutorial

Introduction

Here is the source code for com.hypersocket.server.handlers.impl.ContentHandlerImpl.java

Source

/*******************************************************************************
 * Copyright (c) 2013 Hypersocket Limited.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the GNU Public License v3.0
 * which accompanies this distribution, and is available at
 * http://www.gnu.org/licenses/gpl.html
 ******************************************************************************/
package com.hypersocket.server.handlers.impl;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.TimeZone;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.http.HttpHeaders;
import org.apache.http.HttpStatus;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpMethod;
import org.springframework.mail.javamail.ConfigurableMimeFileTypeMap;

import com.hypersocket.server.handlers.HttpRequestHandler;
import com.hypersocket.server.handlers.HttpResponseProcessor;
import com.hypersocket.utils.HypersocketUtils;

public abstract class ContentHandlerImpl extends HttpRequestHandler implements ContentHandler {

    private static Logger log = LoggerFactory.getLogger(ContentHandlerImpl.class);

    public static final String CONTENT_INPUTSTREAM = "ContentInputStream";

    String basePath;

    public static final String HTTP_DATE_FORMAT = "EEE, dd MMM yyyy HH:mm:ss zzz";
    public static final String HTTP_DATE_GMT_TIMEZONE = "GMT";
    public static final int HTTP_CACHE_SECONDS = 60;

    ConfigurableMimeFileTypeMap mimeTypesMap = new ConfigurableMimeFileTypeMap();

    Map<String, String> aliases = new HashMap<String, String>();
    List<ContentFilter> filters = new ArrayList<ContentFilter>();

    protected ContentHandlerImpl(String name, int priority) {
        super(name, priority);
    }

    @Override
    public boolean handlesRequest(HttpServletRequest request) {
        return request.getRequestURI().startsWith(server.resolvePath(basePath));
    }

    public void setBasePath(String basePath) {
        this.basePath = basePath;
    }

    @Override
    public void handleHttpRequest(HttpServletRequest request, HttpServletResponse response,
            HttpResponseProcessor responseProcessor) throws IOException {

        try {
            if (request.getMethod() != HttpMethod.GET.toString()) {
                response.sendError(HttpStatus.SC_METHOD_NOT_ALLOWED);
                return;
            }

            String path = translatePath(sanitizeUri(request.getRequestURI()));
            if (path.startsWith("/"))
                path = path.substring(1);

            if (path == null) {
                response.sendError(HttpStatus.SC_FORBIDDEN);
                return;
            }

            if (log.isDebugEnabled()) {
                log.debug("Resolving " + getResourceName() + " resource in " + basePath + ": "
                        + request.getRequestURI());
            }

            int status = getResourceStatus(path);

            if (status != HttpStatus.SC_OK) {
                if (log.isDebugEnabled()) {
                    log.debug(
                            "Resource not found in " + basePath + " [" + status + "]: " + request.getRequestURI());
                }
                response.sendError(status);
                return;
            }

            if (log.isDebugEnabled()) {
                log.debug("Resource found in " + basePath + ": " + request.getRequestURI());
            }

            // Cache Validation
            String ifModifiedSince = request.getHeader(HttpHeaders.IF_MODIFIED_SINCE);
            if (ifModifiedSince != null && !ifModifiedSince.equals("")) {
                SimpleDateFormat dateFormatter = new SimpleDateFormat(HTTP_DATE_FORMAT, Locale.US);
                try {
                    Date ifModifiedSinceDate = dateFormatter.parse(ifModifiedSince);

                    // Only compare up to the second because the datetime format we send to the client does
                    // not have milliseconds
                    long ifModifiedSinceDateSeconds = ifModifiedSinceDate.getTime() / 1000;
                    long fileLastModifiedSeconds = getLastModified(path) / 1000;
                    if (ifModifiedSinceDateSeconds == fileLastModifiedSeconds) {
                        if (log.isDebugEnabled()) {
                            log.debug(path + " has not been modified since "
                                    + HypersocketUtils.formatDateTime(ifModifiedSinceDate));
                        }
                        sendNotModified(response);
                        return;
                    }
                } catch (Throwable e) {
                    response.sendError(HttpStatus.SC_BAD_REQUEST);
                    return;
                }
            }

            long fileLength = getResourceLength(path);
            long actualLength = 0;
            InputStream in = getInputStream(path, request);

            if (fileLength <= 131072) {
                int r;
                byte[] buf = new byte[4096];
                while ((r = in.read(buf)) > -1) {
                    response.getOutputStream().write(buf, 0, r);
                    if (fileLength < 0) {
                        actualLength += r;
                    }

                }
                response.setHeader(HttpHeaders.CONTENT_LENGTH, String.valueOf(actualLength));

            } else {
                request.setAttribute(CONTENT_INPUTSTREAM, in);
            }

            setContentTypeHeader(response, path);
            setDateAndCacheHeaders(response, path);

            response.setStatus(HttpStatus.SC_OK);
        } catch (RedirectException e) {
            response.sendRedirect(
                    server.resolvePath(basePath + (e.getMessage().startsWith("/") ? "" : "/") + e.getMessage()));
        } finally {
            responseProcessor.sendResponse(request, response, false);
        }

    }

    public InputStream getInputStream(String path, HttpServletRequest request) throws FileNotFoundException {

        InputStream original = getResourceStream(path);
        for (ContentFilter filter : filters) {
            if (filter.filtersPath(path)) {
                original = filter.getFilterStream(original, request);
            }
        }
        return original;
    }

    @Override
    public abstract String getResourceName();

    @Override
    public abstract InputStream getResourceStream(String path) throws FileNotFoundException;

    @Override
    public abstract long getResourceLength(String path) throws FileNotFoundException;

    @Override
    public abstract long getLastModified(String path) throws FileNotFoundException;

    @Override
    public abstract int getResourceStatus(String path);

    protected String translatePath(String path) throws RedirectException {

        for (Map.Entry<String, String> alias : aliases.entrySet()) {
            if (path.matches(alias.getKey())) {
                if (alias.getValue().startsWith("redirect:")) {
                    throw new RedirectException(alias.getValue().substring(9));
                }
                return alias.getValue();
            }
        }
        return path;
    }

    @Override
    public void addAlias(String alias, String path) {
        aliases.put(alias, path);
    }

    @Override
    public void addFilter(ContentFilter filter) {
        filters.add(filter);
    }

    private String sanitizeUri(String uri) {
        // Decode the path.
        try {
            uri = URLDecoder.decode(uri, "UTF-8");
        } catch (UnsupportedEncodingException e) {
            try {
                uri = URLDecoder.decode(uri, "ISO-8859-1");
            } catch (UnsupportedEncodingException e1) {
                throw new Error();
            }
        }

        uri = uri.replaceAll(server.resolvePath(basePath), "");

        return uri;
    }

    /**
      * When file timestamp is the same as what the browser is sending up, send a "304 Not Modified"
      *
      * @param ctx
      *            Context
      */
    private void sendNotModified(HttpServletResponse response) {
        response.setStatus(HttpStatus.SC_NOT_MODIFIED);
        setDateHeader(response);
    }

    /**
     * Sets the Date header for the HTTP response
     *
     * @param response
     *            HTTP response
     */
    private void setDateHeader(HttpServletResponse response) {
        SimpleDateFormat dateFormatter = new SimpleDateFormat(HTTP_DATE_FORMAT, Locale.US);
        dateFormatter.setTimeZone(TimeZone.getTimeZone(HTTP_DATE_GMT_TIMEZONE));

        Calendar time = new GregorianCalendar();
        response.setHeader(HttpHeaders.DATE, dateFormatter.format(time.getTime()));
    }

    /**
     * Sets the Date and Cache headers for the HTTP Response
     *
     * @param response
     *            HTTP response
     * @param fileToCache
     *            file to extract content type
     */
    private void setDateAndCacheHeaders(HttpServletResponse response, String path) {
        SimpleDateFormat dateFormatter = new SimpleDateFormat(HTTP_DATE_FORMAT, Locale.US);
        dateFormatter.setTimeZone(TimeZone.getTimeZone(HTTP_DATE_GMT_TIMEZONE));

        // Date header
        Calendar time = new GregorianCalendar();
        response.setHeader(HttpHeaders.DATE, dateFormatter.format(time.getTime()));

        // Add cache headers
        time.add(Calendar.SECOND, HTTP_CACHE_SECONDS);
        response.setHeader(HttpHeaders.EXPIRES, dateFormatter.format(time.getTime()));
        response.setHeader(HttpHeaders.CACHE_CONTROL, "private, max-age=" + HTTP_CACHE_SECONDS);
        try {
            response.setHeader(HttpHeaders.LAST_MODIFIED, dateFormatter.format(new Date(getLastModified(path))));
        } catch (FileNotFoundException e) {
        }
    }

    /**
     * Sets the content type header for the HTTP Response
     *
     * @param response
     *            HTTP response
     * @param file
     *            file to extract content type
     */
    private void setContentTypeHeader(HttpServletResponse response, String path) {
        response.setHeader(HttpHeaders.CONTENT_TYPE, mimeTypesMap.getContentType(path));
    }

}