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 org.conqat.lib.commons.cache4j.backend.ICacheBackend;
020import org.conqat.lib.commons.error.NeverThrownRuntimeException;
021import org.conqat.lib.commons.factory.IParameterizedFactory;
022
023/**
024 * Basic implementation of a cache.
025 * 
026 * @param <K>
027 *            the key type. This must have both {@link Object#equals(Object)}
028 *            and {@link Object#hashCode()} correctly implemented.
029 * @param <V>
030 *            the value type. It is generally recommended to use an immutable
031 *            type here, but this is not required.
032 * @param <X>
033 *            the type of exception thrown. If no exception will be throws, use
034 *            {@link NeverThrownRuntimeException}.
035 */
036public class BasicCache<K, V, X extends Exception> implements ICache<K, V, X> {
037
038        /** The name of this cache (e.g. used for statistics). */
039        private final String name;
040
041        /** The cache backend. */
042        private ICacheBackend<K, V> backend;
043
044        /** The factory used to create new elements. */
045        private final IParameterizedFactory<V, K, X> factory;
046
047        /**
048         * Counts the number of cache hits. This variable is not synchronized in any
049         * way, so multi-threaded usage of this class may lead to incorrect results.
050         * However, the amount of error is small enough to be tolerated, while the
051         * performance impact of synchronization is not.
052         */
053        private int hits = 0;
054
055        /**
056         * Counts the number of cache misses. This variable is not synchronized in
057         * any way, so multi-threaded usage of this class may lead to incorrect
058         * results. However, the amount of error is small enough to be tolerated,
059         * while the performance impact of synchronization is not.
060         */
061        private int misses = 0;
062
063        /** Accumulated cost of cache misses in milliseconds. */
064        private long missCostMillis = 0;
065
066        /** Constructor. */
067        public BasicCache(String name, IParameterizedFactory<V, K, X> factory, ICacheBackend<K, V> backend) {
068                this.name = name;
069                this.factory = factory;
070                this.backend = backend;
071        }
072
073        /** {@inheritDoc} */
074        @Override
075        public String getName() {
076                return name;
077        }
078
079        /** {@inheritDoc} */
080        @Override
081        public void clear(boolean allThreads) {
082                backend = backend.newInstance();
083        }
084
085        /** {@inheritDoc} */
086        @Override
087        public V obtain(K key) throws X {
088                V value = backend.retrieve(key);
089
090                if (value != null) {
091                        hits++;
092                        return value;
093                }
094
095                misses++;
096
097                long start = System.currentTimeMillis();
098
099                value = factory.create(key);
100                backend.store(key, value);
101
102                missCostMillis += System.currentTimeMillis() - start;
103
104                return value;
105        }
106
107        /** {@inheritDoc} */
108        @Override
109        public int getHits() {
110                return hits;
111        }
112
113        /** {@inheritDoc} */
114        @Override
115        public int getMisses() {
116                return misses;
117        }
118
119        /** {@inheritDoc} */
120        @Override
121        public long getMissCostMillis() {
122                return missCostMillis;
123        }
124}