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-2009 Pentaho and others
008// All Rights Reserved.
009*/
010package mondrian.util;
011
012import org.apache.log4j.Logger;
013
014import java.lang.management.*;
015import javax.management.*;
016
017/**
018 * The <code>NotificationMemoryMonitor</code> class uses the Java5
019 * memory management system to detect system low memory events.
020 * <p>
021 * This code is loosely based on the code taken from The Java
022 * Specialists' Newsletter,
023 * <a href="http://www.javaspecialists.co.za/archive/newsletter.do?issue=092"
024 *  >issue 92</a> authored by Dr. Heinz M. Kabutz.
025 * As a note, his on-line newletters are a good source of Java information,
026 * take a look.
027 * <p>
028 *  For more information one should review the Java5 classes in
029 *  java.lang.management.
030 *
031 *
032 * @author <a>Richard M. Emberson</a>
033 * @since Feb 03 2007
034 */
035public class NotificationMemoryMonitor extends AbstractMemoryMonitor {
036    private static final Logger LOGGER =
037                Logger.getLogger(NotificationMemoryMonitor.class);
038
039
040    protected static final MemoryPoolMXBean TENURED_POOL;
041
042    static {
043        TENURED_POOL = findTenuredGenPool();
044    }
045
046    private static MemoryPoolMXBean findTenuredGenPool() {
047        for (MemoryPoolMXBean pool : ManagementFactory.getMemoryPoolMXBeans()) {
048            // I don't know whether this approach is better, or whether
049            // we should rather check for the pool name "Tenured Gen"?
050            if (pool.getType() == MemoryType.HEAP
051                && pool.isUsageThresholdSupported())
052            {
053                return pool;
054            }
055        }
056        throw new AssertionError("Could not find tenured space");
057    }
058
059    /**
060     * The <code>NotificationHandler</code> implements the Java memory
061     * notification listener, <code>NotificationListener</code>,
062     * and is used to take notifications from Java and pass them on
063     * to the <code>NotificationMemoryMonitor</code>'s
064     * <code>Listerner</code>s.
065     */
066    private class NotificationHandler implements NotificationListener {
067
068        /**
069         * This method is called by the Java5 code to notify clients
070         * registered with the JVM that the JVM memory threshold
071         * has been exceeded.
072         *
073         * @param notification
074         * @param unused
075         */
076        public void handleNotification(
077            final Notification notification,
078            final Object unused)
079        {
080            final String type = notification.getType();
081
082            if (type.equals(MemoryNotificationInfo.MEMORY_THRESHOLD_EXCEEDED)) {
083                final MemoryUsage usage = TENURED_POOL.getUsage();
084
085                notifyListeners(usage.getUsed(), usage.getMax());
086            }
087        }
088    }
089
090
091    /**
092     * Construct a <code>NotificationMemoryMonitor</code> instance and
093     * register it with the Java5 memory management system.
094     */
095    NotificationMemoryMonitor() {
096        super();
097        final MemoryMXBean mbean = ManagementFactory.getMemoryMXBean();
098        final NotificationEmitter emitter = (NotificationEmitter) mbean;
099
100        // register with the Java5 memory management system.
101        emitter.addNotificationListener(new NotificationHandler(), null, null);
102    }
103
104    /**
105     * Get the <code>Logger</code>.
106     *
107     * @return the <code>Logger</code>.
108     */
109    protected Logger getLogger() {
110        return LOGGER;
111    }
112
113    /**
114     * Notify the Java5 memory management system that there is a new
115     * low threshold.
116     *
117     * @param newLowThreshold the new threshold.
118     */
119    protected void notifyNewLowThreshold(final long newLowThreshold) {
120        if (newLowThreshold == Long.MAX_VALUE) {
121            TENURED_POOL.setUsageThreshold(0);
122        } else {
123            TENURED_POOL.setUsageThreshold(newLowThreshold);
124        }
125    }
126
127    /**
128     * Get the maximum possible memory usage for this JVM instance.
129     *
130     * @return maximum memory that can be used.
131     */
132    public long getMaxMemory() {
133        return TENURED_POOL.getUsage().getMax();
134    }
135    /**
136     * Get the current memory usage for this JVM instance.
137     *
138     * @return current memory used.
139     */
140    public long getUsedMemory() {
141        return TENURED_POOL.getUsage().getUsed();
142    }
143}
144
145// End NotificationMemoryMonitor.java