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