001// Copyright 2013 The Apache Software Foundation
002//
003// Licensed under the Apache License, Version 2.0 (the "License");
004// you may not use this file except in compliance with the License.
005// You may obtain a copy of the License at
006//
007// http://www.apache.org/licenses/LICENSE-2.0
008//
009// Unless required by applicable law or agreed to in writing, software
010// distributed under the License is distributed on an "AS IS" BASIS,
011// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
012// See the License for the specific language governing permissions and
013// limitations under the License.
014
015package org.apache.tapestry5.internal.services.assets;
016
017import org.apache.tapestry5.commons.Resource;
018import org.apache.tapestry5.internal.services.ResourceStreamer;
019
020import java.io.IOException;
021
022/**
023 * Utility used by {@link org.apache.tapestry5.services.assets.AssetRequestHandler} implementations
024 * where the first folder in the extra path is actually a computed checksum of the resource's content.
025 *
026 * @since 5.4
027 */
028public class ChecksumPath
029{
030    private static final String NON_EXISTING_RESOURCE = "_________________________";
031
032    public final String checksum;
033
034    public final String resourcePath;
035
036    private final ResourceStreamer streamer;
037
038    public ChecksumPath(ResourceStreamer streamer, String baseFolder, String extraPath)
039    {
040        this.streamer = streamer;
041        int slashx = extraPath.indexOf('/');
042
043        checksum = extraPath.substring(0, slashx);
044
045        String morePath = extraPath.substring(slashx + 1);
046
047        if (!isBlank(morePath)) {
048            resourcePath = baseFolder == null
049                    ? morePath
050                    : baseFolder + "/" + morePath;
051        }
052        else {
053            // When we only have something which looks like a checksum but no actual path.
054            // For example, /assets/META-INF/
055            resourcePath = NON_EXISTING_RESOURCE;
056        }
057    }
058
059    /**
060     * If the resource exists and the checksum is correct, stream it to the client and return true. Otherwise,
061     * return false.
062     *
063     * @param resource
064     *         to stream
065     * @return true if streamed, false otherwise
066     * @throws IOException
067     */
068    public boolean stream(Resource resource) throws IOException
069    {
070        if (resource == null || !resource.exists())
071        {
072            return false;
073        }
074
075
076        return streamer.streamResource(resource, checksum, ResourceStreamer.DEFAULT_OPTIONS);
077    }
078    
079    /**
080     * Copied from StringUtils since it's the only method we want from it.
081     */
082    private static boolean isBlank(final CharSequence cs) {
083        int strLen;
084        if (cs == null || (strLen = cs.length()) == 0) {
085            return true;
086        }
087        for (int i = 0; i < strLen; i++) {
088            if (!Character.isWhitespace(cs.charAt(i))) {
089                return false;
090            }
091        }
092        return true;
093    }
094
095}