001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.wicket.request.resource.caching.version; 018 019import java.io.Serializable; 020import java.util.Collections; 021import java.util.Map; 022import java.util.regex.Pattern; 023 024import org.apache.wicket.request.resource.caching.IStaticCacheableResource; 025import org.apache.wicket.util.collections.MostRecentlyUsedMap; 026import org.apache.wicket.util.lang.Args; 027 028/** 029 * Caches the results of a delegating {@link IResourceVersion} instance 030 * in a member variable. The cache will be valid for the lifetime of 031 * this instance. It will expire the oldest entries if the maximum number 032 * of entries is exceeded. 033 * 034 * @author Peter Ertl 035 * 036 * @since 1.5 037 */ 038public class CachingResourceVersion implements IResourceVersion 039{ 040 /** 041 * default maximum entries in cache 042 */ 043 private static final int DEFAULT_MAX_CACHE_ENTRIES = 5000; 044 045 /** 046 * null value replacement holder for storing <code>null</code> in the map 047 */ 048 private static final String NULL_VALUE = "null"; 049 050 /** 051 * delegating resource version provider 052 */ 053 private final IResourceVersion delegate; 054 055 /** 056 * cache for resource versions 057 */ 058 private final Map<Serializable, String> cache; 059 060 /** 061 * create version cache 062 * <p/> 063 * the cache will accept up to {@value #DEFAULT_MAX_CACHE_ENTRIES} before 064 * evicting the oldest entries. 065 * 066 * @param delegate 067 * delegating resource version provider 068 */ 069 public CachingResourceVersion(IResourceVersion delegate) 070 { 071 this(delegate, DEFAULT_MAX_CACHE_ENTRIES); 072 } 073 074 /** 075 * create version cache 076 * <p/> 077 * the cache will accept a maximum number of entries specified 078 * by <code>maxEntries</code> before evicting the oldest entries. 079 * 080 * @param delegate 081 * resource version provider 082 * @param maxEntries 083 * maximum number of cache entries 084 */ 085 public CachingResourceVersion(IResourceVersion delegate, int maxEntries) 086 { 087 if (maxEntries < 1) 088 { 089 throw new IllegalArgumentException("maxEntries must be greater than zero"); 090 } 091 092 this.delegate = Args.notNull(delegate, "delegate"); 093 this.cache = Collections.synchronizedMap( 094 new MostRecentlyUsedMap<Serializable, String>(maxEntries)); 095 } 096 097 @Override 098 public String getVersion(IStaticCacheableResource resource) 099 { 100 // get unique cache key for resource reference 101 final Serializable key = resource.getCacheKey(); 102 103 // if key can not be determined do not cache 104 if(key == null) 105 { 106 return null; 107 } 108 109 // lookup version in cache 110 String version = cache.get(key); 111 112 // if not found 113 if (version == null) 114 { 115 // get version from delegate 116 version = delegate.getVersion(resource); 117 118 // replace null values with holder 119 if (version == null) 120 { 121 version = NULL_VALUE; 122 } 123 // update cache 124 cache.put(key, version); 125 } 126 127 //noinspection StringEquality 128 if (version == NULL_VALUE) 129 { 130 // replace holder with null value 131 return null; 132 } 133 134 // return version string 135 return version; 136 } 137 138 @Override 139 public Pattern getVersionPattern() { 140 return delegate.getVersionPattern(); 141 } 142 143 /** 144 * remove cacheable resource from cache 145 * 146 * @param resource 147 * cacheable resource 148 */ 149 public void invalidate(IStaticCacheableResource resource) 150 { 151 // get cache key for resource reference 152 final Serializable key = Args.notNull(resource, "resource").getCacheKey(); 153 154 // if key is available purge cache entry 155 if(key != null) 156 { 157 cache.remove(key); 158 } 159 } 160 161 public void invalidateAll() { 162 cache.clear(); 163 } 164}