org.cast.cwm.ResourceDirectory.java Source code

Java tutorial

Introduction

Here is the source code for org.cast.cwm.ResourceDirectory.java

Source

/*
 * Copyright 2011-2016 CAST, Inc.
 *
 * This file is part of the CAST Wicket Modules:
 * see <http://code.google.com/p/cast-wicket-modules>.
 *
 * The CAST Wicket Modules are free software: you can redistribute and/or
 * modify them under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * The CAST Wicket Modules are distributed in the hope that they will be
 * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this software.  If not, see <http://www.gnu.org/licenses/>.
 */
package org.cast.cwm;

import java.io.IOException;

import javax.servlet.http.HttpServletResponse;

import org.apache.wicket.Application;
import org.apache.wicket.markup.html.IPackageResourceGuard;
import org.apache.wicket.request.http.WebResponse;
import org.apache.wicket.request.mapper.parameter.PageParameters;
import org.apache.wicket.request.resource.AbstractResource;
import org.apache.wicket.request.resource.PackageResource.PackageResourceBlockedException;
import org.apache.wicket.util.file.File;
import org.apache.wicket.util.io.IOUtils;
import org.apache.wicket.util.resource.FileResourceStream;
import org.apache.wicket.util.resource.IResourceStream;
import org.apache.wicket.util.resource.ResourceStreamNotFoundException;
import org.apache.wicket.util.time.Duration;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * <p>Represents a directory in which all of the files can be used as resources
 * (eg, images). Which file to send in response to a particular request is based
 * on the URL, which is appended to the given resource directory path.
 *
 * <p>See {@link ResourceDirectoryReference} for usage examples.</p>
    
 * <p>The installed PackageResourceGuard will be checked to make sure the file is
 * ok to send.</p>
 * 
 * <p>Heavily based on the
 * {@link org.apache.wicket.request.resource.PackageResource} class, but with
 * different logic to locate the file to send.</p>
 * 
 * @see ResourceDirectoryReference
 * 
 * @author bgoldowsky
 * 
 */
public class ResourceDirectory extends AbstractResource {
    private static final Logger log = LoggerFactory.getLogger(ResourceDirectory.class);

    private static final Duration DEFAULT_CACHE_DURATION = Duration.hours(1);

    private static final long serialVersionUID = 1L;

    /**
     * The path to the directory of resources
     */
    private final File sourceDirectory;

    private Duration cacheDuration = DEFAULT_CACHE_DURATION;

    /**
     * Construct a with a given directory of resource files.
     * 
     * @param directory the directory containing public files
     */
    public ResourceDirectory(File directory) {
        this.sourceDirectory = directory;
    }

    /**
     * creates a new resource response based on the request attributes
     * 
     * @param attributes
     *            current request attributes from client
     * @return resource response for answering request
     */
    @Override
    protected ResourceResponse newResourceResponse(Attributes attributes) {
        String relativePath = "";
        PageParameters parameters = attributes.getParameters();
        for (int i = 0; i < parameters.getIndexedCount(); i++) {
            relativePath += parameters.get(i);
            relativePath += '/';
        }
        if (relativePath.endsWith("/"))
            relativePath = relativePath.substring(0, relativePath.length() - 1);
        log.trace("relativePath is {}", relativePath);

        String absolutePath = new File(sourceDirectory, relativePath).getAbsolutePath();

        final ResourceResponse resourceResponse = new ResourceResponse();

        final IResourceStream resourceStream = getResourceStream(absolutePath);

        // bail out if resource stream could not be found
        if (resourceStream == null) {
            return sendResourceError(absolutePath, resourceResponse, HttpServletResponse.SC_NOT_FOUND,
                    "Unable to find resource");
        }

        // allow caching
        resourceResponse.setCacheScope(WebResponse.CacheScope.PUBLIC);
        resourceResponse.setCacheDuration(cacheDuration);

        // add Last-Modified header (to support HEAD requests and If-Modified-Since)
        resourceResponse.setLastModified(resourceStream.lastModifiedTime());

        if (resourceResponse.dataNeedsToBeWritten(attributes)) {
            String contentType = resourceStream.getContentType();

            if (contentType == null && Application.exists())
                contentType = Application.get().getMimeType(absolutePath);

            // set Content-Type (may be null)
            resourceResponse.setContentType(contentType);

            try {
                // read resource data
                final byte[] bytes;

                bytes = IOUtils.toByteArray(resourceStream.getInputStream());

                // send Content-Length header
                resourceResponse.setContentLength(bytes.length);

                // send response body with resource data
                resourceResponse.setWriteCallback(new WriteCallback() {
                    @Override
                    public void writeData(Attributes attributes) {
                        attributes.getResponse().write(bytes);
                    }
                });
            } catch (IOException e) {
                log.debug(e.getMessage(), e);
                return sendResourceError(absolutePath, resourceResponse, 500, "Unable to read resource stream");
            } catch (ResourceStreamNotFoundException e) {
                log.debug(e.getMessage(), e);
                return sendResourceError(absolutePath, resourceResponse, 500, "Unable to open resource stream");
            } finally {
                try {
                    resourceStream.close();
                } catch (IOException e) {
                    log.warn("Unable to close the resource stream", e);
                }
            }
        }

        return resourceResponse;
    }

    /**
     * send resource specific error message and write log entry
     * 
     * @param resourceResponse
     *            resource response
     * @param errorCode
     *            error code (=http status)
     * @param errorMessage
     *            error message (=http error message)
     * @return resource response for method chaining
     */
    private ResourceResponse sendResourceError(String absolutePath, ResourceResponse resourceResponse,
            int errorCode, String errorMessage) {
        String msg = String.format("resource [path = %s]: %s (status=%d)", absolutePath, errorMessage, errorCode);

        log.warn(msg);

        resourceResponse.setError(errorCode, errorMessage);
        return resourceResponse;
    }

    /**
     * locate resource stream for current resource
     * 
     * @return resource stream or <code>null</code> if not found
     */
    public IResourceStream getResourceStream(String absolutePath) {
        if (!accept(Application.class, absolutePath)) {
            log.warn("Access denied by IPackageResrouceGuard to resource: {}", absolutePath);
            throw new PackageResourceBlockedException(
                    "Access denied to (static) package resource " + absolutePath + ". See IPackageResourceGuard");
        }
        return new FileResourceStream(new File(absolutePath));
    }

    /**
     * @param scope
     *            resource scope
     * @param path
     *            resource path
     * @return <code>true<code> if resource access is granted
     */
    private boolean accept(Class<?> scope, String path) {
        IPackageResourceGuard guard = Application.get().getResourceSettings().getPackageResourceGuard();

        return guard.accept(scope, path);
    }

    @Override
    public String toString() {
        return "[" + getClass().getSimpleName() + ' ' + "dir = " + sourceDirectory + ']';
    }

    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + ((sourceDirectory == null) ? 0 : sourceDirectory.hashCode());
        return result;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        ResourceDirectory other = (ResourceDirectory) obj;
        if (sourceDirectory == null) {
            if (other.sourceDirectory != null)
                return false;
        } else if (!sourceDirectory.equals(other.sourceDirectory))
            return false;

        return true;
    }

    public Duration getCacheDuration() {
        return cacheDuration;
    }

    public void setCacheDuration(Duration cacheDuration) {
        this.cacheDuration = cacheDuration;
    }

}