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)
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    }