    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    }