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.rolap.agg; 011 012import mondrian.olap.MondrianProperties; 013import mondrian.resource.MondrianResource; 014import mondrian.spi.*; 015import mondrian.util.ClassResolver; 016import mondrian.util.ServiceDiscovery; 017 018import org.apache.log4j.Logger; 019 020import java.util.*; 021 022/** 023 * Utility class to interact with the {@link SegmentCache}. 024 * 025 * @author LBoudreau 026 * @see SegmentCache 027 */ 028public final class SegmentCacheWorker { 029 030 private static final Logger LOGGER = 031 Logger.getLogger(SegmentCacheWorker.class); 032 033 final SegmentCache cache; 034 private final Thread cacheMgrThread; 035 private final boolean supportsRichIndex; 036 037 /** 038 * Creates a worker. 039 * 040 * @param cache Cache managed by this worker 041 * @param cacheMgrThread Thread that the cache manager actor is running on, 042 * and which therefore should not be used for 043 * potentially long-running calls this this cache. 044 * Pass null if methods can be called from any thread. 045 */ 046 public SegmentCacheWorker(SegmentCache cache, Thread cacheMgrThread) { 047 this.cache = cache; 048 this.cacheMgrThread = cacheMgrThread; 049 050 // no need to call checkThread(): supportsRichIndex is a fast call 051 this.supportsRichIndex = cache.supportsRichIndex(); 052 053 LOGGER.debug( 054 "Segment cache initialized: " 055 + cache.getClass().getName()); 056 } 057 058 /** 059 * Instantiates a cache. Returns null if there is no external cache defined. 060 * 061 * @return Cache 062 */ 063 public static List<SegmentCache> initCache() { 064 final List<SegmentCache> caches = 065 new ArrayList<SegmentCache>(); 066 // First try to get the segmentcache impl class from 067 // mondrian properties. 068 final String cacheName = 069 MondrianProperties.instance().SegmentCache.get(); 070 if (cacheName != null) { 071 caches.add(instantiateCache(cacheName)); 072 } 073 074 // There was no property set. Let's look for Java services. 075 final List<Class<SegmentCache>> implementors = 076 ServiceDiscovery.forClass(SegmentCache.class).getImplementor(); 077 if (implementors.size() > 0) { 078 // The contract is to use the first implementation found. 079 SegmentCache cache = 080 instantiateCache(implementors.get(0).getName()); 081 if (cache != null) { 082 caches.add(cache); 083 } 084 } 085 086 // Check the SegmentCacheInjector 087 // People might have sent instances into this thing. 088 caches.addAll(SegmentCache.SegmentCacheInjector.getCaches()); 089 090 // Done. 091 return caches; 092 } 093 094 /** 095 * Instantiates a cache, given the name of the cache class. 096 * 097 * @param cacheName Name of class that implements the 098 * {@link mondrian.spi.SegmentCache} SPI 099 * 100 * @return Cache instance, or null on error 101 */ 102 private static SegmentCache instantiateCache(String cacheName) { 103 try { 104 LOGGER.debug("Starting cache instance: " + cacheName); 105 return ClassResolver.INSTANCE.instantiateSafe(cacheName); 106 } catch (ClassCastException e) { 107 throw MondrianResource.instance() 108 .SegmentCacheIsNotImplementingInterface.ex(); 109 } catch (Exception e) { 110 LOGGER.error( 111 MondrianResource.instance() 112 .SegmentCacheFailedToInstanciate.baseMessage, 113 e); 114 throw MondrianResource.instance() 115 .SegmentCacheFailedToInstanciate.ex(e); 116 } 117 } 118 119 /** 120 * Returns a segment body corresponding to a header. 121 * 122 * <p>If no cache is configured or there is an error while 123 * querying the cache, null is returned none the less. 124 * 125 * @param header Header to search. 126 * @return Either a segment body object or null if there 127 * was no cache configured or no segment could be found 128 * for the passed header. 129 */ 130 public SegmentBody get(SegmentHeader header) { 131 checkThread(); 132 try { 133 return cache.get(header); 134 } catch (Throwable t) { 135 LOGGER.error( 136 MondrianResource.instance() 137 .SegmentCacheFailedToLoadSegment 138 .baseMessage, 139 t); 140 throw MondrianResource.instance() 141 .SegmentCacheFailedToLoadSegment.ex(t); 142 } 143 } 144 145 /** 146 * Places a segment in the cache. Returns true or false 147 * if the operation succeeds. 148 * 149 * @param header A header to search for in the segment cache. 150 * @param body The segment body to cache. 151 */ 152 public void put(SegmentHeader header, SegmentBody body) { 153 checkThread(); 154 try { 155 final boolean result = cache.put(header, body); 156 if (!result) { 157 LOGGER.error( 158 MondrianResource.instance() 159 .SegmentCacheFailedToSaveSegment 160 .baseMessage); 161 throw MondrianResource.instance() 162 .SegmentCacheFailedToSaveSegment.ex(); 163 } 164 } catch (Throwable t) { 165 LOGGER.error( 166 MondrianResource.instance() 167 .SegmentCacheFailedToSaveSegment 168 .baseMessage, 169 t); 170 throw MondrianResource.instance() 171 .SegmentCacheFailedToSaveSegment.ex(t); 172 } 173 } 174 175 /** 176 * Removes a segment from the cache. 177 * 178 * @param header A header to remove in the segment cache. 179 * @return Whether a segment was removed 180 */ 181 public boolean remove(SegmentHeader header) { 182 checkThread(); 183 try { 184 return cache.remove(header); 185 } catch (Throwable t) { 186 LOGGER.error( 187 MondrianResource.instance() 188 .SegmentCacheFailedToDeleteSegment 189 .baseMessage, 190 t); 191 throw MondrianResource.instance() 192 .SegmentCacheFailedToDeleteSegment.ex(t); 193 } 194 } 195 196 /** 197 * Returns a list of segments present in the cache. 198 * 199 * @return List of headers in the cache 200 */ 201 public List<SegmentHeader> getSegmentHeaders() { 202 checkThread(); 203 try { 204 return cache.getSegmentHeaders(); 205 } catch (Throwable t) { 206 LOGGER.error("Failed to get a list of segment headers.", t); 207 throw MondrianResource.instance() 208 .SegmentCacheFailedToScanSegments.ex(t); 209 } 210 } 211 212 public boolean supportsRichIndex() { 213 return supportsRichIndex; 214 } 215 216 public void shutdown() { 217 checkThread(); 218 cache.tearDown(); 219 } 220 221 private void checkThread() { 222 assert cacheMgrThread != Thread.currentThread() 223 : "this method is potentially slow; you should not call it from " 224 + "the cache manager thread, " + cacheMgrThread; 225 } 226} 227 228// End SegmentCacheWorker.java