/* * RHQ Management Platform * Copyright (C) 2005-2013 Red Hat, Inc. * All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation version 2 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ package org.rhq.enterprise.server.resource.group.definition.mbean; import java.util.HashMap; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.atomic.AtomicLong; import javax.annotation.PostConstruct; import javax.annotation.PreDestroy; import javax.ejb.ConcurrencyManagement; import javax.ejb.ConcurrencyManagementType; import javax.ejb.LocalBean; import javax.ejb.Singleton; import javax.ejb.Startup; import javax.ejb.TransactionAttribute; import javax.ejb.TransactionAttributeType; import javax.management.MBeanServer; import javax.management.MBeanServerInvocationHandler; import javax.management.ObjectName; import org.rhq.core.domain.auth.Subject; import org.rhq.core.domain.resource.group.GroupDefinition; import org.rhq.core.util.ObjectNameFactory; import org.rhq.enterprise.server.util.JMXUtil; import org.rhq.enterprise.server.util.LookupUtil; /** * An MBean that exposes call-time metrics for the cost of recalculating * DynaGroups from their owning {@link GroupDefinition}s * * @author Joseph Marques */ @Singleton @Startup @LocalBean @ConcurrencyManagement(ConcurrencyManagementType.BEAN) @TransactionAttribute(TransactionAttributeType.SUPPORTS) public class GroupDefinitionRecalculationThreadMonitor implements GroupDefinitionRecalculationThreadMonitorMBean { private static final ObjectName OBJECT_NAME = ObjectNameFactory.create("rhq:service=GroupDefinitionRecalculationThreadMonitor"); /* * synchronization policy: all private attributes of this statistics object are meant to be read and written * atomically. as a result, all access to and from them must be guarded by the same lock. extensions to this * class should use the intrinsic object lock to protect during access and/or modification. */ public class GroupDefinitionRecalculationStat { private long dynaGroupCount; private long recalculationCount; private long successfulCount; private long minExecutionTime; private long maxExecutionTime; private long totalEexecutionTime; public synchronized void update(int newDynaGroupCount, boolean success, long executionTime) { dynaGroupCount = newDynaGroupCount; if (success) { successfulCount++; } recalculationCount++; if (executionTime < minExecutionTime) { minExecutionTime = executionTime; } else if (executionTime > maxExecutionTime) { maxExecutionTime = executionTime; } totalEexecutionTime += executionTime; } public synchronized Map<String, Object> getStatistics() { Map<String, Object> stats = new HashMap<String, Object>(); stats.put("dynaGroupCount", dynaGroupCount); stats.put("recalculationCount", recalculationCount); stats.put("successfulCount", successfulCount); stats.put("failureCount", recalculationCount - successfulCount); stats.put("minExecutionTime", minExecutionTime); stats.put("maxExecutionTime", maxExecutionTime); stats.put("avgEexecutionTime", totalEexecutionTime / (double) recalculationCount); return stats; } } private static AtomicLong lastAutoRecalculationThreadTime = new AtomicLong(0); private static ConcurrentMap<String, GroupDefinitionRecalculationStat> statistics = new ConcurrentHashMap<String, GroupDefinitionRecalculationStat>(); private static MBeanServer mbeanServer; private static ObjectName objectName; private static GroupDefinitionRecalculationThreadMonitorMBean proxy; public static GroupDefinitionRecalculationThreadMonitorMBean getMBean() { if (proxy == null) { if (objectName != null) { proxy = (GroupDefinitionRecalculationThreadMonitorMBean) MBeanServerInvocationHandler.newProxyInstance( mbeanServer, objectName, GroupDefinitionRecalculationThreadMonitorMBean.class, false); } else { // create a local object proxy = new GroupDefinitionRecalculationThreadMonitor(); } } return proxy; } public void clear() { statistics.clear(); } public long getGroupDefinitionCount() { return LookupUtil.getGroupDefinitionManager().getGroupDefinitionCount(getOverlord()); } public long getAutoRecalculatingGroupDefinitionCount() { return LookupUtil.getGroupDefinitionManager().getAutoRecalculationGroupDefinitionCount(getOverlord()); } public long getDynaGroupCount() { return LookupUtil.getGroupDefinitionManager().getDynaGroupCount(getOverlord()); } private Subject getOverlord() { return LookupUtil.getSubjectManager().getOverlord(); } public long getAutoRecalculationThreadTime() { return lastAutoRecalculationThreadTime.get(); } public void updateAutoRecalculationThreadTime(long timeInMillis) { lastAutoRecalculationThreadTime.set(timeInMillis); } public void updateStatistic(String groupDefinitionName, int newDynaGroupCount, boolean success, long executionTime) { statistics.putIfAbsent(groupDefinitionName, new GroupDefinitionRecalculationStat()); GroupDefinitionRecalculationStat stat = statistics.get(groupDefinitionName); stat.update(newDynaGroupCount, success, executionTime); } public Map<String, Map<String, Object>> getStatistics() { Map<String, Map<String, Object>> results = new HashMap<String, Map<String, Object>>(); for (Map.Entry<String, GroupDefinitionRecalculationStat> stat : statistics.entrySet()) { String groupDefinitionName = stat.getKey(); Map<String, Object> groupDefinitionStatistics = stat.getValue().getStatistics(); results.put(groupDefinitionName, groupDefinitionStatistics); } return results; } @PostConstruct private void init() { JMXUtil.registerMBean(this, OBJECT_NAME); mbeanServer = JMXUtil.getPlatformMBeanServer(); objectName = OBJECT_NAME; } @PreDestroy private void destroy() { mbeanServer = null; objectName = null; JMXUtil.unregisterMBeanQuietly(OBJECT_NAME); } }