    001    // Copyright 2006, 2007, 2008, 2009, 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.Asset;
    018    import org.apache.tapestry5.ioc.Resource;
    019    import org.apache.tapestry5.ioc.annotations.Marker;
    020    import org.apache.tapestry5.ioc.internal.util.ClasspathResource;
    021    import org.apache.tapestry5.services.AssetFactory;
    022    import org.apache.tapestry5.services.AssetPathConverter;
    023    import org.apache.tapestry5.services.ClasspathAssetAliasManager;
    024    import org.apache.tapestry5.services.ClasspathProvider;
    025
    026    /**
    027     * Generates Assets for files on the classpath. Caches generated client URLs internally, and clears that cache when
    028     * notified to do so by the {@link ResourceDigestManager}.
    029     *
    030     * @see AssetDispatcher
    031     */
    032    @Marker(ClasspathProvider.class)
    033    public class ClasspathAssetFactory implements AssetFactory
    034    {
    035        private final ResourceDigestManager digestManager;
    036
    037        private final ClasspathAssetAliasManager aliasManager;
    038
    039        private final ClasspathResource rootResource;
    040
    041        private final AssetPathConverter converter;
    042
    043        private final boolean invariant;
    044
    045        public ClasspathAssetFactory(ResourceDigestManager digestManager, ClasspathAssetAliasManager aliasManager,
    046                                     AssetPathConverter converter)
    047        {
    048            this.digestManager = digestManager;
    049            this.aliasManager = aliasManager;
    050            this.converter = converter;
    051
    052            rootResource = new ClasspathResource("");
    053
    054            invariant = converter.isInvariant();
    055        }
    056
    057        private String clientURL(Resource resource)
    058        {
    059            String defaultPath = buildDefaultPath(resource);
    060
    061            return converter.convertAssetPath(defaultPath);
    062        }
    063
    064        private String buildDefaultPath(Resource resource)
    065        {
    066            boolean requiresDigest = digestManager.requiresDigest(resource);
    067
    068            String path = resource.getPath();
    069
    070            if (requiresDigest)
    071            {
    072                // Resources with extensions go from foo/bar/baz.txt --> foo/bar/baz.CHECKSUM.txt
    073
    074                int lastdotx = path.lastIndexOf('.');
    075
    076                path = path.substring(0, lastdotx + 1) + digestManager.getDigest(resource) + path.substring(lastdotx);
    077            }
    078
    079            return aliasManager.toClientURL(path);
    080        }
    081
    082        public Asset createAsset(Resource resource)
    083        {
    084            if (invariant)
    085            {
    086                return createInvariantAsset(resource);
    087            }
    088
    089            return createVariantAsset(resource);
    090        }
    091
    092        /**
    093         * A variant asset must pass the resource through clientURL() all the time; very inefficient.
    094         */
    095        private Asset createVariantAsset(final Resource resource)
    096        {
    097            return new AbstractAsset(false)
    098            {
    099                public Resource getResource()
    100                {
    101                    return resource;
    102                }
    103
    104                public String toClientURL()
    105                {
    106                    return clientURL(resource);
    107                }
    108            };
    109        }
    110
    111        /**
    112         * An invariant asset is normal, and only needs to compute the clientURL for the resource once.
    113         */
    114        private Asset createInvariantAsset(final Resource resource)
    115        {
    116            return new AbstractAsset(true)
    117            {
    118                private String clientURL;
    119
    120                public Resource getResource()
    121                {
    122                    return resource;
    123                }
    124
    125                public String toClientURL()
    126                {
    127                    if (clientURL == null)
    128                    {
    129                        clientURL = clientURL(resource);
    130                    }
    131
    132                    return clientURL;
    133                }
    134            };
    135        }
    136
    137        public Resource getRootResource()
    138        {
    139            return rootResource;
    140        }
    141    }