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