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