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    */
020    package org.olap4j.driver.xmla.cache;
021    
022    import org.olap4j.impl.Olap4jUtil;
023    
024    import java.net.URL;
025    import java.util.Map;
026    import java.util.UUID;
027    import 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     */
055    public 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