001/*
002// $Id: XmlaOlap4jNamedMemoryCache.java 482 2012-01-05 23:27:27Z jhyde $
003//
004// Licensed to Julian Hyde under one or more contributor license
005// agreements. See the NOTICE file distributed with this work for
006// additional information regarding copyright ownership.
007//
008// Julian Hyde licenses this file to you under the Apache License,
009// Version 2.0 (the "License"); you may not use this file except in
010// compliance with the License. You may obtain a copy of the License at:
011//
012// http://www.apache.org/licenses/LICENSE-2.0
013//
014// Unless required by applicable law or agreed to in writing, software
015// distributed under the License is distributed on an "AS IS" BASIS,
016// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
017// See the License for the specific language governing permissions and
018// limitations under the License.
019*/
020package org.olap4j.driver.xmla.cache;
021
022import org.olap4j.impl.Olap4jUtil;
023
024import java.net.URL;
025import java.util.Map;
026import java.util.UUID;
027import java.util.concurrent.ConcurrentHashMap;
028
029/**
030 * <p>Implementation of the XMLA SOAP cache that places its cache entries
031 * in memory for later use. It is thread safe and at static class level.
032 *
033 * <p>It supports cache sharing through the Name property.
034 *
035 * <p>All parameters are optional.
036 *
037 * <ul>
038 * <li><b>NAME</b><br />A unique identifier which allows two connections
039 * to share a same cache space. Setting this to an already existing cache
040 * space will cause the cache manager to ignore other configuration properties,
041 * such as eviction mode and so on. Not setting this property will
042 * assign a random name to the cache space, thus creating a unique space.</li>
043 * <li><b>SIZE</b><br />The number of entries to maintain in cache under
044 * the given cache name.</li>
045 * <li><b>TIMEOUT</b><br />The number of seconds to maintain entries in
046 * cache before expiration.</li>
047 * <li><b>MODE</b><br />Supported eviction modes are LIFO (last in first out),
048 * FIFO (first in first out), LFU (least frequently used) and MFU
049 * (most frequently used)</li>
050 * </ul>
051 *
052 * @see XmlaOlap4jNamedMemoryCache.Property
053 * @version $Id: XmlaOlap4jNamedMemoryCache.java 482 2012-01-05 23:27:27Z jhyde $
054 */
055public class XmlaOlap4jNamedMemoryCache implements XmlaOlap4jCache {
056
057    /**
058     * <p>Thread safe hashmap which will be used to keep track of
059     * the current caches. The unique ID is the URL.
060     */
061    private static Map<String, XmlaOlap4jConcurrentMemoryCache> caches = null;
062
063    /**
064     * Properties which will be considered for configuration.
065     *
066     * <p>All parameters are optional.
067     */
068    public static enum Property {
069        /**
070         * A unique identifier which allows two connections to share a same
071         * cache space. Setting this to an already existing cache
072         * space will cause the cache manager to ignore other configuration
073         * properties, such as eviction mode and so on. Not setting this
074         * property will assign a random name to the cache space, thus creating
075         * a unique space.
076         */
077        NAME("Name of a cache to create or to share."),
078
079        /**
080         * The number of entries to maintain in cache under
081         * the given cache name.
082         */
083        SIZE(
084            "Maximum number of SOAP requests which will be cached under the "
085            + "given cache name."),
086
087        /**
088         * The number of seconds to maintain
089         * entries in cache before expiration.
090         */
091        TIMEOUT(
092            "Maximum TTL of SOAP requests which will be cached under the given "
093            + "cache name."),
094
095        /**
096         * Eviction mode. Supported eviction modes are
097         * LIFO (last in first out), FIFO (first in first out),
098         * LFU (least frequently used) and MFU (most frequently used).
099         */
100        MODE("Eviction mode to set to the given cache name.");
101
102        /**
103         * Creates a property.
104         *
105         * @param description Description of property
106         */
107        Property(String description) {
108            Olap4jUtil.discard(description);
109        }
110    }
111
112
113    /**
114     * Defines the supported eviction modes.
115     */
116    public static enum Mode {
117        /** Last-in, first-out. */
118        LIFO,
119        /** First-in, first-out. */
120        FIFO,
121        /** Least-frequently used. */
122        LFU,
123        /** Most-frequently used. */
124        MFU
125    }
126
127
128    /**
129     * Makes sure that the cache is not accessed before it is configured.
130     */
131    private boolean initDone = false;
132
133
134    /**
135     * Default constructor which instantiates the concurrent hash map.
136     */
137    public XmlaOlap4jNamedMemoryCache() {
138        XmlaOlap4jNamedMemoryCache.initCaches();
139    }
140
141
142    /**
143     * Initializes the caches in a static and thread safe way.
144     */
145    private static synchronized void initCaches() {
146        if (caches == null) {
147            caches =
148                new ConcurrentHashMap<
149                String, XmlaOlap4jConcurrentMemoryCache>();
150        }
151    }
152
153    // implement XmlaOlap4jCache
154    public String setParameters(
155        Map<String, String> config,
156        Map<String, String> props)
157    {
158        String refId;
159
160        // Make sure there's a name for the cache. Generate a
161        // random one if needed.
162        if (props.containsKey(
163                XmlaOlap4jNamedMemoryCache.Property.NAME.name()))
164        {
165            refId = (String) props.get(
166                XmlaOlap4jNamedMemoryCache.Property.NAME.name());
167        } else {
168            refId = String.valueOf(UUID.randomUUID());
169            props.put(XmlaOlap4jNamedMemoryCache.Property.NAME.name(), refId);
170        }
171
172
173        // Wait for exclusive access to the caches
174        synchronized (caches) {
175            // Create a cache for this URL if it is not created yet
176            if (!caches.containsKey(
177                    props.get(
178                        XmlaOlap4jNamedMemoryCache.Property.NAME.name())))
179            {
180                caches.put(
181                    (String) props.get(
182                        XmlaOlap4jNamedMemoryCache.Property.NAME.name()),
183                    new XmlaOlap4jConcurrentMemoryCache(props));
184            }
185        }
186
187        // Mark this cache as inited.
188        this.initDone = true;
189
190        // Give back the reference id.
191        return refId;
192    }
193
194
195    // implement XmlaOlap4jCache
196    public byte[] get(
197        String id,
198        URL url,
199        byte[] request)
200        throws XmlaOlap4jInvalidStateException
201    {
202        this.validateState();
203
204        // Wait for exclusive access to the caches
205        synchronized (caches) {
206            if (caches.containsKey(id)) {
207                return caches.get(id).get(url, request);
208            } else {
209                throw new XmlaOlap4jInvalidStateException();
210            }
211        }
212    }
213
214
215    // implement XmlaOlap4jCache
216    public void put(
217        String id,
218        URL url,
219        byte[] request,
220        byte[] response)
221        throws XmlaOlap4jInvalidStateException
222    {
223        this.validateState();
224
225        // Wait for exclusive access to the caches
226        synchronized (caches) {
227            if (caches.containsKey(id)) {
228                caches.get(id).put(url, request, response);
229            } else {
230                throw new XmlaOlap4jInvalidStateException();
231            }
232        }
233    }
234
235    // implement XmlaOlap4jCache
236    public void flushCache() {
237        // Wait for exclusive access to the caches
238        synchronized (caches) {
239            caches.clear();
240        }
241    }
242
243    /**
244     * Helper method to validate that the cache is initialized.
245     *
246     * @throws XmlaOlap4jInvalidStateException When the cache is not initialized.
247     */
248    private void validateState() throws XmlaOlap4jInvalidStateException {
249        if (!this.initDone) {
250            throw new XmlaOlap4jInvalidStateException();
251        }
252    }
253}
254
255// End XmlaOlap4jNamedMemoryCache.java
256
257