/*
* This software is subject to the terms of the Eclipse Public License v1.0
* Agreement, available at the following URL:
* http://www.eclipse.org/legal/epl-v10.html.
* You must accept the terms of that agreement to use this software.
*
* Copyright (c) 2002-2013 Pentaho Corporation..  All rights reserved.
*/

package mondrian.util;

import org.apache.log4j.Logger;

import java.lang.management.*;
import javax.management.*;

/**
 * The <code>NotificationMemoryMonitor</code> class uses the Java5
 * memory management system to detect system low memory events.
 * <p>
 * This code is loosely based on the code taken from The Java
 * Specialists' Newsletter,
 * <a href="http://www.javaspecialists.co.za/archive/newsletter.do?issue=092"
 *  >issue 92</a> authored by Dr. Heinz M. Kabutz.
 * As a note, his on-line newletters are a good source of Java information,
 * take a look.
 * <p>
 *  For more information one should review the Java5 classes in
 *  java.lang.management.
 *
 *
 * @author <a>Richard M. Emberson</a>
 * @since Feb 03 2007
 */
public class NotificationMemoryMonitor extends AbstractMemoryMonitor {
    private static final Logger LOGGER =
                Logger.getLogger(NotificationMemoryMonitor.class);


    protected static final MemoryPoolMXBean TENURED_POOL;

    static {
        TENURED_POOL = findTenuredGenPool();
    }

    private static MemoryPoolMXBean findTenuredGenPool() {
        for (MemoryPoolMXBean pool : ManagementFactory.getMemoryPoolMXBeans()) {
            // I don't know whether this approach is better, or whether
            // we should rather check for the pool name "Tenured Gen"?
            if (pool.getType() == MemoryType.HEAP
                && pool.isUsageThresholdSupported())
            {
                return pool;
            }
        }
        throw new AssertionError("Could not find tenured space");
    }

    /**
     * The <code>NotificationHandler</code> implements the Java memory
     * notification listener, <code>NotificationListener</code>,
     * and is used to take notifications from Java and pass them on
     * to the <code>NotificationMemoryMonitor</code>'s
     * <code>Listerner</code>s.
     */
    private class NotificationHandler implements NotificationListener {

        /**
         * This method is called by the Java5 code to notify clients
         * registered with the JVM that the JVM memory threshold
         * has been exceeded.
         *
         * @param notification
         * @param unused
         */
        public void handleNotification(
            final Notification notification,
            final Object unused)
        {
            final String type = notification.getType();

            if (type.equals(MemoryNotificationInfo.MEMORY_THRESHOLD_EXCEEDED)) {
                final MemoryUsage usage = TENURED_POOL.getUsage();

                notifyListeners(usage.getUsed(), usage.getMax());
            }
        }
    }


    /**
     * Construct a <code>NotificationMemoryMonitor</code> instance and
     * register it with the Java5 memory management system.
     */
    NotificationMemoryMonitor() {
        super();
        final MemoryMXBean mbean = ManagementFactory.getMemoryMXBean();
        final NotificationEmitter emitter = (NotificationEmitter) mbean;

        // register with the Java5 memory management system.
        emitter.addNotificationListener(new NotificationHandler(), null, null);
    }

    /**
     * Get the <code>Logger</code>.
     *
     * @return the <code>Logger</code>.
     */
    protected Logger getLogger() {
        return LOGGER;
    }

    /**
     * Notify the Java5 memory management system that there is a new
     * low threshold.
     *
     * @param newLowThreshold the new threshold.
     */
    protected void notifyNewLowThreshold(final long newLowThreshold) {
        if (newLowThreshold == Long.MAX_VALUE) {
            TENURED_POOL.setUsageThreshold(0);
        } else {
            TENURED_POOL.setUsageThreshold(newLowThreshold);
        }
    }

    /**
     * Get the maximum possible memory usage for this JVM instance.
     *
     * @return maximum memory that can be used.
     */
    public long getMaxMemory() {
        return TENURED_POOL.getUsage().getMax();
    }
    /**
     * Get the current memory usage for this JVM instance.
     *
     * @return current memory used.
     */
    public long getUsedMemory() {
        return TENURED_POOL.getUsage().getUsed();
    }
}

// End NotificationMemoryMonitor.java
