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 }