    001    // Copyright 2009, 2010, 2011 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
    015    package org.apache.tapestry5.internal.services;
    016
    017    import java.io.IOException;
    018
    019    import javax.servlet.http.HttpServletResponse;
    020
    021    import org.apache.tapestry5.ioc.Resource;
    022    import org.apache.tapestry5.services.AssetSource;
    023    import org.apache.tapestry5.services.Response;
    024
    025    public class AssetResourceLocatorImpl implements AssetResourceLocator
    026    {
    027        private final ResourceDigestManager digestManager;
    028
    029        private final Response response;
    030
    031        private final AssetSource assetSource;
    032
    033        public AssetResourceLocatorImpl(ResourceDigestManager digestManager, Response response, AssetSource assetSource)
    034        {
    035            this.digestManager = digestManager;
    036            this.response = response;
    037            this.assetSource = assetSource;
    038        }
    039
    040        public Resource findClasspathResourceForPath(String path) throws IOException
    041        {
    042            Resource resource = assetSource.resourceForPath(path);
    043
    044            if (!digestManager.requiresDigest(resource))
    045                return resource;
    046
    047            return validateChecksumOfClasspathResource(resource);
    048        }
    049
    050        /**
    051         * Validates the checksum encoded into the resource, and returns the true resource (with the checksum
    052         * portion removed from the file name).
    053         */
    054        private Resource validateChecksumOfClasspathResource(Resource resource) throws IOException
    055        {
    056            String file = resource.getFile();
    057
    058            // Somehow this code got real ugly, but it's all about preventing NPEs when a resource
    059            // that should have a digest doesn't.
    060
    061            boolean valid = false;
    062            Resource result = resource;
    063
    064            int lastdotx = file.lastIndexOf('.');
    065
    066            if (lastdotx > 0)
    067            {
    068                int prevdotx = file.lastIndexOf('.', lastdotx - 1);
    069
    070                if (prevdotx > 0)
    071                {
    072                    String requestDigest = file.substring(prevdotx + 1, lastdotx);
    073
    074                    // Strip the digest out of the file name.
    075
    076                    String realFile = file.substring(0, prevdotx) + file.substring(lastdotx);
    077
    078                    result = resource.forFile(realFile);
    079
    080                    String actualDigest = digestManager.getDigest(result);
    081
    082                    valid = requestDigest.equals(actualDigest);
    083                }
    084            }
    085
    086            if (valid)
    087                return result;
    088
    089            // TODO: Perhaps we should send an exception here, so that the caller can decide
    090            // to send the error. I'm not happy with this.
    091
    092            response.sendError(HttpServletResponse.SC_FORBIDDEN, ServicesMessages.wrongAssetDigest(result));
    093
    094            return null;
    095        }
    096    }