Java tutorial
/* * All content copyright (c) 2003-2008 Terracotta, Inc., except as may otherwise be noted in a separate copyright * notice. All rights reserved. */ package com.tc.stats; import org.apache.commons.collections.set.ListOrderedSet; import com.tc.logging.TCLogger; import com.tc.logging.TCLogging; import com.tc.management.RemoteManagement; import com.tc.management.beans.L2MBeanNames; import com.tc.net.NodeID; import com.tc.net.protocol.tcm.MessageChannel; import com.tc.net.protocol.transport.ConnectionPolicy; import com.tc.object.ObjectID; import com.tc.object.net.ChannelStats; import com.tc.object.net.DSOChannelManagerEventListener; import com.tc.object.net.DSOChannelManagerMBean; import com.tc.objectserver.api.GCStats; import com.tc.objectserver.api.GCStatsEventListener; import com.tc.objectserver.api.ObjectInstanceMonitorMBean; import com.tc.objectserver.api.ObjectManagerMBean; import com.tc.objectserver.core.api.ServerConfigurationContext; import com.tc.objectserver.core.impl.ServerManagementContext; import com.tc.objectserver.dgc.impl.GCStatsEventPublisher; import com.tc.objectserver.l1.api.ClientStateManager; import com.tc.objectserver.locks.LockMBean; import com.tc.objectserver.locks.LockManagerMBean; import com.tc.objectserver.search.IndexManager; import com.tc.objectserver.storage.api.OffheapStats; import com.tc.objectserver.storage.api.StorageDataStats; import com.tc.objectserver.tx.ServerTransactionManagerEventListener; import com.tc.objectserver.tx.ServerTransactionManagerMBean; import com.tc.operatorevent.TerracottaOperatorEvent; import com.tc.operatorevent.TerracottaOperatorEventHistoryProvider; import com.tc.stats.api.DSOClassInfo; import com.tc.stats.api.DSOMBean; import com.tc.stats.api.DSOStats; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import java.util.concurrent.Callable; import java.util.concurrent.CancellationException; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import javax.management.Attribute; import javax.management.AttributeList; import javax.management.MBeanServer; import javax.management.MalformedObjectNameException; import javax.management.NotCompliantMBeanException; import javax.management.ObjectName; /** * This is the top-level MBean for the DSO subsystem, off which to hang JSR-77 Stats and Config MBeans. * * @see DSOMBean * @see DSOStatsImpl */ public class DSO extends AbstractNotifyingMBean implements DSOMBean { private final static TCLogger logger = TCLogging.getLogger(DSO.class); private final static String DSO_OBJECT_NAME_PREFIX = L2MBeanNames.DSO.getCanonicalName() + ","; private final DSOStatsImpl dsoStats; private final GCStatsEventPublisher gcStatsPublisher; private final ObjectManagerMBean objMgr; private final MBeanServer mbeanServer; private final ArrayList rootObjectNames = new ArrayList(); private final Set clientObjectNames = new ListOrderedSet(); private final Map<ObjectName, DSOClient> clientMap = new HashMap<ObjectName, DSOClient>(); private final DSOChannelManagerMBean channelMgr; private final ServerTransactionManagerMBean txnMgr; private final LockManagerMBean lockMgr; private final ChannelStats channelStats; private final ObjectInstanceMonitorMBean instanceMonitor; private final ClientStateManager clientStateManager; private final TerracottaOperatorEventHistoryProvider operatorEventHistoryProvider; private final OffheapStats offheapStats; private final StorageDataStats storageStats; private final IndexManager indexManager; private final ConnectionPolicy connectionPolicy; private final RemoteManagement remoteManagement; public DSO(final ServerManagementContext managementContext, final ServerConfigurationContext configContext, final MBeanServer mbeanServer, final GCStatsEventPublisher gcStatsPublisher, TerracottaOperatorEventHistoryProvider operatorEventHistoryProvider, OffheapStats offheapStats, StorageDataStats storageStats) throws NotCompliantMBeanException { super(DSOMBean.class); try { // TraceImplementation.init(TraceTags.LEVEL_TRACE); } catch (Exception e) {/**/ } this.mbeanServer = mbeanServer; this.gcStatsPublisher = gcStatsPublisher; this.dsoStats = new DSOStatsImpl(managementContext); this.lockMgr = managementContext.getLockManager(); this.objMgr = managementContext.getObjectManager(); this.channelMgr = managementContext.getChannelManager(); this.txnMgr = managementContext.getTransactionManager(); this.channelStats = managementContext.getChannelStats(); this.indexManager = managementContext.getIndexManager(); this.instanceMonitor = managementContext.getInstanceMonitor(); this.clientStateManager = configContext.getClientStateManager(); this.operatorEventHistoryProvider = operatorEventHistoryProvider; this.offheapStats = offheapStats; this.storageStats = storageStats; this.connectionPolicy = managementContext.getConnectionPolicy(); this.remoteManagement = managementContext.getRemoteManagement(); // add various listeners (do this before the setupXXX() methods below so we don't ever miss anything) txnMgr.addRootListener(new TransactionManagerListener()); this.gcStatsPublisher.addListener(new DSOGCStatsEventListener()); channelMgr.addEventListener(new ChannelManagerListener()); setupRoots(); setupClients(); } @Override public void optimizeSearchIndex(String indexName) { indexManager.optimizeSearchIndex(indexName); } @Override public String[] getSearchIndexNames() { return indexManager.getSearchIndexNames(); } @Override public void reset() { // TODO: implement this? } @Override public DSOStats getStats() { return dsoStats; } @Override public long getTransactionRate() { return getStats().getTransactionRate(); } @Override public long getReadOperationRate() { return getStats().getReadOperationRate(); } @Override public long getGlobalLockRecallRate() { return getStats().getGlobalLockRecallRate(); } @Override public long getTransactionSizeRate() { return getStats().getTransactionSizeRate(); } @Override public long getBroadcastRate() { return getStats().getBroadcastRate(); } @Override public Number[] getStatistics(String[] names) { return getStats().getStatistics(names); } @Override public GCStats[] getGarbageCollectorStats() { return gcStatsPublisher.getGarbageCollectorStats(); } @Override public List<TerracottaOperatorEvent> getOperatorEvents() { return this.operatorEventHistoryProvider.getOperatorEvents(); } @Override public List<TerracottaOperatorEvent> getOperatorEvents(long sinceTimestamp) { return this.operatorEventHistoryProvider.getOperatorEvents(sinceTimestamp); } @Override public boolean markOperatorEvent(TerracottaOperatorEvent operatorEvent, boolean read) { return operatorEventHistoryProvider.markOperatorEvent(operatorEvent, read); } @Override public Map<String, Integer> getUnreadOperatorEventCount() { return operatorEventHistoryProvider.getUnreadCounts(); } @Override public ObjectName[] getRoots() { synchronized (rootObjectNames) { return (ObjectName[]) rootObjectNames.toArray(new ObjectName[rootObjectNames.size()]); } } @Override public LockMBean[] getLocks() { return this.lockMgr.getAllLocks(); } @Override public ObjectName[] getClients() { synchronized (clientObjectNames) { return (ObjectName[]) clientObjectNames.toArray(new ObjectName[clientObjectNames.size()]); } } @Override public DSOClassInfo[] getClassInfo() { Map counts = instanceMonitor.getInstanceCounts(); List<DSOClassInfo> list = new ArrayList<DSOClassInfo>(); Iterator<Map.Entry<String, Integer>> iter = counts.entrySet().iterator(); while (iter.hasNext()) { Map.Entry<String, Integer> entry = iter.next(); list.add(new DSOClassInfo(entry.getKey(), entry.getValue())); } return list.toArray(new DSOClassInfo[list.size()]); } private void setupRoots() { for (Iterator iter = objMgr.getRootNames(); iter.hasNext();) { String name = (String) iter.next(); final ObjectID rootID; try { rootID = objMgr.lookupRootID(name); } catch (Exception e) { continue; } addRootMBean(name, rootID); } } private void setupClients() { MessageChannel[] channels = channelMgr.getActiveChannels(); for (MessageChannel channel : channels) { addClientMBean(channel); } } private ObjectName makeClientObjectName(MessageChannel channel) { try { return new ObjectName(DSO_OBJECT_NAME_PREFIX + "channelID=" + channel.getChannelID().toLong() + ",productId=" + channel.getProductId()); } catch (MalformedObjectNameException e) { // this shouldn't happen throw new RuntimeException(e); } } private ObjectName makeRootObjectName(String name, ObjectID id) { try { return new ObjectName(DSO_OBJECT_NAME_PREFIX + "rootID=" + id.toLong()); } catch (MalformedObjectNameException e) { // this shouldn't happen throw new RuntimeException(e); } } private void addRootMBean(String name, ObjectID rootID) { // XXX: There should be a cleaner way to do this ignore if (name.startsWith("@")) { // ignore literal roots return; } synchronized (rootObjectNames) { ObjectName rootName = makeRootObjectName(name, rootID); if (mbeanServer.isRegistered(rootName)) { // this can happen since the initial root setup races with the object manager "root created" event logger.debug("Root MBean already registered for name " + rootName); return; } try { DSORoot dsoRoot = new DSORoot(rootID, name); mbeanServer.registerMBean(dsoRoot, rootName); rootObjectNames.add(rootName); sendNotification(ROOT_ADDED, rootName); } catch (Exception e) { logger.error(e); } } } private void removeClientMBean(MessageChannel channel) { ObjectName clientName = makeClientObjectName(channel); synchronized (clientObjectNames) { try { if (mbeanServer.isRegistered(clientName)) { sendNotification(CLIENT_DETACHED, clientName); mbeanServer.unregisterMBean(clientName); } } catch (Exception e) { logger.error(e); } finally { clientObjectNames.remove(clientName); DSOClient client = clientMap.remove(clientName); if (client != null) { client.stopListeningForTunneledBeans(); } } } } private void addClientMBean(final MessageChannel channel) { synchronized (clientObjectNames) { ObjectName clientName = makeClientObjectName(channel); if (mbeanServer.isRegistered(clientName)) { logger.debug("channel MBean already registered for name " + clientName); return; } try { final DSOClient client = new DSOClient(mbeanServer, channel, channelStats, channelMgr.getClientIDFor(channel.getChannelID()), clientStateManager); mbeanServer.registerMBean(client, clientName); clientObjectNames.add(clientName); clientMap.put(clientName, client); sendNotification(CLIENT_ATTACHED, clientName); } catch (Exception e) { logger.error("Unable to register terracotta client MBean", e); } } } @Override public Map<ObjectName, Long> getAllPendingTransactionsCount() { Map<ObjectName, Long> map = new HashMap<ObjectName, Long>(); synchronized (clientObjectNames) { Iterator<ObjectName> iter = clientObjectNames.iterator(); while (iter.hasNext()) { ObjectName clientBeanName = iter.next(); map.put(clientBeanName, clientMap.get(clientBeanName).getPendingTransactionsCount()); } } return map; } /** * Sum of all unacknowledged client transactions */ @Override public long getPendingTransactionsCount() { long result = 0; synchronized (clientObjectNames) { Iterator<ObjectName> iter = clientObjectNames.iterator(); while (iter.hasNext()) { ObjectName clientBeanName = iter.next(); result += clientMap.get(clientBeanName).getPendingTransactionsCount(); } } return result; } @Override public Map<ObjectName, Long> getClientTransactionRates() { Map<ObjectName, Long> result = new HashMap<ObjectName, Long>(); synchronized (clientObjectNames) { Iterator<ObjectName> iter = clientObjectNames.iterator(); while (iter.hasNext()) { ObjectName clientBeanName = iter.next(); result.put(clientBeanName, clientMap.get(clientBeanName).getTransactionRate()); } } return result; } @Override public Map<ObjectName, Map> getL1Statistics() { Map<ObjectName, Map> result = new HashMap<ObjectName, Map>(); synchronized (clientObjectNames) { Iterator<ObjectName> iter = clientObjectNames.iterator(); while (iter.hasNext()) { ObjectName clientBeanName = iter.next(); result.put(clientBeanName, clientMap.get(clientBeanName).getStatistics()); } } return result; } private static final ExecutorService pool = Executors.newCachedThreadPool(); private static class PrimaryClientStatWorker implements Callable<Map> { private final ObjectName clientBeanName; private final DSOClient client; private PrimaryClientStatWorker(ObjectName clientBeanName, DSOClient client) { this.clientBeanName = clientBeanName; this.client = client; } @Override public Map call() { try { Map result = client.getStatistics(); if (result != null) { result.put("TransactionRate", client.getTransactionRate()); result.put("clientBeanName", clientBeanName); } return result; } catch (Exception e) { return null; } } } /* * MemoryUsage, CpuUsage, TransactionRate */ @Override public Map<ObjectName, Map> getPrimaryClientStatistics() { Map<ObjectName, Map> result = new HashMap<ObjectName, Map>(); List<Callable<Map>> tasks = new ArrayList<Callable<Map>>(); synchronized (clientObjectNames) { Iterator<ObjectName> iter = clientObjectNames.iterator(); while (iter.hasNext()) { ObjectName clientBeanName = iter.next(); tasks.add(new PrimaryClientStatWorker(clientBeanName, clientMap.get(clientBeanName))); } } try { List<Future<Map>> results = pool.invokeAll(tasks, 2, TimeUnit.SECONDS); Iterator<Future<Map>> resultIter = results.iterator(); while (resultIter.hasNext()) { Future<Map> future = resultIter.next(); if (future.isDone()) { try { Map statsMap = future.get(); if (statsMap != null) { result.put((ObjectName) statsMap.remove("clientBeanName"), statsMap); } } catch (InterruptedException e) { /**/ } catch (ExecutionException e) { /**/ } } } } catch (InterruptedException ie) {/**/ } return result; } @Override public int getLiveObjectCount() { return objMgr.getLiveObjectCount(); } @Override public long getLastCollectionGarbageCount() { GCStats gcStats = gcStatsPublisher.getLastGarbageCollectorStats(); return gcStats != null ? gcStats.getActualGarbageCount() : -1; } @Override public long getLastCollectionElapsedTime() { GCStats gcStats = gcStatsPublisher.getLastGarbageCollectorStats(); return gcStats != null ? gcStats.getElapsedTime() : -1; } @Override public Map<ObjectName, Integer> getClientLiveObjectCount() { Map<ObjectName, Integer> result = new HashMap<ObjectName, Integer>(); synchronized (clientObjectNames) { Iterator<ObjectName> iter = clientObjectNames.iterator(); while (iter.hasNext()) { ObjectName clientBeanName = iter.next(); result.put(clientBeanName, clientMap.get(clientBeanName).getLiveObjectCount()); } } return result; } @Override public long getGlobalServerMapGetSizeRequestsCount() { return getStats().getGlobalServerMapGetSizeRequestsCount(); } @Override public long getGlobalServerMapGetSizeRequestsRate() { return getStats().getGlobalServerMapGetSizeRequestsRate(); } @Override public long getGlobalServerMapGetValueRequestsCount() { return getStats().getGlobalServerMapGetValueRequestsCount(); } @Override public long getGlobalServerMapGetValueRequestsRate() { return getStats().getGlobalServerMapGetValueRequestsRate(); } @Override public long getWriteOperationRate() { return getStats().getWriteOperationRate(); } @Override public Map<ObjectName, Long> getServerMapGetSizeRequestsCount() { Map<ObjectName, Long> result = new HashMap<ObjectName, Long>(); synchronized (clientObjectNames) { Iterator<ObjectName> iter = clientObjectNames.iterator(); while (iter.hasNext()) { ObjectName clientBeanName = iter.next(); result.put(clientBeanName, clientMap.get(clientBeanName).getServerMapGetSizeRequestsCount()); } } return result; } @Override public Map<ObjectName, Long> getServerMapGetSizeRequestsRate() { Map<ObjectName, Long> result = new HashMap<ObjectName, Long>(); synchronized (clientObjectNames) { Iterator<ObjectName> iter = clientObjectNames.iterator(); while (iter.hasNext()) { ObjectName clientBeanName = iter.next(); result.put(clientBeanName, clientMap.get(clientBeanName).getServerMapGetSizeRequestsRate()); } } return result; } @Override public Map<ObjectName, Long> getServerMapGetValueRequestsCount() { Map<ObjectName, Long> result = new HashMap<ObjectName, Long>(); synchronized (clientObjectNames) { Iterator<ObjectName> iter = clientObjectNames.iterator(); while (iter.hasNext()) { ObjectName clientBeanName = iter.next(); result.put(clientBeanName, clientMap.get(clientBeanName).getServerMapGetValueRequestsCount()); } } return result; } @Override public Map<ObjectName, Long> getServerMapGetValueRequestsRate() { Map<ObjectName, Long> result = new HashMap<ObjectName, Long>(); synchronized (clientObjectNames) { Iterator<ObjectName> iter = clientObjectNames.iterator(); while (iter.hasNext()) { ObjectName clientBeanName = iter.next(); result.put(clientBeanName, clientMap.get(clientBeanName).getServerMapGetValueRequestsRate()); } } return result; } @Override public boolean isResident(NodeID node, ObjectID oid) { return clientStateManager.hasReference(node, oid); } private class TransactionManagerListener implements ServerTransactionManagerEventListener { @Override public void rootCreated(String name, ObjectID rootID) { addRootMBean(name, rootID); } } private class DSOGCStatsEventListener implements GCStatsEventListener { @Override public void update(GCStats stats) { sendNotification(GC_STATUS_UPDATE, stats); } } private class ChannelManagerListener implements DSOChannelManagerEventListener { @Override public void channelCreated(MessageChannel channel) { addClientMBean(channel); } @Override public void channelRemoved(MessageChannel channel) { removeClientMBean(channel); } } private static class SourcedAttributeList { final ObjectName objectName; final AttributeList attributeList; private SourcedAttributeList(ObjectName objectName, AttributeList attributeList) { this.objectName = objectName; this.attributeList = attributeList; } } private static final AttributeList EMPTY_ATTR_LIST = new AttributeList(); private class AttributeListTask implements Callable<SourcedAttributeList> { private final ObjectName objectName; private final Set<String> attributeSet; AttributeListTask(ObjectName objectName, Set<String> attributeSet) { this.objectName = objectName; this.attributeSet = attributeSet; } @Override public SourcedAttributeList call() { AttributeList attributeList; try { attributeList = mbeanServer.getAttributes(objectName, attributeSet.toArray(new String[0])); } catch (Exception e) { attributeList = EMPTY_ATTR_LIST; } return new SourcedAttributeList(objectName, attributeList); } } private static Exception newPlainException(Exception e) { String type = e.getClass().getName(); if (type.startsWith("java.") || type.startsWith("javax.")) { return e; } else { RuntimeException result = new RuntimeException(e.getMessage()); result.setStackTrace(e.getStackTrace()); return result; } } @Override public Map<ObjectName, Exception> setAttribute(Set<ObjectName> onSet, String attrName, Object attrValue) { Map<ObjectName, Exception> result = new HashMap<ObjectName, Exception>(); Iterator<ObjectName> onIter = onSet.iterator(); Attribute attribute = new Attribute(attrName, attrValue); ObjectName on; while (onIter.hasNext()) { on = onIter.next(); try { mbeanServer.setAttribute(on, attribute); } catch (Exception e) { result.put(on, newPlainException(e)); } } return result; } @Override public Map<ObjectName, Exception> setAttribute(String attrName, Map<ObjectName, Object> attrMap) { Map<ObjectName, Exception> result = new HashMap<ObjectName, Exception>(); Iterator<Entry<ObjectName, Object>> entryIter = attrMap.entrySet().iterator(); while (entryIter.hasNext()) { Entry<ObjectName, Object> entry = entryIter.next(); ObjectName on = entry.getKey(); try { Attribute attribute = new Attribute(attrName, entry.getValue()); mbeanServer.setAttribute(on, attribute); } catch (Exception e) { result.put(on, newPlainException(e)); } } return result; } @Override public Map<ObjectName, Map<String, Object>> getAttributeMap(Map<ObjectName, Set<String>> attributeMap, long timeout, TimeUnit unit) { Map<ObjectName, Map<String, Object>> result = new HashMap<ObjectName, Map<String, Object>>(); List<Callable<SourcedAttributeList>> tasks = new ArrayList<Callable<SourcedAttributeList>>(); Iterator<Entry<ObjectName, Set<String>>> entryIter = attributeMap.entrySet().iterator(); while (entryIter.hasNext()) { Entry<ObjectName, Set<String>> entry = entryIter.next(); tasks.add(new AttributeListTask(entry.getKey(), entry.getValue())); } try { List<Future<SourcedAttributeList>> results = pool.invokeAll(tasks, timeout, unit); Iterator<Future<SourcedAttributeList>> resultIter = results.iterator(); while (resultIter.hasNext()) { Future<SourcedAttributeList> future = resultIter.next(); if (future.isDone() && !future.isCancelled()) { try { SourcedAttributeList sal = future.get(); Iterator<Object> attrIter = sal.attributeList.iterator(); Map<String, Object> onMap = new HashMap<String, Object>(); while (attrIter.hasNext()) { Attribute attr = (Attribute) attrIter.next(); onMap.put(attr.getName(), attr.getValue()); } result.put(sal.objectName, onMap); } catch (CancellationException ce) { /**/ } catch (ExecutionException ee) { /**/ } } } } catch (InterruptedException ie) {/**/ } return result; } private static class SimpleInvokeResult { final ObjectName objectName; final Object result; private SimpleInvokeResult(ObjectName objectName, Object result) { this.objectName = objectName; this.result = result; } } private static final Object[] SIMPLE_INVOKE_PARAMS = new Object[0]; private static final String[] SIMPLE_INVOKE_SIG = new String[0]; private class SimpleInvokeTask implements Callable<SimpleInvokeResult> { private final ObjectName objectName; private final String operation; private final Object[] arguments; private final String[] signatures; SimpleInvokeTask(ObjectName objectName, String operation, Object[] arguments, String[] signatures) { this.objectName = objectName; this.operation = operation; this.arguments = arguments; this.signatures = signatures; } @Override public SimpleInvokeResult call() { Object result; try { result = mbeanServer.invoke(objectName, operation, arguments, signatures); } catch (Exception e) { result = e; } return new SimpleInvokeResult(objectName, result); } } @Override public Map<ObjectName, Object> invoke(Set<ObjectName> onSet, String operation, long timeout, TimeUnit unit) { return invoke(onSet, operation, timeout, unit, SIMPLE_INVOKE_PARAMS, SIMPLE_INVOKE_SIG); } @Override public Map<ObjectName, Object> invoke(Set<ObjectName> onSet, String operation, long timeout, TimeUnit unit, Object[] args, String[] sigs) { Map<ObjectName, Object> result = new HashMap<ObjectName, Object>(); List<Callable<SimpleInvokeResult>> tasks = new ArrayList<Callable<SimpleInvokeResult>>(); Iterator<ObjectName> onIter = onSet.iterator(); while (onIter.hasNext()) { tasks.add(new SimpleInvokeTask(onIter.next(), operation, args, sigs)); } try { List<Future<SimpleInvokeResult>> results = pool.invokeAll(tasks, timeout, unit); Iterator<Future<SimpleInvokeResult>> resultIter = results.iterator(); while (resultIter.hasNext()) { Future<SimpleInvokeResult> future = resultIter.next(); if (future.isDone() && !future.isCancelled()) { try { SimpleInvokeResult sir = future.get(); result.put(sir.objectName, sir.result); } catch (CancellationException ce) { /**/ } catch (ExecutionException ee) { /**/ } } } } catch (InterruptedException ie) {/**/ } return result; } @Override public long getOffheapMaxSize() { return offheapStats.getOffheapMaxSize(); } @Override public long getOffheapReservedSize() { return offheapStats.getOffheapReservedSize(); } @Override public long getOffheapUsedSize() { return offheapStats.getOffheapUsedSize(); } @Override public int getActiveLicensedClientCount() { return connectionPolicy.getNumberOfActiveConnections(); } @Override public int getLicensedClientHighCount() { return connectionPolicy.getConnectionHighWatermark(); } @Override public long getEvictionRate() { return getStats().getEvictionRate(); } @Override public long getExpirationRate() { return getStats().getExpirationRate(); } @Override public Map<String, Map<String, Long>> getStorageStats() { return storageStats.getStorageStats(); } @Override public RemoteManagement getRemoteManagement() { return remoteManagement; } }