co.paralleluniverse.actors.JMXActorMonitor Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of quasar-actors Show documentation
Show all versions of quasar-actors Show documentation
Fibers, Channels and Actors for the JVM
/*
* Quasar: lightweight threads and actors for the JVM.
* Copyright (C) 2013, Parallel Universe Software Co. All rights reserved.
*
* This program and the accompanying materials are dual-licensed under
* either the terms of the Eclipse Public License v1.0 as published by
* the Eclipse Foundation
*
* or (per the licensee's choosing)
*
* under the terms of the GNU Lesser General Public License version 3.0
* as published by the Free Software Foundation.
*/
package co.paralleluniverse.actors;
import co.paralleluniverse.common.monitoring.Counter;
import co.paralleluniverse.common.monitoring.MonitoringServices;
import co.paralleluniverse.common.util.Objects;
import java.lang.management.ManagementFactory;
import java.lang.ref.WeakReference;
import java.util.List;
import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedQueue;
import javax.management.AttributeChangeNotification;
import javax.management.InstanceAlreadyExistsException;
import javax.management.InstanceNotFoundException;
import javax.management.MBeanNotificationInfo;
import javax.management.MBeanRegistrationException;
import javax.management.MBeanServer;
import javax.management.MalformedObjectNameException;
import javax.management.NotCompliantMBeanException;
import javax.management.Notification;
import javax.management.NotificationBroadcasterSupport;
import javax.management.NotificationEmitter;
import javax.management.NotificationListener;
import javax.management.ObjectName;
import javax.management.StandardEmitterMBean;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
*
* @author pron
*/
public class JMXActorMonitor extends StandardEmitterMBean implements ActorMonitor, ActorMXBean, NotificationListener, NotificationEmitter {
private static final Logger LOG = LoggerFactory.getLogger(JMXActorMonitor.class);
/*
* For the time being, we're not worried about data races. Messages counters are all updated by the actor, so there's no problem there.
* For the JMX thread to see the messages counter, it should really be volatile, but as an approximation, we keep it a regular int.
*/
private WeakReference actor;
private final String name;
private boolean registered;
private long lastCollectTime;
private int notificationSequenceNumber;
//
private int messageCounter;
private int skippedMessageCounter;
private final Counter restartCounter = new Counter();
private final Queue deathCauses = new ConcurrentLinkedQueue<>();
// These hold counter values for the previous window
private volatile long messages;
private volatile long skippedMessages;
//
public JMXActorMonitor(String name) {
super(ActorMXBean.class, true, new NotificationBroadcasterSupport());
this.name = beanName(name);
LOG.info("Starting monitor {}: {}", name, this.name);
lastCollectTime = nanoTime();
refresh();
registerMBean();
}
private static String beanName(String name) {
return "co.paralleluniverse:type=quasar,monitor=actor,name=" + name;
}
@Override
public void setActor(ActorRef actor) {
if (actor == null && this.actor == null)
return;
LOG.info("Setting actor {} for monitor {}", actor, name);
reset();
this.actor = (actor != null ? new WeakReference(actor) : null);
}
@Override
public void shutdown() {
LOG.info("Stopping monitor {}", name);
unregisterMBean();
this.actor = null;
}
private void registerMBean() {
try {
MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
ObjectName mxbeanName = new ObjectName(name);
if (mbs.isRegistered(mxbeanName)) {
try {
LOG.info("MBean named {} is alreasdy registered. Unregistering it.", name);
mbs.unregisterMBean(mxbeanName);
} catch (InstanceNotFoundException e) {
}
}
mbs.registerMBean(this, mxbeanName);
MonitoringServices.getInstance().addPerfNotificationListener(this, name);
this.registered = true;
} catch (InstanceAlreadyExistsException ex) {
throw new RuntimeException(ex);
} catch (MBeanRegistrationException ex) {
ex.printStackTrace();
} catch (NotCompliantMBeanException | MalformedObjectNameException ex) {
throw new AssertionError(ex);
}
}
public void unregisterMBean() {
try {
if (registered) {
ManagementFactory.getPlatformMBeanServer().unregisterMBean(new ObjectName(name));
MonitoringServices.getInstance().removePerfNotificationListener(this);
}
this.registered = false;
} catch (InstanceNotFoundException | MBeanRegistrationException | MalformedObjectNameException ex) {
ex.printStackTrace();
}
}
@Override
public MBeanNotificationInfo[] getNotificationInfo() {
String[] types = new String[]{
AttributeChangeNotification.ATTRIBUTE_CHANGE
};
String notifName = AttributeChangeNotification.class.getName();
String description = "An attribute of this MBean has changed";
MBeanNotificationInfo info = new MBeanNotificationInfo(types, notifName, description);
return new MBeanNotificationInfo[]{info};
}
@Override
public void handleNotification(Notification notification, Object handback) {
if ("perfTimer".equals(notification.getType())) {
assert Objects.equal(handback, name);
refresh();
}
}
@Override
public void refresh() {
if (registered) {
final WeakReference a1 = actor;
if (a1 != null && a1.get() == null)
unregisterMBean();
else
collect(nanoTime() - lastCollectTime);
reset();
}
}
protected void collect(long intervalNanos) {
messages = messageCounter;
}
protected void reset() {
messageCounter = 0;
lastCollectTime = nanoTime();
}
private long nanoTime() {
return System.nanoTime();
}
@Override
public void addDeath(Object cause) {
if (cause == null)
cause = "normal";
while (deathCauses.size() > 20)
deathCauses.poll();
deathCauses.add(cause.toString());
}
@Override
public void addRestart() {
restartCounter.inc();
}
@Override
public void addMessage() {
messageCounter++;
}
@Override
public void skippedMessage() {
skippedMessageCounter++;
}
@Override
public void resetSkippedMessages() {
skippedMessageCounter = 0;
}
/////////
@Override
public int getQueueLength() {
if (this.actor == null)
return 0;
final ActorRef a = this.actor.get();
if (a == null)
return 0;
return LocalActorUtil.getQueueLength(a);
}
@Override
public long getTotalReceivedMessages() {
return messages;
}
@Override
public int getTotalRestarts() {
return (int) restartCounter.get();
}
@Override
public String[] getLastDeathCauses() {
return deathCauses.toArray(new String[0]);
}
@Override
public String[] mailbox() {
final WeakReference a1 = actor;
final ActorRef a = a1 != null ? a1.get() : null;
if (a == null) {
unregisterMBean();
return null;
}
List