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