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