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.juneau.internal; 018 019import static org.apache.juneau.common.utils.Utils.*; 020 021import java.util.concurrent.*; 022import java.util.concurrent.atomic.*; 023import java.util.function.*; 024 025import org.apache.juneau.common.utils.*; 026 027/** 028 * Simple in-memory cache of objects. 029 * 030 * <p> 031 * Essentially just a wrapper around a ConcurrentHashMap. 032 * 033 * <h5 class='section'>See Also:</h5><ul> 034 * </ul> 035 * 036 * @param <K> The key type. 037 * @param <V> The value type. 038 */ 039public class Cache<K,V> { 040 041 //----------------------------------------------------------------------------------------------------------------- 042 // Static 043 //----------------------------------------------------------------------------------------------------------------- 044 045 /** 046 * Static creator. 047 * 048 * @param <K> The key type. 049 * @param <V> The value type. 050 * @param key The key type. 051 * @param type The value type. 052 * @return A new builder for this object. 053 */ 054 public static <K,V> Builder<K,V> of(Class<K> key, Class<V> type) { 055 return new Builder<>(type); 056 } 057 058 //----------------------------------------------------------------------------------------------------------------- 059 // Builder 060 //----------------------------------------------------------------------------------------------------------------- 061 062 /** 063 * Builder class. 064 * 065 * @param <K> The key type. 066 * @param <V> The value type. 067 */ 068 public static class Builder<K,V> { 069 boolean disabled, logOnExit; 070 int maxSize; 071 Class<V> type; 072 073 Builder(Class<V> type) { 074 this.type = type; 075 disabled = env("juneau.cache.disable", false); 076 maxSize = env("juneau.cache.maxSize", 1000); 077 logOnExit = env("juneau.cache.logOnExit", false); 078 } 079 080 /** 081 * Disables this cache. 082 * 083 * @return This object. 084 */ 085 public Builder<K,V> disabled() { 086 disabled = true; 087 return this; 088 } 089 090 /** 091 * When enabled, logs cache hit statistics on this cache. 092 * 093 * @return This object. 094 */ 095 public Builder<K,V> logOnExit() { 096 logOnExit = true; 097 return this; 098 } 099 100 /** 101 * Specifies the maximum size of this cache. 102 * 103 * @param value The value for this setting. 104 * @return This object. 105 */ 106 public Builder<K,V> maxSize(int value) { 107 maxSize = value; 108 return this; 109 } 110 111 /** 112 * Builds this object. 113 * 114 * @return A new cache. 115 */ 116 public Cache<K,V> build() { 117 return new Cache<>(this); 118 } 119 } 120 121 //----------------------------------------------------------------------------------------------------------------- 122 // Instance 123 //----------------------------------------------------------------------------------------------------------------- 124 125 private final int maxSize; 126 private final ConcurrentHashMap<K,V> cache; 127 private final AtomicInteger cacheHits = new AtomicInteger(); 128 129 /** 130 * Constructor 131 * 132 * @param builder The builder for this object. 133 */ 134 protected Cache(Builder<K,V> builder) { 135 cache = builder.disabled ? null : new ConcurrentHashMap<>(); 136 maxSize = builder.maxSize; 137 if (builder.logOnExit) { 138 SystemUtils.shutdownMessage(()->builder.type.getSimpleName() + " cache: hits=" + cacheHits.get() + ", misses: " + cache.size()); 139 } 140 } 141 /** 142 * Retrieves the value with the specified key from this cache. 143 * 144 * @param key The key. 145 * @param supplier The supplier for creating this object if it's not found in the cache. 146 * @return The value. 147 */ 148 public V get(K key, Supplier<V> supplier) { 149 if (cache == null || key == null) 150 return supplier.get(); 151 V v = cache.get(key); 152 if (v == null) { 153 if (cache.size() > maxSize) 154 cache.clear(); 155 v = supplier.get(); 156 cache.putIfAbsent(key, v); 157 } else { 158 cacheHits.incrementAndGet(); 159 } 160 return v; 161 } 162}