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