001// Copyright 2006, 2008, 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
015package org.apache.tapestry5.internal.services;
016
017import org.apache.tapestry5.SymbolConstants;
018import org.apache.tapestry5.ioc.annotations.Symbol;
019import org.apache.tapestry5.ioc.annotations.UsesMappedConfiguration;
020import org.apache.tapestry5.ioc.internal.util.CollectionFactory;
021import org.apache.tapestry5.services.ClasspathAssetAliasManager;
022import org.apache.tapestry5.services.Dispatcher;
023import org.apache.tapestry5.services.Request;
024import org.apache.tapestry5.services.Response;
025import org.apache.tapestry5.services.assets.AssetRequestHandler;
026
027import javax.servlet.http.HttpServletResponse;
028import java.io.IOException;
029import java.util.Collections;
030import java.util.Comparator;
031import java.util.List;
032import java.util.Map;
033
034/**
035 * Recognizes requests where the path begins with "/asset/" and delivers the content therein as a bytestream. Also
036 * handles requests that are simply polling for a change to the file.
037 *
038 * @see ResourceStreamer
039 * @see ClasspathAssetAliasManager
040 * @see AssetRequestHandler
041 */
042@UsesMappedConfiguration(AssetRequestHandler.class)
043public class AssetDispatcher implements Dispatcher
044{
045    /**
046     * Keyed on extended path name, which includes the pathPrefix first and a trailing slash.
047     */
048    private final Map<String, AssetRequestHandler> pathToHandler = CollectionFactory.newMap();
049
050    /**
051     * List of path prefixes in the pathToHandler, sorted be descending length.
052     */
053    private final List<String> assetPaths = CollectionFactory.newList();
054
055    private final String pathPrefix;
056
057    public AssetDispatcher(Map<String, AssetRequestHandler> configuration,
058
059                           @Symbol(SymbolConstants.APPLICATION_VERSION)
060                           String applicationVersion,
061
062                           @Symbol(SymbolConstants.APPLICATION_FOLDER)
063                           String applicationFolder,
064
065                           @Symbol(SymbolConstants.ASSET_PATH_PREFIX)
066                           String assetPathPrefix
067                           )
068    {
069        String folder = applicationFolder.equals("") ? "" : "/" + applicationFolder;
070
071        this.pathPrefix = folder + assetPathPrefix + applicationVersion + "/";
072
073        for (String path : configuration.keySet())
074        {
075            String extendedPath = this.pathPrefix + path + "/";
076
077            pathToHandler.put(extendedPath, configuration.get(path));
078
079            assetPaths.add(extendedPath);
080        }
081
082        // Sort by descending length
083
084        Collections.sort(assetPaths, new Comparator<String>()
085        {
086            public int compare(String o1, String o2)
087            {
088                return o2.length() - o1.length();
089            }
090        });
091    }
092
093    public boolean dispatch(Request request, Response response) throws IOException
094    {
095        String path = request.getPath();
096
097        // Remember that the request path does not include the context path, so we can simply start
098        // looking for the asset path prefix right off the bat.
099
100        if (!path.startsWith(pathPrefix))
101        {
102            return false;
103        }
104
105        for (String extendedPath : assetPaths)
106        {
107
108            if (path.startsWith(extendedPath))
109            {
110                AssetRequestHandler handler = pathToHandler.get(extendedPath);
111
112                String extraPath = path.substring(extendedPath.length());
113
114                boolean handled = handler.handleAssetRequest(request, response, extraPath);
115
116                if (handled)
117                {
118                    return true;
119                }
120            }
121        }
122
123        response.sendError(HttpServletResponse.SC_NOT_FOUND, path);
124
125        return true;
126    }
127}