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}