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.thread.ThreadSafe;
023import org.apache.cocoon.acting.ServiceableAction;
024import org.apache.cocoon.environment.ObjectModelHelper;
025import org.apache.cocoon.environment.Redirector;
026import org.apache.cocoon.environment.Request;
027import org.apache.cocoon.environment.SourceResolver;
028import org.apache.excalibur.source.Source;
029import org.apache.excalibur.source.SourceNotFoundException;
030import org.apache.excalibur.source.TraversableSource;
031import org.apache.excalibur.source.impl.FileSource;
032import org.slf4j.Logger;
033import org.slf4j.LoggerFactory;
034
035import org.ametys.plugins.site.cache.monitoring.Constants;
036import org.ametys.plugins.site.cache.monitoring.process.access.ResourceAccessComponent;
037import org.ametys.plugins.site.cache.monitoring.process.access.impl.FrontResourceAccess;
038
039/**
040 * Blocks until the cacheable status is known.
041 */
042public class IsPageCacheableAction extends ServiceableAction implements ThreadSafe
043{
044    /** The cache access component. */
045    protected CacheAccessManager _cacheAccess;
046
047    /** The resource access monitoring component */
048    protected ResourceAccessComponent _resourceAccessComponent;
049
050    /** The source resolver. */
051    protected SourceResolver _resolver;
052
053    private final Logger _logger = LoggerFactory.getLogger("site.cache.log");
054
055    /**
056     * Get the cache access manager
057     * @return the CacheAccessManager
058     */
059    protected CacheAccessManager _getCacheAccessManager()
060    {
061        if (_cacheAccess == null)
062        {
063            try
064            {
065                _cacheAccess = (CacheAccessManager) manager.lookup(CacheAccessManager.ROLE);
066            }
067            catch (ServiceException e)
068            {
069                throw new IllegalStateException("Cannot get CacheAccessManager", e);
070            }
071        }
072        return _cacheAccess;
073    }
074
075    /**
076     * Get the resource access component
077     * @return the ResourceAccessComponent
078     */
079    protected ResourceAccessComponent _getResourceAccessComponent()
080    {
081        if (_resourceAccessComponent == null)
082        {
083            try
084            {
085                _resourceAccessComponent = (ResourceAccessComponent) manager.lookup(ResourceAccessComponent.ROLE);
086            }
087            catch (ServiceException e)
088            {
089                throw new IllegalStateException("Cannot get ResourceAccessComponent", e);
090            }
091        }
092        return _resourceAccessComponent;
093    }
094    
095    @Override
096    public Map act(Redirector redirector, SourceResolver resolver, Map objectModel, String source, Parameters parameters) throws Exception
097    {
098        Request request = ObjectModelHelper.getRequest(objectModel);
099
100        String method = request.getMethod();
101        
102        if (!method.equals("GET"))
103        {
104            return null;
105        }
106        
107        try
108        {
109            // Block until known if the page is cacheable.
110            boolean cacheable = _getCacheAccessManager().isCacheable(source);
111
112
113            if (_logger.isDebugEnabled())
114            {
115                String uniqueId = request.getHeader("UNIQUE_ID");
116                if (uniqueId != null)
117                {
118                    _logger.debug(uniqueId + " - cacheable : " + cacheable);
119                }
120            }
121
122            // Resource monitoring
123            FrontResourceAccess fra = (FrontResourceAccess) request.getAttribute(Constants.REQUEST_ATTRIBUTE_PAGEACCESS);
124            fra.setCacheable(cacheable);
125
126            // If the page isn't cacheable, unlock and generate (return null).
127            String resourceURI = parameters.getParameter("uri", "");
128            if (!cacheable)
129            {
130                _getResourceAccessComponent().addAccessRecord(fra);
131
132                _getCacheAccessManager().unlock(source);
133                return null;
134            }
135            // If cacheable and the resource exists, we don't have to generate it.
136            else if (cacheable && _resourceExists(resolver, resourceURI))
137            {
138                fra.setCacheHit2(true);
139                _getResourceAccessComponent().addAccessRecord(fra);
140
141                _getCacheAccessManager().unlock(source);
142                return EMPTY_MAP;
143            }
144
145            // Else, return null: the page will be generated into the cache and unlocked when it's done.
146            fra.setCacheHit2(false);
147            _getResourceAccessComponent().addAccessRecord(fra);
148
149            return null;
150        }
151        catch (Exception e)
152        {
153            _getCacheAccessManager().unlock(source);
154            throw e;
155        }
156    }
157
158    /**
159     * Tests if the resource exists.
160     * @param resolver the resolver.
161     * @param resourceURI the resource uri.
162     * @return true if the resource exists, false otherwise.
163     */
164    protected boolean _resourceExists(SourceResolver resolver, String resourceURI)
165    {
166        boolean exists = false;
167
168        Source source = null;
169        try
170        {
171            source = resolver.resolveURI(resourceURI);
172
173            if (source instanceof FileSource && !SiteCacheHelper.isValid((FileSource) source))
174            {
175                // Release the old source and replace it by a hashed file source.
176                source = SiteCacheHelper.getHashedFileSource(resolver, (FileSource) source);
177            }
178
179            if (source.exists() && (!(source instanceof TraversableSource) || !((TraversableSource) source).isCollection()))
180            {
181                exists = true;
182            }
183        }
184        catch (SourceNotFoundException e)
185        {
186            // Do not log
187        }
188        catch (Exception e)
189        {
190            getLogger().warn("Exception resolving resource " + resourceURI, e);
191        }
192        finally
193        {
194            if (source != null)
195            {
196                resolver.release(source);
197            }
198        }
199
200        return exists;
201    }
202}