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 if (_logger.isDebugEnabled()) 113 { 114 String uniqueId = request.getHeader("UNIQUE_ID"); 115 if (uniqueId != null) 116 { 117 _logger.debug(uniqueId + " - cacheable : " + cacheable); 118 } 119 } 120 121 // Resource monitoring 122 FrontResourceAccess fra = (FrontResourceAccess) request.getAttribute(Constants.REQUEST_ATTRIBUTE_PAGEACCESS); 123 fra.setCacheable(cacheable); 124 125 // If the page isn't cacheable, unlock and generate (return null). 126 String resourceURI = parameters.getParameter("uri", ""); 127 if (!cacheable) 128 { 129 _getResourceAccessComponent().addAccessRecord(fra); 130 131 _getCacheAccessManager().unlock(source); 132 return null; 133 } 134 // If cacheable and the resource exists, we don't have to generate it. 135 else if (cacheable && _resourceExists(resolver, resourceURI)) 136 { 137 fra.setCacheHit2(true); 138 _getResourceAccessComponent().addAccessRecord(fra); 139 140 _getCacheAccessManager().unlock(source); 141 return EMPTY_MAP; 142 } 143 144 // Else, return null: the page will be generated into the cache and unlocked when it's done. 145 fra.setCacheHit2(false); 146 _getResourceAccessComponent().addAccessRecord(fra); 147 148 return null; 149 } 150 catch (Exception e) 151 { 152 _getCacheAccessManager().unlock(source); 153 throw e; 154 } 155 } 156 157 /** 158 * Tests if the resource exists. 159 * @param resolver the resolver. 160 * @param resourceURI the resource uri. 161 * @return true if the resource exists, false otherwise. 162 */ 163 protected boolean _resourceExists(SourceResolver resolver, String resourceURI) 164 { 165 boolean exists = false; 166 167 Source source = null; 168 try 169 { 170 source = resolver.resolveURI(resourceURI); 171 172 if (source instanceof FileSource && !SiteCacheHelper.isValid((FileSource) source)) 173 { 174 // Release the old source and replace it by a hashed file source. 175 source = SiteCacheHelper.getHashedFileSource(resolver, (FileSource) source); 176 } 177 178 if (source.exists() && (!(source instanceof TraversableSource) || !((TraversableSource) source).isCollection())) 179 { 180 exists = true; 181 } 182 } 183 catch (SourceNotFoundException e) 184 { 185 // Do not log 186 } 187 catch (Exception e) 188 { 189 getLogger().warn("Exception resolving resource " + resourceURI, e); 190 } 191 finally 192 { 193 if (source != null) 194 { 195 resolver.release(source); 196 } 197 } 198 199 return exists; 200 } 201}