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}