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