001/*
002 * Copyright 2007 The Kuali Foundation
003 * 
004 * Licensed under the Educational Community License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 * 
008 * http://www.opensource.org/licenses/ecl2.php
009 * 
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016package org.kuali.ole.sys;
017
018import java.lang.management.ManagementFactory;
019import java.lang.management.MemoryNotificationInfo;
020import java.lang.management.MemoryPoolMXBean;
021import java.lang.management.MemoryType;
022import java.util.ArrayList;
023import java.util.Arrays;
024import java.util.Collection;
025import java.util.HashMap;
026import java.util.Map;
027
028import javax.management.ListenerNotFoundException;
029import javax.management.Notification;
030import javax.management.NotificationEmitter;
031import javax.management.NotificationListener;
032
033import org.apache.log4j.Logger;
034
035public class MemoryMonitor {
036    private final Collection<Listener> listeners = new ArrayList<Listener>();
037    private static final Logger LOG = Logger.getLogger(MemoryMonitor.class);
038    private String springContextId;
039
040    public interface Listener {
041        public void memoryUsageLow(String springContextId, Map<String, String> memoryUsageStatistics, String deadlockedThreadIds);
042    }
043    NotificationListener lowMemoryListener;
044    public MemoryMonitor() {
045        LOG.info("initializing");
046        this.springContextId = "Unknown";
047        ManagementFactory.getThreadMXBean().setThreadContentionMonitoringEnabled(true);
048        ManagementFactory.getThreadMXBean().setThreadCpuTimeEnabled(true);
049        lowMemoryListener = new NotificationListener() {
050            public void handleNotification(Notification n, Object hb) {
051                if (n.getType().equals(MemoryNotificationInfo.MEMORY_THRESHOLD_EXCEEDED)) {
052                    Map<String, String> memoryUsageStatistics = new HashMap<String, String>();
053                    memoryUsageStatistics.put("MemoryMXBean: " + MemoryType.HEAP, ManagementFactory.getMemoryMXBean().getHeapMemoryUsage().toString());
054                    memoryUsageStatistics.put("MemoryMXBean:" + MemoryType.NON_HEAP, ManagementFactory.getMemoryMXBean().getNonHeapMemoryUsage().toString());
055                    for (MemoryPoolMXBean pool : ManagementFactory.getMemoryPoolMXBeans()) {
056                        memoryUsageStatistics.put("MemoryPoolMXBean: " + pool.getType(), pool.getUsage().toString());
057                    }
058                    for (Listener listener : listeners) {
059                        listener.memoryUsageLow(springContextId, memoryUsageStatistics, Arrays.toString(ManagementFactory.getThreadMXBean().findMonitorDeadlockedThreads()));
060                    }
061                }
062            }
063        };
064        ((NotificationEmitter) ManagementFactory.getMemoryMXBean()).addNotificationListener(lowMemoryListener, null, null);
065    }
066    
067    public void stop() {
068        try {
069            removeAllListeners();
070            ((NotificationEmitter) ManagementFactory.getMemoryMXBean()).removeNotificationListener(lowMemoryListener);
071        } catch (ListenerNotFoundException ex) {
072            LOG.error( "Unable to unregister mbean listener", ex);
073        }
074    }
075    
076    public void removeAllListeners() {
077        listeners.clear();
078    }
079
080    public MemoryMonitor(String springContextId) {
081        this();
082        this.springContextId = springContextId;
083    }
084
085    public boolean addListener(Listener listener) {
086        return listeners.add(listener);
087    }
088
089    public boolean removeListener(Listener listener) {
090        return listeners.remove(listener);
091    }
092
093    public static void setPercentageUsageThreshold(double percentage) {
094        for (MemoryPoolMXBean pool : ManagementFactory.getMemoryPoolMXBeans()) {
095            if (pool.getType() == MemoryType.HEAP && pool.isUsageThresholdSupported()) {
096                if (percentage <= 0.0 || percentage > 1.0) {
097                    throw new IllegalArgumentException("percentage not in range");
098                }
099                long warningThreshold = (long) (pool.getUsage().getMax() * percentage);
100                pool.setUsageThreshold(warningThreshold);
101            }
102        }
103    }
104}