001    // Copyright 2006, 2008, 2009 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 org.apache.tapestry5.ioc.Resource;
018    import org.apache.tapestry5.services.ClasspathAssetAliasManager;
019    import org.apache.tapestry5.services.Dispatcher;
020    import org.apache.tapestry5.services.Request;
021    import org.apache.tapestry5.services.Response;
022    
023    import javax.servlet.http.HttpServletResponse;
024    import java.io.IOException;
025    
026    /**
027     * Recognizes requests where the path begins with "/asset/" and delivers the content therein as a bytestream. Also
028     * handles requests that are simply polling for a change to the file.
029     *
030     * @see ResourceStreamer
031     * @see ClasspathAssetAliasManager
032     * @see ResourceCache
033     */
034    public class AssetDispatcher implements Dispatcher
035    {
036        private final ResourceStreamer streamer;
037    
038        private final ResourceCache resourceCache;
039    
040        private final AssetResourceLocator assetResourceLocator;
041    
042        static final String IF_MODIFIED_SINCE_HEADER = "If-Modified-Since";
043    
044        public AssetDispatcher(ResourceStreamer streamer,
045    
046                               ResourceCache resourceCache,
047    
048                               AssetResourceLocator assetResourceLocator)
049        {
050            this.streamer = streamer;
051            this.resourceCache = resourceCache;
052            this.assetResourceLocator = assetResourceLocator;
053    
054        }
055    
056        public boolean dispatch(Request request, Response response) throws IOException
057        {
058            String path = request.getPath();
059    
060            // Remember that the request path does not include the context path, so we can simply start
061            // looking for the asset path prefix right off the bat.
062    
063            if (!path.startsWith(RequestConstants.ASSET_PATH_PREFIX)) return false;
064    
065            // ClassLoaders like their paths to start with a leading slash.
066    
067            Resource resource = assetResourceLocator.findResourceForPath(path);
068    
069            if (resource == null) return true;
070    
071            if (!resource.exists())
072            {
073                response.sendError(HttpServletResponse.SC_NOT_FOUND, ServicesMessages
074                        .assetDoesNotExist(resource));
075                return true;
076            }
077    
078            long ifModifiedSince = 0;
079    
080            try
081            {
082                ifModifiedSince = request.getDateHeader(IF_MODIFIED_SINCE_HEADER);
083            }
084            catch (IllegalArgumentException ex)
085            {
086                // Simulate the header being missing if it is poorly formatted.
087    
088                ifModifiedSince = -1;
089            }
090    
091            if (ifModifiedSince > 0)
092            {
093                long modified = resourceCache.getTimeModified(resource);
094    
095                if (ifModifiedSince >= modified)
096                {
097                    response.sendError(HttpServletResponse.SC_NOT_MODIFIED, "");
098                    return true;
099                }
100            }
101    
102            streamer.streamResource(resource);
103    
104            return true;
105        }
106    }