001/* 002// This software is subject to the terms of the Eclipse Public License v1.0 003// Agreement, available at the following URL: 004// http://www.eclipse.org/legal/epl-v10.html. 005// You must accept the terms of that agreement to use this software. 006// 007// Copyright (C) 2011-2013 Pentaho and others 008// All Rights Reserved. 009*/ 010package mondrian.spi; 011 012import mondrian.olap.MondrianProperties; 013 014import java.util.ArrayList; 015import java.util.List; 016 017/** 018 * SPI definition of the segments cache. 019 * 020 * <p>Lookups are performed using {@link SegmentHeader}s and 021 * {@link SegmentBody}s. Both are immutable and fully serializable. 022 * 023 * <p>There are a few ways to declare a SegmentCache implementation in 024 * Mondrian. The first one is to set the 025 * {@link MondrianProperties#SegmentCache} property. 026 * 027 * <p>The second one is to use the Java Services API. This is the preferred 028 * mean. You will need to create a jar file, accessible through the same 029 * class loader as Mondrian, and add a file called 030 * <code>/META-INF/services/mondrian.spi.SegmentCache</code> which contains 031 * the name of the segment cache implementation to use. 032 * 033 * <p>The third method is to use the {@link SegmentCacheInjector}. 034 * This is to be used as a last resort, in environments where the 035 * cache implementation is not part of the same class loader as Mondrian. 036 * In those cases, Mondrian can't dynamically load the segment cache class. 037 * The injector serves as an IoC-like service. 038 * 039 * <p>All of the segment caches that Mondrian discovers, throughout all 040 * of these means of discovery, will be used simultaneously. It is not possible 041 * to register new segment caches for a previously existing instance 042 * of a Mondrian server. The caches are scanned and configured when each 043 * Mondrian instance gets created. 044 * 045 * <p>Implementations are expected to be thread-safe. Mondrian is likely to 046 * submit multiple requests at the same time, from different threads. It is the 047 * responsibility of the cache implementation to maintain a consistent 048 * state.</p> 049 * 050 * <p>Implementations must implement a time-out policy, if needed. Mondrian 051 * knows that a call to the cache might take a while. (Mondrian uses worker 052 * threads to call into the cache for precisely that reason.) Left to its 053 * own devices, Mondrian will wait forever for a call to complete. The cache 054 * implementation might know that a call to {@link #get} that has taken 100 055 * milliseconds already is probably hung, so it should return null or throw 056 * an exception. Then Mondrian can get on with its life, and get the segment 057 * some other way.</p> 058 * 059 * <p>Implementations must provide a default empty constructor. 060 * Mondrian creates one segment cache instance per Mondrian server. 061 * There could be more than one Mondrian server running in the same JVM. 062 * 063 * @author LBoudreau 064 */ 065public interface SegmentCache { 066 /** 067 * Returns a SegmentBody once the 068 * cache has returned any results, or null if no 069 * segment corresponding to the header could be found. 070 * 071 * <p>Cache implementations are at liberty to 'forget' segments. Therefore 072 * it is allowable for this method to return null at any time</p> 073 * 074 * @param header The header of the segment to find. 075 * Consider this as a key. 076 * 077 * @return A SegmentBody, or <code>null</code> 078 * if no corresponding segment could be found in cache. 079 */ 080 SegmentBody get(SegmentHeader header); 081 082 /** 083 * Returns a list of all segments present in the cache. 084 * 085 * @return A List of segment headers describing the 086 * contents of the cache. 087 */ 088 List<SegmentHeader> getSegmentHeaders(); 089 090 /** 091 * Stores a segment data in the cache. 092 * 093 * @return Whether the cache write succeeded 094 * @param header The header of the segment. 095 * @param body The segment body to cache. 096 */ 097 boolean put(SegmentHeader header, SegmentBody body); 098 099 /** 100 * Removes a segment from the cache. 101 * 102 * @param header The header of the segment we want to remove. 103 * 104 * @return True if the segment was found and removed, 105 * false otherwise. 106 */ 107 boolean remove(SegmentHeader header); 108 109 /** 110 * Tear down and clean up the cache. 111 */ 112 void tearDown(); 113 114 /** 115 * Adds a listener to this segment cache implementation. 116 * The listener will get notified via 117 * {@link SegmentCacheListener.SegmentCacheEvent} instances. 118 * 119 * @param listener The listener to attach to this cache. 120 */ 121 void addListener(SegmentCacheListener listener); 122 123 /** 124 * Unregisters a listener from this segment cache implementation. 125 * 126 * @param listener The listener to remove. 127 */ 128 void removeListener(SegmentCacheListener listener); 129 130 /** 131 * Tells Mondrian whether this segment cache uses the {@link SegmentHeader} 132 * objects as an index, thus preserving them in a serialized state, or if 133 * it uses its identification number only. 134 * 135 * <p>Not using a rich index prevents 136 * Mondrian from doing partial cache invalidation.</p> 137 * 138 * <p>It is assumed that this method returns fairly quickly, and for a given 139 * cache always returns the same value.</p> 140 * 141 * @return Whether this segment cache preserves headers in serialized state 142 */ 143 boolean supportsRichIndex(); 144 145 /** 146 * {@link SegmentCacheListener} objects are used to listen 147 * to the state of the cache and be notified of changes to its 148 * state or its entries. Mondrian will automatically register 149 * a listener with the implementations it uses. 150 * 151 * Implementations of SegmentCache should only send events if the 152 * cause of the event is not Mondrian itself. Only in cases where 153 * the cache gets updated by other Mondrian nodes or by a third 154 * party application is it required to use this interface. 155 */ 156 interface SegmentCacheListener { 157 /** 158 * Handle an event 159 * @param e Event to handle. 160 */ 161 void handle(SegmentCacheEvent e); 162 163 /** 164 * Defines the event types that a listener can look for. 165 */ 166 interface SegmentCacheEvent { 167 /** 168 * Defined the possible types of events used by 169 * the {@link SegmentCacheListener} class. 170 */ 171 enum EventType { 172 /** 173 * An Entry was created in cache. 174 */ 175 ENTRY_CREATED, 176 /** 177 * An entry was deleted from the cache. 178 */ 179 ENTRY_DELETED 180 } 181 182 /** 183 * Returns the event type of the current SegmentCacheEvent 184 * instance. 185 */ 186 EventType getEventType(); 187 188 /** 189 * Returns the segment header at the source of the event. 190 */ 191 SegmentHeader getSource(); 192 193 /** 194 * Tells whether or not this event was a local event or 195 * an event triggered by an operation on a remote node. 196 * If the implementation cannot differentiate or doesn't 197 * support remote nodes, always return false. 198 */ 199 boolean isLocal(); 200 } 201 } 202 203 /** 204 * The {@link SegmentCacheInjector} is a means to inject 205 * {@link SegmentCache} instances directly into Mondrian, 206 * instead of passing a class name. This is particularly 207 * useful in plugin environments, like the Pentaho Platform. 208 * Mondrian can't always get access to the child class loader, 209 * therefore passing an instance is the only way. 210 * 211 * It is recommended to use the Java Services API when possible 212 * instead of the injector. See {@link SegmentCache}. 213 */ 214 public static class SegmentCacheInjector { 215 private static final List<SegmentCache> caches = 216 new ArrayList<SegmentCache>(); 217 /** 218 * Adds a {@link SegmentCache} instance for Mondrian's use. 219 */ 220 public static void addCache(SegmentCache cache) { 221 caches.add(cache); 222 } 223 public static List<SegmentCache> getCaches() { 224 return caches; 225 } 226 } 227} 228// End SegmentCache.java