001package org.apache.commons.jcs3.engine.memory.soft; 002 003/* 004 * Licensed to the Apache Software Foundation (ASF) under one 005 * or more contributor license agreements. See the NOTICE file 006 * distributed with this work for additional information 007 * regarding copyright ownership. The ASF licenses this file 008 * to you under the Apache License, Version 2.0 (the 009 * "License"); you may not use this file except in compliance 010 * with the License. You may obtain a copy of the License at 011 * 012 * http://www.apache.org/licenses/LICENSE-2.0 013 * 014 * Unless required by applicable law or agreed to in writing, 015 * software distributed under the License is distributed on an 016 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 017 * KIND, either express or implied. See the License for the 018 * specific language governing permissions and limitations 019 * under the License. 020 */ 021 022import java.io.IOException; 023import java.lang.ref.SoftReference; 024import java.util.HashSet; 025import java.util.List; 026import java.util.Map; 027import java.util.Set; 028import java.util.concurrent.ConcurrentHashMap; 029import java.util.concurrent.ConcurrentMap; 030import java.util.concurrent.LinkedBlockingQueue; 031 032import org.apache.commons.jcs3.engine.behavior.ICacheElement; 033import org.apache.commons.jcs3.engine.behavior.ICompositeCacheAttributes; 034import org.apache.commons.jcs3.engine.control.CompositeCache; 035import org.apache.commons.jcs3.engine.memory.AbstractMemoryCache; 036import org.apache.commons.jcs3.engine.memory.util.MemoryElementDescriptor; 037import org.apache.commons.jcs3.engine.memory.util.SoftReferenceElementDescriptor; 038import org.apache.commons.jcs3.engine.stats.StatElement; 039import org.apache.commons.jcs3.engine.stats.behavior.IStatElement; 040import org.apache.commons.jcs3.engine.stats.behavior.IStats; 041import org.apache.commons.jcs3.log.Log; 042import org.apache.commons.jcs3.log.LogManager; 043 044/** 045 * A JCS IMemoryCache that has {@link SoftReference} to all its values. 046 * This cache does not respect {@link ICompositeCacheAttributes#getMaxObjects()} 047 * as overflowing is handled by Java GC. 048 * <p> 049 * The cache also has strong references to a maximum number of objects given by 050 * the maxObjects parameter 051 * </p> 052 */ 053public class SoftReferenceMemoryCache<K, V> extends AbstractMemoryCache<K, V> 054{ 055 /** The logger. */ 056 private static final Log log = LogManager.getLog(SoftReferenceMemoryCache.class); 057 058 /** 059 * Strong references to the maxObjects number of newest objects. 060 * <p> 061 * Trimming is done by {@link #trimStrongReferences()} instead of by 062 * overriding removeEldestEntry to be able to control waterfalling as easy 063 * as possible 064 */ 065 private LinkedBlockingQueue<ICacheElement<K, V>> strongReferences; 066 067 /** 068 * For post reflection creation initialization 069 * <p> 070 * @param hub 071 */ 072 @Override 073 public synchronized void initialize( final CompositeCache<K, V> hub ) 074 { 075 super.initialize( hub ); 076 strongReferences = new LinkedBlockingQueue<>(); 077 log.info( "initialized Soft Reference Memory Cache for {0}", 078 this::getCacheName ); 079 } 080 081 /** 082 * @see org.apache.commons.jcs3.engine.memory.AbstractMemoryCache#createMap() 083 */ 084 @Override 085 public ConcurrentMap<K, MemoryElementDescriptor<K, V>> createMap() 086 { 087 return new ConcurrentHashMap<>(); 088 } 089 090 /** 091 * @see org.apache.commons.jcs3.engine.memory.behavior.IMemoryCache#getKeySet() 092 */ 093 @Override 094 public Set<K> getKeySet() 095 { 096 final Set<K> keys = new HashSet<>(); 097 for (final Map.Entry<K, MemoryElementDescriptor<K, V>> e : map.entrySet()) 098 { 099 final SoftReferenceElementDescriptor<K, V> sred = (SoftReferenceElementDescriptor<K, V>) e.getValue(); 100 if (sred.getCacheElement() != null) 101 { 102 keys.add(e.getKey()); 103 } 104 } 105 106 return keys; 107 } 108 109 /** 110 * Returns the current cache size. 111 * <p> 112 * @return The size value 113 */ 114 @Override 115 public int getSize() 116 { 117 int size = 0; 118 for (final MemoryElementDescriptor<K, V> me : map.values()) 119 { 120 final SoftReferenceElementDescriptor<K, V> sred = (SoftReferenceElementDescriptor<K, V>) me; 121 if (sred.getCacheElement() != null) 122 { 123 size++; 124 } 125 } 126 return size; 127 } 128 129 /** 130 * @return statistics about the cache 131 */ 132 @Override 133 public IStats getStatistics() 134 { 135 final IStats stats = super.getStatistics(); 136 stats.setTypeName("Soft Reference Memory Cache"); 137 138 final List<IStatElement<?>> elems = stats.getStatElements(); 139 final int emptyrefs = map.size() - getSize(); 140 elems.add(new StatElement<>("Empty References", Integer.valueOf(emptyrefs))); 141 elems.add(new StatElement<>("Strong References", Integer.valueOf(strongReferences.size()))); 142 143 return stats; 144 } 145 146 /** 147 * Update control structures after get 148 * (guarded by the lock) 149 * 150 * @param me the memory element descriptor 151 */ 152 @Override 153 protected void lockedGetElement(final MemoryElementDescriptor<K, V> me) 154 { 155 final ICacheElement<K, V> val = me.getCacheElement(); 156 val.getElementAttributes().setLastAccessTimeNow(); 157 158 // update the ordering of the strong references 159 strongReferences.add(val); 160 trimStrongReferences(); 161 } 162 163 /** 164 * Remove element from control structure 165 * (guarded by the lock) 166 * 167 * @param me the memory element descriptor 168 */ 169 @Override 170 protected void lockedRemoveElement(final MemoryElementDescriptor<K, V> me) 171 { 172 strongReferences.remove(me.getCacheElement()); 173 } 174 175 /** 176 * Removes all cached items from the cache control structures. 177 * (guarded by the lock) 178 */ 179 @Override 180 protected void lockedRemoveAll() 181 { 182 strongReferences.clear(); 183 } 184 185 /** 186 * Puts an item to the cache. 187 * <p> 188 * @param ce Description of the Parameter 189 * @throws IOException Description of the Exception 190 */ 191 @Override 192 public void update(final ICacheElement<K, V> ce) throws IOException 193 { 194 putCnt.incrementAndGet(); 195 ce.getElementAttributes().setLastAccessTimeNow(); 196 197 lock.lock(); 198 199 try 200 { 201 map.put(ce.getKey(), new SoftReferenceElementDescriptor<>(ce)); 202 strongReferences.add(ce); 203 trimStrongReferences(); 204 } 205 finally 206 { 207 lock.unlock(); 208 } 209 } 210 211 /** 212 * Trim the number of strong references to equal or below the number given 213 * by the maxObjects parameter. 214 */ 215 private void trimStrongReferences() 216 { 217 final int max = getCacheAttributes().getMaxObjects(); 218 final int startsize = strongReferences.size(); 219 220 for (int cursize = startsize; cursize > max; cursize--) 221 { 222 final ICacheElement<K, V> ce = strongReferences.poll(); 223 waterfal(ce); 224 } 225 } 226 227 /** 228 * This can't be implemented. 229 * <p> 230 * @param numberToFree 231 * @return 0 232 * @throws IOException 233 */ 234 @Override 235 public int freeElements(final int numberToFree) throws IOException 236 { 237 return 0; 238 } 239}