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
015package org.apache.tapestry5.internal.services;
016
017import org.apache.tapestry5.Asset;
018import org.apache.tapestry5.ioc.Resource;
019import org.apache.tapestry5.ioc.annotations.Marker;
020import org.apache.tapestry5.ioc.internal.util.ClasspathResource;
021import org.apache.tapestry5.services.AssetFactory;
022import org.apache.tapestry5.services.AssetPathConverter;
023import org.apache.tapestry5.services.ClasspathAssetAliasManager;
024import 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)
033public 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}