001/*
002 *  Copyright 2011 Anyware Services
003 *
004 *  Licensed under the Apache License, Version 2.0 (the "License");
005 *  you may not use this file except in compliance with the License.
006 *  You may obtain a copy of the License at
007 *
008 *      http://www.apache.org/licenses/LICENSE-2.0
009 *
010 *  Unless required by applicable law or agreed to in writing, software
011 *  distributed under the License is distributed on an "AS IS" BASIS,
012 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 *  See the License for the specific language governing permissions and
014 *  limitations under the License.
015 */
016package org.ametys.site;
017
018import java.util.Map;
019
020import org.apache.avalon.framework.parameters.Parameters;
021import org.apache.avalon.framework.service.ServiceException;
022import org.apache.avalon.framework.service.ServiceManager;
023import org.apache.avalon.framework.thread.ThreadSafe;
024import org.apache.cocoon.acting.ServiceableAction;
025import org.apache.cocoon.environment.ObjectModelHelper;
026import org.apache.cocoon.environment.Redirector;
027import org.apache.cocoon.environment.Request;
028import org.apache.cocoon.environment.SourceResolver;
029import org.apache.excalibur.source.Source;
030import org.apache.excalibur.source.SourceNotFoundException;
031import org.apache.excalibur.source.TraversableSource;
032import org.apache.excalibur.source.impl.FileSource;
033import org.slf4j.Logger;
034import org.slf4j.LoggerFactory;
035
036import org.ametys.plugins.site.cache.monitoring.Constants;
037import org.ametys.plugins.site.cache.monitoring.process.access.ResourceAccessComponent;
038import org.ametys.plugins.site.cache.monitoring.process.access.impl.FrontResourceAccess;
039
040/**
041 * Blocks until the cacheable status is known.
042 */
043public class IsPageCacheableAction extends ServiceableAction implements ThreadSafe
044{
045    /** The cache access component. */
046    protected CacheAccessManager _cacheAccess;
047
048    /** The resource access monitoring component */
049    protected ResourceAccessComponent _resourceAccessComponent;
050
051    /** The source resolver. */
052    protected SourceResolver _resolver;
053
054    private final Logger _logger = LoggerFactory.getLogger("site.cache.log");
055
056    @Override
057    public void service(ServiceManager serviceManager) throws ServiceException
058    {
059        super.service(serviceManager);
060        _cacheAccess = (CacheAccessManager) serviceManager.lookup(CacheAccessManager.ROLE);
061        _resourceAccessComponent = (ResourceAccessComponent) serviceManager.lookup(ResourceAccessComponent.ROLE);
062    }
063
064    @Override
065    public Map act(Redirector redirector, SourceResolver resolver, Map objectModel, String source, Parameters parameters) throws Exception
066    {
067        try
068        {
069            // Block until known if the page is cacheable.
070            boolean cacheable = _cacheAccess.isCacheable(source);
071
072            Request request = ObjectModelHelper.getRequest(objectModel);
073
074            if (_logger.isDebugEnabled())
075            {
076                String uniqueId = request.getHeader("UNIQUE_ID");
077                if (uniqueId != null)
078                {
079                    _logger.debug(uniqueId + " - cacheable : " + cacheable);
080                }
081            }
082
083            // Resource monitoring
084            FrontResourceAccess fra = (FrontResourceAccess) request.getAttribute(Constants.REQUEST_ATTRIBUTE_PAGEACCESS);
085            fra.setCacheable(cacheable);
086
087            // If the page isn't cacheable, unlock and generate (return null).
088            String resourceURI = parameters.getParameter("uri", "");
089            if (!cacheable)
090            {
091                _resourceAccessComponent.addAccessRecord(fra);
092
093                _cacheAccess.unlock(source);
094                return null;
095            }
096            // If cacheable and the resource exists, we don't have to generate it.
097            else if (cacheable && _resourceExists(resolver, resourceURI))
098            {
099                fra.setCacheHit2(true);
100                _resourceAccessComponent.addAccessRecord(fra);
101
102                _cacheAccess.unlock(source);
103                return EMPTY_MAP;
104            }
105
106            // Else, return null: the page will be generated into the cache and unlocked when it's done.
107            fra.setCacheHit2(false);
108            _resourceAccessComponent.addAccessRecord(fra);
109
110            return null;
111        }
112        catch (Exception e)
113        {
114            _cacheAccess.unlock(source);
115            throw e;
116        }
117    }
118
119    /**
120     * Tests if the resource exists.
121     * @param resolver the resolver.
122     * @param resourceURI the resource uri.
123     * @return true if the resource exists, false otherwise.
124     */
125    protected boolean _resourceExists(SourceResolver resolver, String resourceURI)
126    {
127        boolean exists = false;
128
129        Source source = null;
130        try
131        {
132            source = resolver.resolveURI(resourceURI);
133
134            if (source instanceof FileSource && !SiteCacheHelper.isValid((FileSource) source))
135            {
136                // Release the old source and replace it by a hashed file source.
137                source = SiteCacheHelper.getHashedFileSource(resolver, (FileSource) source);
138            }
139
140            if (source.exists() && (!(source instanceof TraversableSource) || !((TraversableSource) source).isCollection()))
141            {
142                exists = true;
143            }
144        }
145        catch (SourceNotFoundException e)
146        {
147            // Do not log
148        }
149        catch (Exception e)
150        {
151            getLogger().warn("Exception resolving resource " + resourceURI, e);
152        }
153        finally
154        {
155            if (source != null)
156            {
157                resolver.release(source);
158            }
159        }
160
161        return exists;
162    }
163}