001/*-------------------------------------------------------------------------+
002|                                                                          |
003| Copyright 2005-2011 The ConQAT Project                                   |
004|                                                                          |
005| Licensed under the Apache License, Version 2.0 (the "License");          |
006| you may not use this file except in compliance with the License.         |
007| 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.conqat.lib.commons.cache4j;
018
019import java.util.ArrayList;
020import java.util.Collections;
021import java.util.List;
022
023import org.conqat.lib.commons.cache4j.backend.ICacheBackend;
024import org.conqat.lib.commons.error.NeverThrownRuntimeException;
025import org.conqat.lib.commons.factory.IParameterizedFactory;
026
027/**
028 * Implementation of a cache that manages a separate backend for each thread,
029 * thus isolating threads from each other. Note that accessing this cache from
030 * many temporary threads can lead to memory leaks (as the cached data is still
031 * stored) for certain backends.
032 * 
033 * @param <K>
034 *            the key type. This must have both {@link Object#equals(Object)}
035 *            and {@link Object#hashCode()} correctly implemented.
036 * @param <V>
037 *            the value type. It is generally recommended to use an immutable
038 *            type here, but this is not required.
039 * @param <X>
040 *            the type of exception thrown. If no exception will be throws, use
041 *            {@link NeverThrownRuntimeException}.
042 */
043public class ThreadLocalCache<K, V, X extends Exception> implements ICache<K, V, X> {
044
045        /** The name of this cache (used for reporting). */
046        private final String name;
047
048        /** The factory used to obtain elements. */
049        private final IParameterizedFactory<V, K, X> factory;
050
051        /** The backend used. */
052        private final ICacheBackend<K, V> backend;
053
054        /** Stores all sub caches for reporting. */
055        private final List<BasicCache<K, V, X>> subCaches = Collections
056                        .synchronizedList(new ArrayList<BasicCache<K, V, X>>());
057
058        /** Manages the individual caches for each thread. */
059        private final ThreadLocal<BasicCache<K, V, X>> localCaches = new ThreadLocal<BasicCache<K, V, X>>() {
060                /** {@inheritDoc} */
061                @Override
062                protected BasicCache<K, V, X> initialValue() {
063                        BasicCache<K, V, X> cache = new BasicCache<K, V, X>(name + ":" + Thread.currentThread().getId(), factory,
064                                        backend.newInstance());
065                        subCaches.add(cache);
066                        return cache;
067                }
068        };
069
070        /**
071         * Constructor.
072         * 
073         * @param factory
074         *            the factory has to either support multi-threaded access or to
075         *            be synchronized!
076         */
077        public ThreadLocalCache(String name, IParameterizedFactory<V, K, X> factory, ICacheBackend<K, V> backend) {
078                this.name = name;
079                this.factory = factory;
080                this.backend = backend;
081        }
082
083        /** {@inheritDoc} */
084        @Override
085        public V obtain(K parameter) throws X {
086                return localCaches.get().obtain(parameter);
087        }
088
089        /** {@inheritDoc} */
090        @Override
091        public void clear(boolean allThreads) {
092                if (allThreads) {
093                        for (BasicCache<K, V, X> cache : subCaches) {
094                                cache.clear(false);
095                        }
096                } else {
097                        localCaches.get().clear(false);
098                }
099        }
100
101        /** {@inheritDoc} */
102        @Override
103        public String getName() {
104                return name;
105        }
106
107        /** {@inheritDoc} */
108        @Override
109        public int getHits() {
110                int result = 0;
111                for (BasicCache<K, V, X> cache : subCaches) {
112                        result += cache.getHits();
113                }
114                return result;
115        }
116
117        /** {@inheritDoc} */
118        @Override
119        public int getMisses() {
120                int result = 0;
121                for (BasicCache<K, V, X> cache : subCaches) {
122                        result += cache.getMisses();
123                }
124                return result;
125        }
126
127        /** {@inheritDoc} */
128        @Override
129        public long getMissCostMillis() {
130                long result = 0;
131                for (BasicCache<K, V, X> cache : subCaches) {
132                        result += cache.getMissCostMillis();
133                }
134                return result;
135        }
136}