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) 2007-2007 Pentaho and others
008// All Rights Reserved.
009*/
010package mondrian.util;
011
012/**
013 * API for Mondrian's memory monitors.
014 *
015 * <p>For Java4, the available monitors
016 * do nothing since there is no reliable way of detecting that
017 * memory is running low using such a JVM (you are welcome to
018 * try to create one, but I believe you will fail - some such candidates
019 * only make it more likely that an OutOfMemory condition will occur).
020 *
021 * <p>For Java5 one
022 * can optionally enable a monitor which is based upon the Java5
023 * memory classes locate in java.lang.management.
024 *
025 * <p>A client must implement the <code>MemoryMonitor.Listener</code> interface
026 * and register with the <code>MemoryMonitor</code>.
027 *
028 * <p>The <code>MemoryMonitor</code> supports having multiple
029 * <code>Listener</code> clients. The clients can have the same
030 * threshold percentage or different values. The threshold percentage value
031 * is used by the <code>MemoryMonitor</code> to determine when to
032 * notify a client. It is the percentage of the total memory:
033 * <blockquote>
034 * <code>
035 * 100 * free-memory / total-memory (0 &le; free-memory &le; total-memory).
036 * </code>
037 * </blockquote>
038 *
039 * @author Richard M. Emberson
040 * @since Feb 01 2007
041 */
042public interface MemoryMonitor {
043
044    /**
045     * Adds a <code>Listener</code> to the <code>MemoryMonitor</code> with
046     * a given threshold percentage.
047     *
048     * <p>If the threshold is below the Java5 memory managment system's
049     * threshold, then the Listener is notified from within this
050     * method.
051     *
052     * @param listener the <code>Listener</code> to be added.
053     * @param thresholdPercentage the threshold percentage for this
054     *   <code>Listener</code>.
055     * @return <code>true</code> if the <code>Listener</code> was
056     *   added and <code>false</code> otherwise.
057     */
058    boolean addListener(Listener listener, int thresholdPercentage);
059
060    /**
061     * Adds a <code>Listener</code> using the default threshold percentage.
062     *
063     * <p>If the threshold is below the Java5 memory managment system's
064     * threshold, then the Listener is notified from within this
065     * method.
066     *
067     * @param listener the <code>Listener</code> to be added.
068     * @return <code>true</code> if the <code>Listener</code> was
069     * added and <code>false</code> otherwise.
070     */
071    boolean addListener(final Listener listener);
072
073    /**
074     * Changes the threshold percentage of a given <code>Listener</code>.
075     *
076     * <p>If the new value is below the system's current value, then the
077     * <code>Listener</code> will have its notification callback called
078     * while in this method - so a client should always check if its
079     * notification method was called immediately after calling this
080     * method.
081     *
082     * <p>This method can be used if, for example, an algorithm has
083     * different approaches that result in different memory
084     * usage profiles; one, large memory but fast and
085     * a second which is low-memory but slow. The algorithm starts
086     * with the large memory approach, receives a low memory
087     * notification, switches to the low memory approach and changes
088     * when it should be notified for this new approach. The first
089     * approach need to be notified at a lower percentage because it
090     * uses lots of memory, possibly quickly; while the second
091     * approach, possibly a file based algorithm, has smaller memory
092     * requirements and uses memory less quickly thus one can
093     * live with a higher notification threshold percentage.
094     *
095     * @param listener the <code>Listener</code> being updated.
096     * @param percentage new percentage threshold.
097     */
098    void updateListenerThreshold(Listener listener, int percentage);
099
100    /**
101     * Removes a <code>Listener</code> from the <code>MemoryMonitor</code>.
102     * Returns <code>true</code> if listener was removed and
103     * <code>false</code> otherwise.
104     *
105     * @param listener the listener to be removed
106     * @return <code>true</code> if listener was removed.
107     */
108    boolean removeListener(Listener listener);
109
110    /**
111     * Clear out all <code>Listener</code>s and turnoff JVM
112     * memory notification.
113     */
114    void removeAllListener();
115
116    /**
117     * Returns the maximum memory usage.
118     *
119     * @return the maximum memory usage.
120     */
121    long getMaxMemory();
122
123    /**
124     * Returns the current memory used.
125     *
126     * @return the current memory used.
127     */
128    long getUsedMemory();
129
130
131    /**
132     * A <code>MemoryMonitor</code> client implements the <code>Listener</code>
133     * interface and registers with the <code>MemoryMonitor</code>.
134     * When the <code>MemoryMonitor</code> detects that free memory is
135     * low, it notifies the client by calling the client's
136     * <code>memoryUsageNotification</code> method. It is important
137     * that the client quickly return from this call, that the
138     * <code>memoryUsageNotification</code> method does not do a lot of
139     * work. It is best if it simply sets a flag. The flag should be
140     * polled by an application thread and when it detects that the
141     * flag was set, it should take immediate memory relinquishing operations.
142     * In the case of Mondrian, the present query is aborted.
143     */
144    interface Listener {
145
146        /**
147         * When the <code>MemoryMonitor</code> determines that the
148         * <code>Listener</code>'s threshold is equal to or less than
149         * the current available memory (post garbage collection),
150         * then this method is called with the current memory usage,
151         * <code>usedMemory</code>, and the maximum memory (which
152         * is a constant per JVM invocation).
153         * <p>
154         * This method is called (in the case of Java5) by a system
155         * thread associated with the garbage collection activity.
156         * When this method is called, the client should quickly do what
157         * it needs to to communicate with an application thread and
158         * then return. Generally, quickly de-referencing some big objects
159         * and setting a flag is the most that should be done by
160         * implementations of this method. If the implementor chooses to
161         * de-reference some objects, then the application code must
162         * be written so that if will not throw a NullPointerException
163         * when such de-referenced objects are accessed. If a flag
164         * is set, then the application must be written to check the
165         * flag periodically.
166         *
167         * @param usedMemory the current memory used.
168         * @param maxMemory the maximum available memory.
169         */
170        void memoryUsageNotification(long usedMemory, long maxMemory);
171    }
172
173    /**
174     * This is an interface that a <code>MemoryMonitor</code> may optionally
175     * implement. These methods give the tester access to some of the
176     * internal, white-box data.
177     * <p>
178     * During testing Mondrian has a default
179     * <code>MemoryMonitor</code> which might be replaced with a test
180     * <code>MemoryMonitor</code>s using the <code>ThreadLocal</code>
181     * mechanism. After the test using the test
182     * <code>MemoryMonitor</code> finishes, a call to the
183     * <code>resetFromTest</code> method allows
184     * the default <code>MemoryMonitor</code> reset itself.
185     * This is hook that should only be called as part of testing.
186     */
187    interface Test {
188
189        /**
190         * This should only be called when one is switching from a
191         * test <code>MemoryMonitor</code> back to the default system
192         * <code>MemoryMonitor</code>. In particular, look at
193         * the <code>MemoryMonitorFactory</code>'s
194         * <code>clearThreadLocalClassName()</code> method for its
195         * usage.
196         */
197        void resetFromTest();
198    }
199}
200
201// End MemoryMonitor.java