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    
015    package org.apache.tapestry5.internal.services;
016    
017    import org.apache.tapestry5.SymbolConstants;
018    import org.apache.tapestry5.ioc.annotations.Symbol;
019    import org.apache.tapestry5.ioc.annotations.UsesMappedConfiguration;
020    import org.apache.tapestry5.ioc.internal.util.CollectionFactory;
021    import org.apache.tapestry5.services.ClasspathAssetAliasManager;
022    import org.apache.tapestry5.services.Dispatcher;
023    import org.apache.tapestry5.services.Request;
024    import org.apache.tapestry5.services.Response;
025    import org.apache.tapestry5.services.assets.AssetRequestHandler;
026    
027    import javax.servlet.http.HttpServletResponse;
028    import java.io.IOException;
029    import java.util.Collections;
030    import java.util.Comparator;
031    import java.util.List;
032    import 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)
043    public 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) String applicationFolder)
063        {
064            String folder = applicationFolder.equals("") ? "" : "/" + applicationFolder;
065    
066            this.pathPrefix = folder + RequestConstants.ASSET_PATH_PREFIX + applicationVersion + "/";
067    
068            for (String path : configuration.keySet())
069            {
070                String extendedPath = this.pathPrefix + path + "/";
071    
072                pathToHandler.put(extendedPath, configuration.get(path));
073    
074                assetPaths.add(extendedPath);
075            }
076    
077            // Sort by descending length
078    
079            Collections.sort(assetPaths, new Comparator<String>()
080            {
081                public int compare(String o1, String o2)
082                {
083                    return o2.length() - o1.length();
084                }
085            });
086        }
087    
088        public boolean dispatch(Request request, Response response) throws IOException
089        {
090            String path = request.getPath();
091    
092            // Remember that the request path does not include the context path, so we can simply start
093            // looking for the asset path prefix right off the bat.
094    
095            if (!path.startsWith(pathPrefix))
096            {
097                return false;
098            }
099    
100            for (String extendedPath : assetPaths)
101            {
102    
103                if (path.startsWith(extendedPath))
104                {
105                    AssetRequestHandler handler = pathToHandler.get(extendedPath);
106    
107                    String extraPath = path.substring(extendedPath.length());
108    
109                    boolean handled = handler.handleAssetRequest(request, response, extraPath);
110    
111                    if (handled)
112                    {
113                        return true;
114                    }
115                }
116            }
117    
118            response.sendError(HttpServletResponse.SC_NOT_FOUND, path);
119    
120            return true;
121        }
122    }