001 /*
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements. See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership. The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License. You may obtain a copy of the License at
009 *
010 * http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing,
013 * software distributed under the License is distributed on an
014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015 * KIND, either express or implied. See the License for the
016 * specific language governing permissions and limitations
017 * under the License.
018 */
019 package org.apache.shiro.cache;
020
021 import org.apache.shiro.util.Destroyable;
022 import org.apache.shiro.util.LifecycleUtils;
023 import org.apache.shiro.util.StringUtils;
024
025 import java.util.Collection;
026 import java.util.concurrent.ConcurrentHashMap;
027 import java.util.concurrent.ConcurrentMap;
028
029 /**
030 * Very simple abstract {@code CacheManager} implementation that retains all created {@link Cache Cache} instances in
031 * an in-memory {@link ConcurrentMap ConcurrentMap}. {@code Cache} instance creation is left to subclasses via
032 * the {@link #createCache createCache} method implementation.
033 *
034 * @since 1.0
035 */
036 public abstract class AbstractCacheManager implements CacheManager, Destroyable {
037
038 /**
039 * Retains all Cache objects maintained by this cache manager.
040 */
041 private final ConcurrentMap<String, Cache> caches;
042
043 /**
044 * Default no-arg constructor that instantiates an internal name-to-cache {@code ConcurrentMap}.
045 */
046 public AbstractCacheManager() {
047 this.caches = new ConcurrentHashMap<String, Cache>();
048 }
049
050 /**
051 * Returns the cache with the specified {@code name}. If the cache instance does not yet exist, it will be lazily
052 * created, retained for further access, and then returned.
053 *
054 * @param name the name of the cache to acquire.
055 * @return the cache with the specified {@code name}.
056 * @throws IllegalArgumentException if the {@code name} argument is {@code null} or does not contain text.
057 * @throws CacheException if there is a problem lazily creating a {@code Cache} instance.
058 */
059 public <K, V> Cache<K, V> getCache(String name) throws IllegalArgumentException, CacheException {
060 if (!StringUtils.hasText(name)) {
061 throw new IllegalArgumentException("Cache name cannot be null or empty.");
062 }
063
064 Cache cache;
065
066 cache = caches.get(name);
067 if (cache == null) {
068 cache = createCache(name);
069 Cache existing = caches.putIfAbsent(name, cache);
070 if (existing != null) {
071 cache = existing;
072 }
073 }
074
075 //noinspection unchecked
076 return cache;
077 }
078
079 /**
080 * Creates a new {@code Cache} instance associated with the specified {@code name}.
081 *
082 * @param name the name of the cache to create
083 * @return a new {@code Cache} instance associated with the specified {@code name}.
084 * @throws CacheException if the {@code Cache} instance cannot be created.
085 */
086 protected abstract Cache createCache(String name) throws CacheException;
087
088 /**
089 * Cleanup method that first {@link LifecycleUtils#destroy destroys} all of it's managed caches and then
090 * {@link java.util.Map#clear clears} out the internally referenced cache map.
091 *
092 * @throws Exception if any of the managed caches can't destroy properly.
093 */
094 public void destroy() throws Exception {
095 while (!caches.isEmpty()) {
096 for (Cache cache : caches.values()) {
097 LifecycleUtils.destroy(cache);
098 }
099 caches.clear();
100 }
101 }
102
103 public String toString() {
104 Collection<Cache> values = caches.values();
105 StringBuilder sb = new StringBuilder(getClass().getSimpleName())
106 .append(" with ")
107 .append(caches.size())
108 .append(" cache(s)): [");
109 int i = 0;
110 for (Cache cache : values) {
111 if (i > 0) {
112 sb.append(", ");
113 }
114 sb.append(cache.toString());
115 i++;
116 }
117 sb.append("]");
118 return sb.toString();
119 }
120 }