001 // Copyright 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.SymbolConstants;
018 import org.apache.tapestry5.ioc.Resource;
019 import org.apache.tapestry5.ioc.annotations.Inject;
020 import org.apache.tapestry5.ioc.annotations.Symbol;
021 import org.apache.tapestry5.ioc.internal.util.ClasspathResource;
022 import org.apache.tapestry5.services.AssetFactory;
023 import org.apache.tapestry5.services.ClasspathAssetAliasManager;
024 import org.apache.tapestry5.services.ContextProvider;
025 import org.apache.tapestry5.services.Response;
026
027 import javax.servlet.http.HttpServletResponse;
028 import java.io.IOException;
029
030 public class AssetResourceLocatorImpl implements AssetResourceLocator
031 {
032
033 private final ClasspathAssetAliasManager aliasManager;
034
035 private final ResourceCache resourceCache;
036
037 private final AssetFactory contextAssetFactory;
038
039 private final Response response;
040
041 private final String applicationAssetPrefix;
042
043 public AssetResourceLocatorImpl(ClasspathAssetAliasManager aliasManager,
044
045 ResourceCache resourceCache,
046
047 @Inject @Symbol(SymbolConstants.APPLICATION_VERSION)
048 String applicationVersion,
049
050 @ContextProvider
051 AssetFactory contextAssetFactory,
052
053 Response response)
054
055 {
056 this.aliasManager = aliasManager;
057 this.resourceCache = resourceCache;
058 this.contextAssetFactory = contextAssetFactory;
059 this.response = response;
060
061 applicationAssetPrefix = RequestConstants.ASSET_PATH_PREFIX + RequestConstants.CONTEXT_FOLDER + applicationVersion + "/";
062 }
063
064 public Resource findResourceForPath(String path) throws IOException
065 {
066 if (path.startsWith(applicationAssetPrefix))
067 return findContextResource(path.substring(applicationAssetPrefix.length()));
068
069 String resourcePath = aliasManager.toResourcePath(path);
070
071 Resource resource = new ClasspathResource(resourcePath);
072
073 if (!resourceCache.requiresDigest(resource)) return resource;
074
075 String file = resource.getFile();
076
077 // Somehow this code got real ugly, but it's all about preventing NPEs when a resource
078 // that should have a digest doesn't.
079
080 boolean valid = false;
081 Resource result = resource;
082
083 int lastdotx = file.lastIndexOf('.');
084
085 if (lastdotx > 0)
086 {
087 int prevdotx = file.lastIndexOf('.', lastdotx - 1);
088
089 if (prevdotx > 0)
090 {
091
092 String requestDigest = file.substring(prevdotx + 1, lastdotx);
093
094 // Strip the digest out of the file name.
095
096 String realFile = file.substring(0, prevdotx) + file.substring(lastdotx);
097
098 result = resource.forFile(realFile);
099
100 String actualDigest = resourceCache.getDigest(result);
101
102 valid = requestDigest.equals(actualDigest);
103 }
104 }
105
106 if (valid) return result;
107
108 // TODO: Perhaps we should send an exception here, so that the caller can decide
109 // to send the error. I'm not happy with this.
110
111 response.sendError(HttpServletResponse.SC_FORBIDDEN,
112 ServicesMessages.wrongAssetDigest(result));
113
114 return null;
115 }
116
117 private Resource findContextResource(String contextPath)
118 {
119 return contextAssetFactory.getRootResource().forFile(contextPath);
120 }
121 }