001// *************************************************************************************************************************** 002// * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file * 003// * distributed with this work for additional information regarding copyright ownership. The ASF licenses this file * 004// * to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance * 005// * with the License. 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 distributed under the License is distributed on an * 010// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the * 011// * specific language governing permissions and limitations under the License. * 012// *************************************************************************************************************************** 013package org.apache.juneau.internal; 014 015import static org.apache.juneau.internal.SystemEnv.*; 016import java.util.concurrent.*; 017import java.util.concurrent.atomic.*; 018import java.util.function.*; 019 020import org.apache.juneau.common.internal.*; 021 022/** 023 * Simple in-memory cache of objects. 024 * 025 * <p> 026 * Essentially just a wrapper around a ConcurrentHashMap. 027 * 028 * <h5 class='section'>See Also:</h5><ul> 029 * </ul> 030 * 031 * @param <K> The key type. 032 * @param <V> The value type. 033 */ 034public class Cache<K,V> { 035 036 //----------------------------------------------------------------------------------------------------------------- 037 // Static 038 //----------------------------------------------------------------------------------------------------------------- 039 040 /** 041 * Static creator. 042 * 043 * @param <K> The key type. 044 * @param <V> The value type. 045 * @param key The key type. 046 * @param type The value type. 047 * @return A new builder for this object. 048 */ 049 public static <K,V> Builder<K,V> of(Class<K> key, Class<V> type) { 050 return new Builder<>(type); 051 } 052 053 //----------------------------------------------------------------------------------------------------------------- 054 // Builder 055 //----------------------------------------------------------------------------------------------------------------- 056 057 /** 058 * Builder class. 059 * 060 * @param <K> The key type. 061 * @param <V> The value type. 062 */ 063 public static class Builder<K,V> { 064 boolean disabled, logOnExit; 065 int maxSize; 066 Class<V> type; 067 068 Builder(Class<V> type) { 069 this.type = type; 070 disabled = env("juneau.cache.disable", false); 071 maxSize = env("juneau.cache.maxSize", 1000); 072 logOnExit = env("juneau.cache.logOnExit", false); 073 } 074 075 /** 076 * Disables this cache. 077 * 078 * @return This object. 079 */ 080 public Builder<K,V> disabled() { 081 disabled = true; 082 return this; 083 } 084 085 /** 086 * When enabled, logs cache hit statistics on this cache. 087 * 088 * @return This object. 089 */ 090 public Builder<K,V> logOnExit() { 091 logOnExit = true; 092 return this; 093 } 094 095 /** 096 * Specifies the maximum size of this cache. 097 * 098 * @param value The value for this setting. 099 * @return This object. 100 */ 101 public Builder<K,V> maxSize(int value) { 102 maxSize = value; 103 return this; 104 } 105 106 /** 107 * Builds this object. 108 * 109 * @return A new cache. 110 */ 111 public Cache<K,V> build() { 112 return new Cache<>(this); 113 } 114 } 115 116 //----------------------------------------------------------------------------------------------------------------- 117 // Instance 118 //----------------------------------------------------------------------------------------------------------------- 119 120 private final int maxSize; 121 private final ConcurrentHashMap<K,V> cache; 122 private final AtomicInteger cacheHits = new AtomicInteger(); 123 124 /** 125 * Constructor 126 * 127 * @param builder The builder for this object. 128 */ 129 protected Cache(Builder<K,V> builder) { 130 cache = builder.disabled ? null : new ConcurrentHashMap<>(); 131 maxSize = builder.maxSize; 132 if (builder.logOnExit) { 133 SystemUtils.shutdownMessage(()->builder.type.getSimpleName() + " cache: hits=" + cacheHits.get() + ", misses: " + cache.size()); 134 } 135 } 136 /** 137 * Retrieves the value with the specified key from this cache. 138 * 139 * @param key The key. 140 * @param supplier The supplier for creating this object if it's not found in the cache. 141 * @return The value. 142 */ 143 public V get(K key, Supplier<V> supplier) { 144 if (cache == null || key == null) 145 return supplier.get(); 146 V v = cache.get(key); 147 if (v == null) { 148 if (cache.size() > maxSize) 149 cache.clear(); 150 v = supplier.get(); 151 cache.putIfAbsent(key, v); 152 } else { 153 cacheHits.incrementAndGet(); 154 } 155 return v; 156 } 157}