com.haulmont.cuba.web.sys.CubaWebJarsHandler.java Source code

Java tutorial

Introduction

Here is the source code for com.haulmont.cuba.web.sys.CubaWebJarsHandler.java

Source

/*
 * Copyright (c) 2008-2017 Haulmont.
 *
 * 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.haulmont.cuba.web.sys;

import com.haulmont.cuba.core.global.FileTypesHelper;
import com.vaadin.server.RequestHandler;
import com.vaadin.server.VaadinRequest;
import com.vaadin.server.VaadinResponse;
import com.vaadin.server.VaadinSession;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.net.URLConnection;

import static org.apache.commons.io.IOUtils.copy;

public class CubaWebJarsHandler implements RequestHandler {
    protected static final String VAADIN_WEBJARS_PREFIX = "/VAADIN/webjars/";
    protected static final String VAADIN_PREFIX = "/VAADIN/";
    protected static final String CLASSPATH_WEBJAR_PREFIX = "/META-INF/resources/";

    private final Logger log = LoggerFactory.getLogger(CubaWebJarsHandler.class);

    protected ServletContext servletContext;

    public CubaWebJarsHandler(ServletContext servletContext) {
        this.servletContext = servletContext;
    }

    @Override
    public boolean handleRequest(VaadinSession session, VaadinRequest request, VaadinResponse response)
            throws IOException {
        String path = request.getPathInfo();

        if (StringUtils.isEmpty(path) || StringUtils.isNotEmpty(path) && !path.startsWith(VAADIN_WEBJARS_PREFIX)) {
            return false;
        }

        log.trace("WebJar resource requested: {}", path.replace(VAADIN_WEBJARS_PREFIX, ""));

        String errorMessage = checkResourcePath(path);
        if (StringUtils.isNotEmpty(errorMessage)) {
            log.warn(errorMessage);
            response.sendError(HttpServletResponse.SC_FORBIDDEN, errorMessage);
            return false;
        }

        URL resourceUrl = getStaticResourceUrl(path);

        if (resourceUrl == null) {
            resourceUrl = getClassPathResourceUrl(path);
        }

        if (resourceUrl == null) {
            String msg = String.format("Requested WebJar resource is not found: %s", path);
            response.sendError(HttpServletResponse.SC_NOT_FOUND, msg);
            log.warn(msg);
            return false;
        }

        String resourceName = getResourceName(path);
        String mimeType = servletContext.getMimeType(resourceName);
        response.setContentType(mimeType != null ? mimeType : FileTypesHelper.DEFAULT_MIME_TYPE);

        String cacheControl = "public, max-age=0, must-revalidate";
        int resourceCacheTime = getCacheTime(resourceName);
        if (resourceCacheTime > 0) {
            cacheControl = "max-age=" + String.valueOf(resourceCacheTime);
        }
        response.setHeader("Cache-Control", cacheControl);
        response.setDateHeader("Expires", System.currentTimeMillis() + (resourceCacheTime * 1000));

        InputStream inputStream = null;
        try {
            URLConnection connection = resourceUrl.openConnection();
            long lastModifiedTime = connection.getLastModified();
            // Remove milliseconds to avoid comparison problems (milliseconds
            // are not returned by the browser in the "If-Modified-Since"
            // header).
            lastModifiedTime = lastModifiedTime - lastModifiedTime % 1000;
            response.setDateHeader("Last-Modified", lastModifiedTime);

            if (browserHasNewestVersion(request, lastModifiedTime)) {
                response.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
                return true;
            }

            inputStream = connection.getInputStream();

            copy(inputStream, response.getOutputStream());

            return true;
        } finally {
            if (inputStream != null) {
                inputStream.close();
            }
        }
    }

    protected String getResourceName(String uri) {
        String[] tokens = uri.split("/");
        return tokens[tokens.length - 1];
    }

    // copy-pasted from VaadinServlet
    protected boolean browserHasNewestVersion(VaadinRequest request, long resourceLastModifiedTimestamp) {
        if (resourceLastModifiedTimestamp < 1) {
            // We do not know when it was modified so the browser cannot have an
            // up-to-date version
            return false;
        }
        /*
         * The browser can request the resource conditionally using an
         * If-Modified-Since header. Check this against the last modification
         * time.
         */
        try {
            // If-Modified-Since represents the timestamp of the version cached
            // in the browser
            long headerIfModifiedSince = request.getDateHeader("If-Modified-Since");

            if (headerIfModifiedSince >= resourceLastModifiedTimestamp) {
                // Browser has this an up-to-date version of the resource
                return true;
            }
        } catch (Exception e) {
            // Failed to parse header. Fail silently - the browser does not have
            // an up-to-date version in its cache.
        }
        return false;
    }

    protected URL getStaticResourceUrl(String path) throws IOException {
        URL resourceUrl = servletContext.getResource(path);

        if (resourceUrl != null) {
            log.trace("Overridden version of WebJar resource found: {}", path);
        }

        return resourceUrl;
    }

    protected URL getClassPathResourceUrl(String path) {
        String classpathPath = path.replace(VAADIN_PREFIX, CLASSPATH_WEBJAR_PREFIX);

        log.trace("Load WebJar resource from classpath: {}", classpathPath);

        return this.getClass().getResource(classpathPath);
    }

    protected int getCacheTime(String filename) {
        if (filename.contains(".nocache.")) {
            return 0;
        }
        if (filename.contains(".cache.")) {
            return 60 * 60 * 24 * 365;
        }
        return 60 * 60;
    }

    protected String checkResourcePath(String uri) {
        if (uri.endsWith("/")) {
            return String.format("Directory loading is forbidden: %s", uri);
        }

        if (uri.contains("/../")) {
            return String.format("Loading WebJar resource with the upward path is forbidden: %s", uri);
        }

        return null;
    }
}