com.khannedy.sajikeun.servlet.AssetServlet.java Source code

Java tutorial

Introduction

Here is the source code for com.khannedy.sajikeun.servlet.AssetServlet.java

Source

/*
 * Copyright 2014 Eko Khannedy
 *
 * 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 com.khannedy.sajikeun.servlet;

import com.google.common.base.CharMatcher;
import com.google.common.io.Resources;
import com.google.common.net.HttpHeaders;
import com.google.common.net.MediaType;

import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.net.URISyntaxException;
import java.nio.charset.Charset;
import java.util.logging.Logger;

import static com.google.common.base.Preconditions.checkArgument;

/**
 * @author Eko Khannedy
 */
public class AssetServlet extends HttpServlet {

    private Logger logger = Logger.getLogger(AssetServlet.class.getName());

    private static final CharMatcher SLASHES = CharMatcher.is('/');

    private static final MediaType DEFAULT_MEDIA_TYPE = MediaType.HTML_UTF_8;

    private final File resourcePath;

    private final String uriPath;

    private final String indexFile;

    private final Charset defaultCharset;

    private final boolean cache;

    public AssetServlet(File resourcePath, String uriPath, String indexFile, Charset defaultCharset,
            boolean cache) {
        this.resourcePath = resourcePath;
        final String trimmedUri = SLASHES.trimTrailingFrom(uriPath);
        this.uriPath = trimmedUri.isEmpty() ? "/" : trimmedUri;
        this.indexFile = indexFile;
        this.defaultCharset = defaultCharset;
        this.cache = cache;
    }

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doHttp(req, resp);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doHttp(req, resp);
    }

    protected void doHttp(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        try {

            final StringBuilder builder = new StringBuilder(req.getServletPath());
            if (req.getPathInfo() != null) {
                builder.append(req.getPathInfo());
            }
            final Asset asset = loadAsset(builder.toString());
            if (asset == null) {
                resp.sendError(HttpServletResponse.SC_NOT_FOUND);
                return;
            }

            if (cache) {
                if (isCachedClientSide(req, asset)) {
                    resp.sendError(HttpServletResponse.SC_NOT_MODIFIED);
                    return;
                }
            }

            resp.setDateHeader(HttpHeaders.LAST_MODIFIED, asset.getLastModified());
            resp.setHeader(HttpHeaders.ETAG, asset.getETag());

            final String mimeTypeOfExtension = req.getServletContext().getMimeType(req.getRequestURI());
            MediaType mediaType = DEFAULT_MEDIA_TYPE;

            if (mimeTypeOfExtension != null) {
                try {
                    mediaType = MediaType.parse(mimeTypeOfExtension);
                    if (defaultCharset != null && mediaType.is(MediaType.ANY_TEXT_TYPE)) {
                        mediaType = mediaType.withCharset(defaultCharset);
                    }
                } catch (IllegalArgumentException ignore) {
                }
            }

            resp.setContentType(mediaType.type() + '/' + mediaType.subtype());

            if (mediaType.charset().isPresent()) {
                resp.setCharacterEncoding(mediaType.charset().get().toString());
            }

            try (ServletOutputStream output = resp.getOutputStream()) {
                output.write(asset.getResource());
            }
        } catch (RuntimeException | URISyntaxException ignored) {
            resp.sendError(HttpServletResponse.SC_NOT_FOUND);
        }
    }

    private Asset loadAsset(String key) throws URISyntaxException, IOException {
        checkArgument(key.startsWith(uriPath));

        File file = new File(resourcePath, key);
        if (file.isDirectory()) {
            if (indexFile != null) {
                file = new File(file, indexFile);
            } else {
                // directory requested but no index file defined
                logger.info("[FAILED] " + file.getAbsolutePath());
                return null;
            }
        }

        if (!file.exists()) {
            logger.info("[FAILED] " + file.getAbsolutePath());
            return null;
        }

        logger.info("[SUCCESS] " + file.getAbsolutePath());

        long lastModified = file.lastModified();
        if (lastModified < 1) {
            // Something went wrong trying to get the last modified time: just use the current time
            lastModified = System.currentTimeMillis();
        }

        // zero out the millis since the date we get back from If-Modified-Since will not have them
        lastModified = (lastModified / 1000) * 1000;
        return new Asset(Resources.toByteArray(file.toURI().toURL()), lastModified);
    }

    private boolean isCachedClientSide(HttpServletRequest req, Asset asset) {
        return asset.getETag().equals(req.getHeader(HttpHeaders.IF_NONE_MATCH))
                || (req.getDateHeader(HttpHeaders.IF_MODIFIED_SINCE) >= asset.getLastModified());
    }
}