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-2012 Pentaho and others 008// All Rights Reserved. 009*/ 010package mondrian.rolap.cache; 011 012import mondrian.spi.*; 013 014import java.lang.ref.SoftReference; 015import java.util.concurrent.ConcurrentHashMap; 016import java.util.*; 017import java.util.concurrent.CopyOnWriteArrayList; 018 019/** 020 * Implementation of {@link mondrian.spi.SegmentCache} that stores segments 021 * in memory. 022 * 023 * <p>Segments are held via soft references, so the garbage collector can remove 024 * them if it sees fit.</p> 025 * 026 * @author Julian Hyde 027 */ 028public class MemorySegmentCache implements SegmentCache { 029 // Use a thread-safe map because the SegmentCache 030 // interface requires thread safety. 031 private final Map<SegmentHeader, SoftReference<SegmentBody>> map = 032 new ConcurrentHashMap<SegmentHeader, SoftReference<SegmentBody>>(); 033 private final List<SegmentCacheListener> listeners = 034 new CopyOnWriteArrayList<SegmentCacheListener>(); 035 036 public SegmentBody get(SegmentHeader header) { 037 final SoftReference<SegmentBody> ref = map.get(header); 038 if (ref == null) { 039 return null; 040 } 041 final SegmentBody body = ref.get(); 042 if (body == null) { 043 map.remove(header); 044 } 045 return body; 046 } 047 048 public boolean contains(SegmentHeader header) { 049 final SoftReference<SegmentBody> ref = map.get(header); 050 if (ref == null) { 051 return false; 052 } 053 final SegmentBody body = ref.get(); 054 if (body == null) { 055 map.remove(header); 056 return false; 057 } 058 return true; 059 } 060 061 public List<SegmentHeader> getSegmentHeaders() { 062 return new ArrayList<SegmentHeader>(map.keySet()); 063 } 064 065 public boolean put(final SegmentHeader header, SegmentBody body) { 066 // REVIEW: What's the difference between returning false 067 // and throwing an exception? 068 assert header != null; 069 assert body != null; 070 map.put(header, new SoftReference<SegmentBody>(body)); 071 fireSegmentCacheEvent( 072 new SegmentCache.SegmentCacheListener.SegmentCacheEvent() { 073 public boolean isLocal() { 074 return true; 075 } 076 public SegmentHeader getSource() { 077 return header; 078 } 079 public EventType getEventType() { 080 return SegmentCacheListener.SegmentCacheEvent 081 .EventType.ENTRY_CREATED; 082 } 083 }); 084 return true; // success 085 } 086 087 public boolean remove(final SegmentHeader header) { 088 final boolean result = 089 map.remove(header) != null; 090 if (result) { 091 fireSegmentCacheEvent( 092 new SegmentCache.SegmentCacheListener.SegmentCacheEvent() { 093 public boolean isLocal() { 094 return true; 095 } 096 public SegmentHeader getSource() { 097 return header; 098 } 099 public EventType getEventType() { 100 return 101 SegmentCacheListener.SegmentCacheEvent 102 .EventType.ENTRY_DELETED; 103 } 104 }); 105 } 106 return result; 107 } 108 109 public void tearDown() { 110 map.clear(); 111 listeners.clear(); 112 } 113 114 public void addListener(SegmentCacheListener listener) { 115 listeners.add(listener); 116 } 117 118 public void removeListener(SegmentCacheListener listener) { 119 listeners.remove(listener); 120 } 121 122 public boolean supportsRichIndex() { 123 return true; 124 } 125 126 public void fireSegmentCacheEvent( 127 SegmentCache.SegmentCacheListener.SegmentCacheEvent evt) 128 { 129 for (SegmentCacheListener listener : listeners) { 130 listener.handle(evt); 131 } 132 } 133} 134 135// End MemorySegmentCache.java