001// Copyright 2011, 2012 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.assets; 016 017import org.apache.tapestry5.commons.Resource; 018import org.apache.tapestry5.commons.util.CollectionFactory; 019import org.apache.tapestry5.internal.TapestryInternalUtils; 020import org.apache.tapestry5.services.assets.ResourceDependencies; 021import org.apache.tapestry5.services.assets.StreamableResource; 022import org.apache.tapestry5.services.assets.StreamableResourceProcessing; 023import org.apache.tapestry5.services.assets.StreamableResourceSource; 024 025import java.io.IOException; 026import java.lang.ref.SoftReference; 027import java.util.Map; 028 029/** 030 * An interceptor for the {@link StreamableResourceSource} service that handles caching of content. 031 */ 032public class SRSCachingInterceptor extends DelegatingSRS 033{ 034 private final Map<Resource, SoftReference<StreamableResource>> cache = CollectionFactory.newConcurrentMap(); 035 036 public SRSCachingInterceptor(StreamableResourceSource delegate, ResourceChangeTracker tracker) 037 { 038 super(delegate); 039 040 tracker.clearOnInvalidation(cache); 041 } 042 043 public StreamableResource getStreamableResource(Resource baseResource, StreamableResourceProcessing processing, ResourceDependencies dependencies) 044 throws IOException 045 { 046 if (!enableCache(processing)) 047 { 048 return delegate.getStreamableResource(baseResource, processing, dependencies); 049 } 050 051 StreamableResource result = TapestryInternalUtils.getAndDeref(cache, baseResource); 052 053 if (result == null) 054 { 055 result = delegate.getStreamableResource(baseResource, processing, dependencies); 056 057 if (isCacheable(result)) 058 { 059 dependencies.addDependency(baseResource); 060 061 cache.put(baseResource, new SoftReference<StreamableResource>(result)); 062 } 063 } 064 065 return result; 066 } 067 068 /** 069 * Always returns true; a subclass may extend this to only cache the resource in some circumstances. 070 * 071 * @param resource 072 * @return true to cache the resource 073 */ 074 protected boolean isCacheable(StreamableResource resource) 075 { 076 return true; 077 } 078 079 /** 080 * Returns true unless the processing is {@link StreamableResourceProcessing#FOR_AGGREGATION}. 081 * Subclasses may override. When the cache is not enabled, the request is passed on to the interceptor's 082 * {@link #delegate}, and no attempt is made to read or update this interceptor's cache. 083 * 084 * @since 5.3.5 085 */ 086 protected boolean enableCache(StreamableResourceProcessing processing) 087 { 088 return processing != StreamableResourceProcessing.FOR_AGGREGATION; 089 } 090}