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