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