All Downloads are FREE. Search and download functionalities are using the official Maven repository.

co.paralleluniverse.actors.JMXActorsMonitor Maven / Gradle / Ivy

There is a newer version: 0.8.0
Show newest version
/*
 * 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 java.lang.management.ManagementFactory;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedQueue;
import javax.management.InstanceAlreadyExistsException;
import javax.management.InstanceNotFoundException;
import javax.management.MBeanRegistrationException;
import javax.management.MBeanServer;
import javax.management.MalformedObjectNameException;
import javax.management.NotCompliantMBeanException;
import javax.management.Notification;
import javax.management.NotificationListener;
import javax.management.ObjectName;
import jsr166e.LongAdder;
import org.cliffc.high_scale_lib.NonBlockingHashMapLong;
import org.cliffc.high_scale_lib.NonBlockingHashMapLong.IteratorLong;

/**
 *
 * @author pron
 */
class JMXActorsMonitor implements NotificationListener, ActorsMXBean {
    private static final JMXActorsMonitor instance = new JMXActorsMonitor();

    public static JMXActorsMonitor getInstance() {
        return instance;
    }
    private final String mbeanName;
    private boolean registered;
    private final NonBlockingHashMapLong> actors = new NonBlockingHashMapLong>();
    private final NonBlockingHashMapLong watchedActors = new NonBlockingHashMapLong();
    private long lastCollectTime;
    private final LongAdder activeCount = new LongAdder();

    private JMXActorsMonitor() {
        this.mbeanName = "co.paralleluniverse:type=Actors";
        registerMBean();
        lastCollectTime = nanoTime();
    }

    @SuppressWarnings({"CallToPrintStackTrace", "CallToThreadDumpStack"})
    private void registerMBean() {
        try {
            final MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
            final ObjectName mxbeanName = new ObjectName(mbeanName);
            mbs.registerMBean(this, mxbeanName);
            this.registered = true;
        } catch (InstanceAlreadyExistsException ex) {
            throw new RuntimeException(ex);
        } catch (MBeanRegistrationException ex) {
            ex.printStackTrace();
        } catch (NotCompliantMBeanException ex) {
            throw new AssertionError(ex);
        } catch (MalformedObjectNameException ex) {
            throw new AssertionError(ex);
        }
        MonitoringServices.getInstance().addPerfNotificationListener(this, mbeanName);
    }

    @SuppressWarnings({"CallToPrintStackTrace", "CallToThreadDumpStack"})
    private void unregister() {
        try {
            if (registered) {
                MonitoringServices.getInstance().removePerfNotificationListener(this);
                ManagementFactory.getPlatformMBeanServer().unregisterMBean(new ObjectName(mbeanName));
            }
            this.registered = false;
        } catch (InstanceNotFoundException ex) {
            ex.printStackTrace();
        } catch (MBeanRegistrationException ex) {
            ex.printStackTrace();
        } catch (MalformedObjectNameException ex) {
            throw new AssertionError(ex);
        }
    }

    @Override
    public void handleNotification(Notification notification, Object handback) {
        if ("perfTimer".equals(notification.getType()))
            refresh();
    }

    @Override
    public void refresh() {
        collectAndResetCounters();
    }

    public boolean isRegistered() {
        return registered;
    }

    private void collectAndResetCounters() {
        if (isRegistered()) {
            collectAndResetCounters(nanoTime() - lastCollectTime);
        }
    }

    protected void collectAndResetCounters(long intervalNanos) {

        lastCollectTime = nanoTime();
    }

    private long nanoTime() {
        return System.nanoTime();
    }

    void actorStarted(ActorRef actor) {
        activeCount.increment();
        actors.put(LocalActorUtil.getStrand(actor).getId(), actor);
    }

    void actorTerminated(ActorRef actor) {
        activeCount.decrement();
        actors.remove(LocalActorUtil.getStrand(actor).getId());
    }

    @Override
    public int getNumActiveActors() {
        return activeCount.intValue();
    }

    @Override
    public long[] getAllActorIds() {
        int size = actors.size();
        IteratorLong it = (IteratorLong) actors.keys();
        long[] ids = new long[size];
        int i = 0;
        while (it.hasNext() && i < size) {
            ids[i] = it.nextLong();
            i++;
        }
        if (i < size)
            return Arrays.copyOf(ids, i);
        else
            return ids; // might not include all nw fibers
    }

    @Override
    public String getStackTrace(long actorId) {
        ActorRef actor = actors.get(actorId);
        if (actor == null)
            return null;

        return getStackTrace(actor);
    }

    private String getStackTrace(ActorRef actor) {
        final StackTraceElement[] stackTrace = LocalActorUtil.getStackTrace(actor);
        final StringBuilder sb = new StringBuilder();
        for (StackTraceElement ste : stackTrace)
            sb.append(ste).append('\n');
        return sb.toString();
    }
    
    @Override
    public String[] getMailbox(long actorId) {
        ActorRef actor = actors.get(actorId);
        if (actor == null)
            return null;

        return getMailbox(actor);
    }

    private String[] getMailbox(ActorRef actor) {
        List list = LocalActorUtil.getMailboxSnapshot(actor);
        String[] ms = new String[list.size()];
        int i = 0;
        for (Object m : list)
            ms[i++] = m.toString();
        return ms;
    }
    
    @Override
    public void addWatch(long actorId) {
        ActorRef actor = actors.get(actorId);
        if (actor == null)
            throw new IllegalArgumentException("No actor with id " + actorId + " found.");
        SmallActorMonitor mon = new SmallActorMonitor(actorId);
        LocalActorUtil.setMonitor(actor, mon);
        watchedActors.put(actorId, mon);
    }

    @Override
    public void removeWatch(long actorId) {
        SmallActorMonitor mon = watchedActors.get(actorId);
        if (mon == null)
            return;
        LocalActorUtil.stopMonitor(mon.actor);
    }

    @Override
    public List getWatchedActorsInfo() {
        List list = new ArrayList<>(watchedActors.size());
        for (SmallActorMonitor mon : watchedActors.values()) {
            ActorInfo ai = getActorInfo(mon);
            if (ai != null)
                list.add(ai);
        }
        Collections.sort(list, new Comparator() {
            public int compare(ActorInfo o1, ActorInfo o2) {
                return (int) (o1.getId() - o2.getId());
            }
        });
        return list;
    }

    private ActorInfo getActorInfo(SmallActorMonitor mon) {
        final ActorRef actor = mon.actor;
        if (actor == null)
            return null;
        return new ActorInfo(mon.id,
                actor.getName(),
                LocalActorUtil.getStrand(actor).isFiber(),
                mon.messages,
                LocalActorUtil.getQueueLength(actor),
                (int) mon.restartCounter.get(),
                mon.deathCauses.toArray(new String[0]),
                getMailbox(actor),
                getStackTrace(actor));
    }

    private class SmallActorMonitor implements ActorMonitor {
        private final long id;
        private ActorRef actor;
        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
        volatile long messages;
        volatile long skippedMessages;

        public SmallActorMonitor(long id) {
            this.id = id;
        }

        void refresh() {
            if (registered) {
                final ActorRef a1 = actor;
                if (a1 != null)
                    collect(nanoTime() - lastCollectTime);

                reset();
            }
        }

        protected void collect(long intervalNanos) {
            messages = messageCounter;
        }

        protected void reset() {
            messageCounter = 0;

            lastCollectTime = nanoTime();
        }

        @Override
        public void setActor(ActorRef actor) {
            this.actor = actor;
        }

        @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 void shutdown() {
            watchedActors.remove(id);
        }
    }
}