Alter JavaDoc

Alter JavaDoc

/*
 * Copyright (c) 2013 original authors
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.eigengo.monitor.agent.akka;

import akka.actor.*;
import org.eigengo.monitor.agent.AgentConfiguration;
import org.eigengo.monitor.output.CounterInterface;
import scala.Option;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.ConcurrentHashMap;

/**
 * Contains advices for monitoring behaviour of an actor; typically imprisoned in an {@code ActorCell}.
 */
public aspect ActorCellMonitoringAspect extends AbstractMonitoringAspect issingleton() {
    private AkkaAgentConfiguration agentConfiguration;
    private final CounterInterface counterInterface;
    private final Option<String> noActorClazz = Option.empty();
    private final ConcurrentHashMap<Option<String>, AtomicLong> samplingCounters  = new ConcurrentHashMap<Option<String>, AtomicLong>();
    private final ConcurrentHashMap<Option<String>, AtomicInteger> numberOfActors = new ConcurrentHashMap<Option<String>, AtomicInteger>();

    /**
     * Constructs this aspect
     */
    public ActorCellMonitoringAspect() {
        AgentConfiguration<AkkaAgentConfiguration> configuration = getAgentConfiguration("akka", AkkaAgentConfigurationJapi.apply());
        this.agentConfiguration = configuration.agent();
        this.counterInterface = createCounterInterface(configuration.common());
    }

    /**
     * Injects the new {@code AkkaAgentConfiguration} instance.
     *
     * @param agentConfiguration the new configuration
     */
    synchronized final void setAgentConfiguration(AkkaAgentConfiguration agentConfiguration) {
        this.agentConfiguration = agentConfiguration;
    }

    /**
     * decide whether to include this ActorCell in our measurements
     * */
    private boolean includeActorPath(final PathAndClass pathAndClass) {
        // do not monitor our own output code
        if (pathAndClass.actorClassName().isDefined()) {
            if (pathAndClass.actorClassName().get().startsWith("org.eigengo.monitor.output")) return false;
        }

        // skip if not included
        if (!this.agentConfiguration.included().accept(pathAndClass)) return false;
        // skip if excluded
        if (this.agentConfiguration.excluded().accept(pathAndClass)) return false;

        // skip system actor checks if we wish to monitor them
        if (this.agentConfiguration.includeSystemAgents()) return true;

        String userOrSystem = pathAndClass.actorPath().getElements().iterator().next();
        return "user".equals(userOrSystem);
    }

    /**
     * get the sample rate for an actor
     * */
    private int getSampleRate(final PathAndClass pathAndClass) {
        return this.agentConfiguration.sampling().getRate(pathAndClass);
    }

    /**
     * decide whether to sample an actor on a particular occasion
     *
     * @param pathAndClass
     * @return true if we should sample this actor, otherwise false.
     */
    private final boolean sampleMessage(final PathAndClass pathAndClass) {
        int sampleRate = getSampleRate(pathAndClass);
        if (sampleRate == 1) return true;

        this.samplingCounters.putIfAbsent(pathAndClass.actorClassName(), new AtomicLong(0));
        long timesSeenSoFar = this.samplingCounters.get(pathAndClass.actorClassName()).incrementAndGet();
        return (timesSeenSoFar % sampleRate == 1); // == 1 to log first value (incrementAndGet returns updated value)
    }

    /**
     * get the tags for the cell
     * */
    private String[] getTags(final ActorPath actorPath, final Actor actor) {
        List<String> tags = new ArrayList<String>();

        // TODO: Improve detection of routed actors. This relies only on the naming :(.
        String lastPathElement = actorPath.elements().last();
        if (lastPathElement.startsWith("$")) {
            // this is routed actor.
            tags.add(actorPath.parent().toString());
            if (this.agentConfiguration.includeRoutees()) tags.add(actorPath.toString());
        } else {
            // there is no supervisor
            tags.add(actorPath.toString());
        }
        if (actor != null) {
            tags.add(String.format("akka:%s.%s", actor.context().system().name(), actor.getClass().getCanonicalName()));
        }

        return tags.toArray(new String[tags.size()]);
    }

    /**
     * returns the canonical name of the actor type associated with an ActorCell
     * */
    private String uncheckedActorNameFrom(final ActorCell actorCell) {
        return uncheckedActorNameFrom(actorCell.props());
    }

    /**
     * returns the canonical name of the actor type associated with a Props instance
     * */
    private String uncheckedActorNameFrom(final Props props) {
        return props.actorClass().getCanonicalName();
    }

    /**
     * Advises the {@code ActorCell.receiveMessage(message: Object): Unit}
     * We proceed with the pointcut if the actor is to be included in the monitoring *and* this is
     * the 'multiple-of-n'th time we've seen a message for an actor with a sample rate of n.
     *
     * Currently, we sample queue size, the fact that the message is delivered, the simple name of the class of the
     * message, and the time taken to complete the actor's reactive action.
     *
     * @param actorCell the ActorCell where the actor that receives the message "lives"
     * @param msg the incoming message
     */
    Object around(ActorCell actorCell, Object msg) : Pointcuts.actorCellReceiveMessage(actorCell, msg) {

        final ActorPath actorPath = actorCell.self().path();
        final PathAndClass pathAndClass = new PathAndClass(actorPath, Option.apply(uncheckedActorNameFrom(actorCell)));
        if (!includeActorPath(pathAndClass) || !sampleMessage(pathAndClass)) return proceed(actorCell, msg);

        int samplingRate = getSampleRate(pathAndClass);

        // we tag by actor name
        final String[] tags = getTags(actorPath, actorCell.actor());

        // record the queue size
        this.counterInterface.recordGaugeValue("akka.queue.size", actorCell.numberOfMessages(), tags);
        // record the message, general and specific
        this.counterInterface.incrementCounter("akka.actor.delivered", samplingRate, tags);
        this.counterInterface.incrementCounter("akka.actor.delivered." + msg.getClass().getSimpleName(), samplingRate, tags);

        // measure the time. we're using the ``nanoTime`` call to access the high-precision timer.
        // since we're not really interested in wall time, but just some increasing measure of
        // elapsed time
        Object result = null;
        final long start = System.nanoTime();
        // result will always be ``null``, because target returns ``Unit``
        result = proceed(actorCell, msg);
        // ns or µs is too fine. ms will do for now.
        final long duration = (System.nanoTime() - start) / 1000000;

        // record the actor duration
        this.counterInterface.recordExecutionTime("akka.actor.duration", (int)duration, tags);

        // return null would do the trick, but we want to be _proper_.
        return result;
    }

    /**
     * Advises the {@code ActorCell.handleInvokeFailure(_, failure: Throwable): Unit}
     *
     * @param actorCell the ActorCell where the error spreads from
     * @param failure the exception that escaped from the {@code receive} method
     */
    before(ActorCell actorCell, Throwable failure) : Pointcuts.actorCellHandleInvokeFailure(actorCell, failure) {
        // record the error, general and specific
        String[] tags = getTags(actorCell.self().path(), actorCell.actor());
        this.counterInterface.incrementCounter("akka.actor.error", tags);
        this.counterInterface.incrementCounter(String.format("akka.actor.error.%s", failure.getMessage()), tags);
    }

    /**
     * Advises the {@code EventStream.publish(event: Any): Unit}
     *
     * @param event the received event
     */
    before(Object event) : Pointcuts.eventStreamPublish(event) {
        if (event instanceof UnhandledMessage) {
            UnhandledMessage unhandledMessage = (UnhandledMessage)event;
            String[] tags = getTags(unhandledMessage.recipient().path(), null);
            this.counterInterface.incrementCounter("akka.actor.undelivered", tags);
            this.counterInterface.incrementCounter("akka.actor.undelivered." + unhandledMessage.getMessage().getClass().getSimpleName(), tags);
        }
    }

    /**
     * Advises the {@code actorOf} method of {@code ActorRefFactory} implementations
     *
     * @param props the {@code Props} instance used in the call
     * @param actor the {@code ActorRef} returned from the call
     */
    after(Props props) returning (ActorRef actor): Pointcuts.anyActorOf(props) {
        recordActorCreation(props, actor);
    }
    /**
     * Advises the {@code actorOf} method of {@code ActorRefFactory} implementations when actor is named in construction
     *
     * @param props the {@code Props} instance used in the call
     * @param actorName the {@code String} used to name the actor at creation site
     * @param actor the {@code ActorRef} returned from the call
     */
    after(Props props, String actorName) returning (ActorRef actor): Pointcuts.namedActorOf(props, actorName) {
        recordActorCreation(props, actor);
    }

    /**
     * method containing the recording logic for advising actor creation
     * */
    private void recordActorCreation(Props props, ActorRef actor) {
        if (!includeActorPath(new PathAndClass(actor.path(), this.noActorClazz))) return;
        final String uncheckedClassName = uncheckedActorNameFrom(props);
        final Option<String> className = Option.apply(uncheckedClassName);

        this.numberOfActors.putIfAbsent(className, new AtomicInteger(0));
        // increment and get the current number of actors of this type (if the value was 0, then this returns 1 -- which is correct)
        final int currentNumberOfActors = this.numberOfActors.get(className).incrementAndGet();

        // record the current number of actors of this type
        this.counterInterface.recordGaugeValue("akka.actor.count", currentNumberOfActors, uncheckedClassName);
    }

    /**
     * Advises the {@code LocalActorRef.stop} method
     *
     * @param actorCell the {@code ActorCell} of the actor being stopped
     */
    after(ActorCell actorCell) : Pointcuts.actorCellInternalStop(actorCell) {
        if (!includeActorPath(new PathAndClass(actorCell.self().path(), this.noActorClazz))) return;
        final String uncheckedClassName = uncheckedActorNameFrom(actorCell);
        final Option<String> className = Option.apply(uncheckedClassName);

        this.numberOfActors.putIfAbsent(className, new AtomicInteger(0));
        // decrement and get the current number of actors of this type (if the value was 1, then this returns 0 -- which is correct)
        final int currentNumberOfActors = this.numberOfActors.get(className).decrementAndGet();

        this.counterInterface.recordGaugeValue("akka.actor.count", currentNumberOfActors, uncheckedClassName);
    }

}

/*
 * Copyright (c) 2013 original authors
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.eigengo.monitor.agent.akka;

import akka.actor.*;
import org.eigengo.monitor.agent.AgentConfiguration;
import org.eigengo.monitor.output.CounterInterface;
import scala.Option;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.ConcurrentHashMap;

/**
 * Contains advices for monitoring behaviour of an actor; typically imprisoned in an {@code ActorCell}.
 */
public aspect ActorCellMonitoringAspect extends AbstractMonitoringAspect issingleton() {
    private AkkaAgentConfiguration agentConfiguration;
    private final CounterInterface counterInterface;
    private final Option<String> noActorClazz = Option.empty();
    private final ConcurrentHashMap<Option<String>, AtomicLong> samplingCounters  = new ConcurrentHashMap<Option<String>, AtomicLong>();
    private final ConcurrentHashMap<Option<String>, AtomicInteger> numberOfActors = new ConcurrentHashMap<Option<String>, AtomicInteger>();

    /**
     * Constructs this aspect
     */
    public ActorCellMonitoringAspect() {
        AgentConfiguration<AkkaAgentConfiguration> configuration = getAgentConfiguration("akka", AkkaAgentConfigurationJapi.apply());
        this.agentConfiguration = configuration.agent();
        this.counterInterface = createCounterInterface(configuration.common());
    }

    /**
     * Injects the new {@code AkkaAgentConfiguration} instance.
     *
     * @param agentConfiguration the new configuration
     */
    synchronized final void setAgentConfiguration(AkkaAgentConfiguration agentConfiguration) {
        this.agentConfiguration = agentConfiguration;
    }

    /**
     * decide whether to include this ActorCell in our measurements
     * */
    private boolean includeActorPath(final PathAndClass pathAndClass) {
        // do not monitor our own output code
        if (pathAndClass.actorClassName().isDefined()) {
            if (pathAndClass.actorClassName().get().startsWith("org.eigengo.monitor.output")) return false;
        }

        // skip if not included
        if (!this.agentConfiguration.included().accept(pathAndClass)) return false;
        // skip if excluded
        if (this.agentConfiguration.excluded().accept(pathAndClass)) return false;

        // skip system actor checks if we wish to monitor them
        if (this.agentConfiguration.includeSystemAgents()) return true;

        String userOrSystem = pathAndClass.actorPath().getElements().iterator().next();
        return "user".equals(userOrSystem);
    }

    /**
     * get the sample rate for an actor
     * */
    private int getSampleRate(final PathAndClass pathAndClass) {
        return this.agentConfiguration.sampling().getRate(pathAndClass);
    }

    /**
     * decide whether to sample an actor on a particular occasion
     *
     * @param pathAndClass
     * @return true if we should sample this actor, otherwise false.
     */
    private final boolean sampleMessage(final PathAndClass pathAndClass) {
        int sampleRate = getSampleRate(pathAndClass);
        if (sampleRate == 1) return true;

        this.samplingCounters.putIfAbsent(pathAndClass.actorClassName(), new AtomicLong(0));
        long timesSeenSoFar = this.samplingCounters.get(pathAndClass.actorClassName()).incrementAndGet();
        return (timesSeenSoFar % sampleRate == 1); // == 1 to log first value (incrementAndGet returns updated value)
    }

    /**
     * get the tags for the cell
     * */
    private String[] getTags(final ActorPath actorPath, final Actor actor) {
        List<String> tags = new ArrayList<String>();

        // TODO: Improve detection of routed actors. This relies only on the naming :(.
        String lastPathElement = actorPath.elements().last();
        if (lastPathElement.startsWith("$")) {
            // this is routed actor.
            tags.add(actorPath.parent().toString());
            if (this.agentConfiguration.includeRoutees()) tags.add(actorPath.toString());
        } else {
            // there is no supervisor
            tags.add(actorPath.toString());
        }
        if (actor != null) {
            tags.add(String.format("akka:%s.%s", actor.context().system().name(), actor.getClass().getCanonicalName()));
        }

        return tags.toArray(new String[tags.size()]);
    }

    /**
     * returns the canonical name of the actor type associated with an ActorCell
     * */
    private String uncheckedActorNameFrom(final ActorCell actorCell) {
        return uncheckedActorNameFrom(actorCell.props());
    }

    /**
     * returns the canonical name of the actor type associated with a Props instance
     * */
    private String uncheckedActorNameFrom(final Props props) {
        return props.actorClass().getCanonicalName();
    }

    /**
     * Advises the {@code ActorCell.receiveMessage(message: Object): Unit}
     * We proceed with the pointcut if the actor is to be included in the monitoring *and* this is
     * the 'multiple-of-n'th time we've seen a message for an actor with a sample rate of n.
     *
     * Currently, we sample queue size, the fact that the message is delivered, the simple name of the class of the
     * message, and the time taken to complete the actor's reactive action.
     *
     * @param actorCell the ActorCell where the actor that receives the message "lives"
     * @param msg the incoming message
     */
    Object around(ActorCell actorCell, Object msg) : Pointcuts.actorCellReceiveMessage(actorCell, msg) {

        final ActorPath actorPath = actorCell.self().path();
        final PathAndClass pathAndClass = new PathAndClass(actorPath, Option.apply(uncheckedActorNameFrom(actorCell)));
        if (!includeActorPath(pathAndClass) || !sampleMessage(pathAndClass)) return proceed(actorCell, msg);

        int samplingRate = getSampleRate(pathAndClass);

        // we tag by actor name
        final String[] tags = getTags(actorPath, actorCell.actor());

        // record the queue size
        this.counterInterface.recordGaugeValue("akka.queue.size", actorCell.numberOfMessages(), tags);
        // record the message, general and specific
        this.counterInterface.incrementCounter("akka.actor.delivered", samplingRate, tags);
        this.counterInterface.incrementCounter("akka.actor.delivered." + msg.getClass().getSimpleName(), samplingRate, tags);

        // measure the time. we're using the ``nanoTime`` call to access the high-precision timer.
        // since we're not really interested in wall time, but just some increasing measure of
        // elapsed time
        Object result = null;
        final long start = System.nanoTime();
        // result will always be ``null``, because target returns ``Unit``
        result = proceed(actorCell, msg);
        // ns or µs is too fine. ms will do for now.
        final long duration = (System.nanoTime() - start) / 1000000;

        // record the actor duration
        this.counterInterface.recordExecutionTime("akka.actor.duration", (int)duration, tags);

        // return null would do the trick, but we want to be _proper_.
        return result;
    }

    /**
     * Advises the {@code ActorCell.handleInvokeFailure(_, failure: Throwable): Unit}
     *
     * @param actorCell the ActorCell where the error spreads from
     * @param failure the exception that escaped from the {@code receive} method
     */
    before(ActorCell actorCell, Throwable failure) : Pointcuts.actorCellHandleInvokeFailure(actorCell, failure) {
        // record the error, general and specific
        String[] tags = getTags(actorCell.self().path(), actorCell.actor());
        this.counterInterface.incrementCounter("akka.actor.error", tags);
        this.counterInterface.incrementCounter(String.format("akka.actor.error.%s", failure.getMessage()), tags);
    }

    /**
     * Advises the {@code EventStream.publish(event: Any): Unit}
     *
     * @param event the received event
     */
    before(Object event) : Pointcuts.eventStreamPublish(event) {
        if (event instanceof UnhandledMessage) {
            UnhandledMessage unhandledMessage = (UnhandledMessage)event;
            String[] tags = getTags(unhandledMessage.recipient().path(), null);
            this.counterInterface.incrementCounter("akka.actor.undelivered", tags);
            this.counterInterface.incrementCounter("akka.actor.undelivered." + unhandledMessage.getMessage().getClass().getSimpleName(), tags);
        }
    }

    /**
     * Advises the {@code actorOf} method of {@code ActorCell} and {@code ActorSystem}
     *
     * @param props the {@code Props} instance used in the call
     * @param actor the {@code ActorRef} returned from the call
     */
    after(Props props) returning (ActorRef actor): Pointcuts.anyActorOf(props) {
        recordActorCreation(props, actor);
    }
    /**
     * Advises the {@code actorOf} method of {@code ActorCell} and {@code ActorSystem} when actor is named in construction
     *
     * @param props the {@code Props} instance used in the call
     * @param actorName the {@code String} used to name the actor at creation site
     * @param actor the {@code ActorRef} returned from the call
     */
    after(Props props, String actorName) returning (ActorRef actor): Pointcuts.namedActorOf(props, actorName) {
        recordActorCreation(props, actor);
    }

    /**
     * method containing the recording logic for advising actor creation
     * */
    private void recordActorCreation(Props props, ActorRef actor) {
        if (!includeActorPath(new PathAndClass(actor.path(), this.noActorClazz))) return;
        final String uncheckedClassName = uncheckedActorNameFrom(props);
        final Option<String> className = Option.apply(uncheckedClassName);

        this.numberOfActors.putIfAbsent(className, new AtomicInteger(0));
        // increment and get the current number of actors of this type (if the value was 0, then this returns 1 -- which is correct)
        final int currentNumberOfActors = this.numberOfActors.get(className).incrementAndGet();

        // record the current number of actors of this type
        this.counterInterface.recordGaugeValue("akka.actor.count", currentNumberOfActors, uncheckedClassName);
    }

    /**
     * Advises the {@code LocalActorRef.stop} method
     *
     * @param actorCell the {@code ActorCell} of the actor being stopped
     */
    after(ActorCell actorCell) : Pointcuts.actorCellInternalStop(actorCell) {
        if (!includeActorPath(new PathAndClass(actorCell.self().path(), this.noActorClazz))) return;
        final String uncheckedClassName = uncheckedActorNameFrom(actorCell);
        final Option<String> className = Option.apply(uncheckedClassName);

        this.numberOfActors.putIfAbsent(className, new AtomicInteger(0));
        // decrement and get the current number of actors of this type (if the value was 1, then this returns 0 -- which is correct)
        final int currentNumberOfActors = this.numberOfActors.get(className).decrementAndGet();

        this.counterInterface.recordGaugeValue("akka.actor.count", currentNumberOfActors, uncheckedClassName);
    }

}


Update Queues instrumentation

Update Queues instrumentation

package edu.brown.cs.systems.tracing.aspects;

import java.util.concurrent.BlockingQueue;

import edu.brown.cs.systems.baggage.Baggage;
import edu.brown.cs.systems.baggage.DetachedBaggage;
import edu.brown.cs.systems.tracing.aspects.Annotations.InstrumentQueues;
import edu.brown.cs.systems.tracing.aspects.Annotations.InstrumentedQueueElement;

/** Instruments potentially asynchronous executions -- runnables, callables, and threads. Special-case handling for
 * threads since they can themselves be a runnable, or they can wrap a runnable */
public aspect Queues {

    /* ================================================================================================================
     *
     * QueueElementWithBaggage is added to any class that is used as a queue element, with the InstrumentedQueueElement
     * annotation. */

    public interface QueueElementWithBaggage {
        public DetachedBaggage takeAttachedBaggage();
        public void attachProvidedBaggage(DetachedBaggage baggage);
    }

    public volatile DetachedBaggage QueueElementWithBaggage.attachedQueueElementBaggage;

    public DetachedBaggage QueueElementWithBaggage.takeAttachedBaggage() {
        try {
            return attachedQueueElementBaggage;
        } finally {
            attachedQueueElementBaggage = null;
        }
    }

    public void QueueElementWithBaggage.attachProvidedBaggage(DetachedBaggage baggage) {
        attachedQueueElementBaggage = baggage;
    }

    /* ================================================================================================================
     *
     * All instrumented queue element annotated get baggage added */

    declare parents: (@InstrumentedQueueElement Object)+ implements QueueElementWithBaggage;

    before(QueueElementWithBaggage e): args(e,..) && within(@InstrumentQueues *) && (
            call(* BlockingQueue+.add(..)) || call(* BlockingQueue+.offer(..)) || call(* BlockingQueue+.put(..))
            ) {
        e.attachProvidedBaggage(Baggage.fork());
    }

    before(): within(@InstrumentQueues *) && call(* BlockingQueue+.take(..)) {
        Baggage.discard();
    }

    after() returning(QueueElementWithBaggage e): within(@InstrumentQueues *) && call(* BlockingQueue+.take(..)) {
        if (e != null) {
            Baggage.start(e.takeAttachedBaggage());
        }
    }

}

package edu.brown.cs.systems.tracing.aspects;

import java.util.concurrent.BlockingQueue;

import edu.brown.cs.systems.baggage.Baggage;
import edu.brown.cs.systems.baggage.DetachedBaggage;
import edu.brown.cs.systems.tracing.aspects.Annotations.InstrumentQueues;
import edu.brown.cs.systems.tracing.aspects.Annotations.InstrumentedQueueElement;

/** Instruments potentially asynchronous executions -- runnables, callables, and threads. Special-case handling for
 * threads since they can themselves be a runnable, or they can wrap a runnable */
public aspect Queues {

    /* ================================================================================================================
     *
     * QueueElementWithBaggage is added to any class that is used as a queue element, with the InstrumentedQueueElement
     * annotation. */

    public interface QueueElementWithBaggage {
        public DetachedBaggage takeAttachedBaggage();
        public void attachProvidedBaggage(DetachedBaggage baggage);
    }

    public volatile DetachedBaggage QueueElementWithBaggage.attachedQueueElementBaggage;

    public DetachedBaggage QueueElementWithBaggage.takeAttachedBaggage() {
        try {
            return attachedQueueElementBaggage;
        } finally {
            attachedQueueElementBaggage = null;
        }
    }

    public void QueueElementWithBaggage.attachProvidedBaggage(DetachedBaggage baggage) {
        attachedQueueElementBaggage = baggage;
    }

    /* ================================================================================================================
     *
     * All instrumented queue element annotated get baggage added */

    declare parents: (@InstrumentedQueueElement Object)+ implements QueueElementWithBaggage;

    before(QueueElementWithBaggage e): args(e,..) && within(@InstrumentQueues *) && call(* BlockingQueue+.add(..)) {
        e.attachProvidedBaggage(Baggage.fork());
    }

    before(): within(@InstrumentQueues *) && call(* BlockingQueue+.take(..)) {
        Baggage.discard();
    }

    after() returning(QueueElementWithBaggage e): within(@InstrumentQueues *) && call(* BlockingQueue+.take(..)) {
        if (e != null) {
            Baggage.start(e.takeAttachedBaggage());
        }
    }

}


delete relationship

delete relationship

package org.springframework.datastore.graph.neo4j.spi.node;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.reflect.FieldSignature;
import org.neo4j.graphdb.DynamicRelationshipType;
import org.neo4j.graphdb.Node;
import org.neo4j.graphdb.Relationship;
import org.neo4j.graphdb.RelationshipType;
import org.neo4j.graphdb.traversal.TraversalDescription;
import org.neo4j.graphdb.traversal.Traverser;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.datastore.graph.api.GraphEntity;
import org.springframework.datastore.graph.api.NodeBacked;
import org.springframework.datastore.graph.api.RelationshipBacked;
import org.springframework.datastore.graph.neo4j.fieldaccess.*;
import org.springframework.datastore.graph.neo4j.support.GraphDatabaseContext;
import org.springframework.persistence.support.AbstractTypeAnnotatingMixinFields;

import org.springframework.persistence.support.AbstractConstructorEntityInstantiator;
import org.springframework.datastore.graph.neo4j.spi.node.Neo4jConstructorGraphEntityInstantiator;
import org.springframework.datastore.graph.neo4j.spi.relationship.ConstructorBypassingGraphRelationshipInstantiator;

import java.lang.reflect.Field;

import static org.springframework.datastore.graph.neo4j.fieldaccess.DoReturn.unwrap;
import org.springframework.datastore.graph.neo4j.fieldaccess.NodeEntityStateAccessorsFactory;
import org.springframework.persistence.support.StateProvider;

/**
 * Aspect to turn an object annotated with GraphEntity into a graph entity using Neo4J.
 * Delegates all field access (except for fields assumed to be transient)
 * to an underlying Neo4 graph node.
 *
 * @author Rod Johnson
 */
public aspect Neo4jNodeBacking extends AbstractTypeAnnotatingMixinFields<GraphEntity, NodeBacked> {
    private GraphDatabaseContext graphDatabaseContext;
    private NodeEntityStateAccessorsFactory entityStateAccessorsFactory;

    @Autowired
    public void init(GraphDatabaseContext graphDatabaseContext, NodeEntityStateAccessorsFactory entityStateAccessorsFactory) {
        this.graphDatabaseContext = graphDatabaseContext;
        this.entityStateAccessorsFactory = entityStateAccessorsFactory;
    }
	//-------------------------------------------------------------------------
	// Advise user-defined constructors of NodeBacked objects to create a new Neo4J backing node
	//-------------------------------------------------------------------------
	pointcut arbitraryUserConstructorOfNodeBackedObject(NodeBacked entity) :
		execution((@GraphEntity *).new(..)) &&
		!execution((@GraphEntity *).new(Node)) &&
		this(entity) && !cflowbelow(call(* fromStateInternal(..)));

	// Create a new node in the Graph if no Node was passed in a constructor
    before(NodeBacked entity): arbitraryUserConstructorOfNodeBackedObject(entity) {
        if (entityStateAccessorsFactory == null) {
            log.error("entityStateAccessorsFactory not set, not creating accessors for " + entity.getClass());
        } else {
            entity.stateAccessors = entityStateAccessorsFactory.getEntityStateAccessors(entity);
            Node node = StateProvider.retrieveState();
            if (node != null) {
                entity.setUnderlyingState(node);
            } else {
                if (graphDatabaseContext.transactionIsRunning()) {
                    entity.stateAccessors.createAndAssignState();
                } else {
                    log.warn("New Nodebacked created outside of transaction " + entity.getClass());
                }
            }
        }
    }

    // Introduced field
	private transient Node NodeBacked.underlyingNode;
    private transient EntityStateAccessors<NodeBacked,Node> NodeBacked.stateAccessors;

    /*
    public NodeBacked.new(Node n) {
       this.setUnderlyingState(n);
    }
    */
	public void NodeBacked.setUnderlyingState(Node n) {
		this.underlyingNode = n;
        if (this.stateAccessors == null) {
            this.stateAccessors = Neo4jNodeBacking.aspectOf().entityStateAccessorsFactory.getEntityStateAccessors(this);
        } else {
            this.stateAccessors.setUnderlyingState(n);
        }
	}

	public Node NodeBacked.getUnderlyingState() {
		return underlyingNode;
	}

    public EntityStateAccessors NodeBacked.getStateAccessors() {
        return stateAccessors;
    }

    public boolean NodeBacked.hasUnderlyingNode() {
        return underlyingNode!=null;
    }

	public Relationship NodeBacked.relateTo(NodeBacked nb, RelationshipType type) {
		return this.underlyingNode.createRelationshipTo(nb.getUnderlyingState(), type);
	}

	public Long NodeBacked.getNodeId() {
        if (!hasUnderlyingNode()) return null;
		return underlyingNode.getId();
	}

    public  Iterable<? extends NodeBacked> NodeBacked.find(final Class<? extends NodeBacked> targetType, TraversalDescription traversalDescription) {
        if (!hasUnderlyingNode()) throw new IllegalStateException("No node attached to " + this);
        final Traverser traverser = traversalDescription.traverse(this.getUnderlyingState());
        return new NodeBackedNodeIterableWrapper(traverser, targetType, Neo4jNodeBacking.aspectOf().graphDatabaseContext);
    }
    /* todo Andy Clement
    public Iterable<? extends NodeBacked> NodeBacked.traverse(TraversalDescription traversalDescription) {
        final Class<? extends NodeBacked> target = this.getClass();
        return this.traverse(target,traversalDescription);
    }
    */

    /* todo Andy Clement
    public <R extends RelationshipBacked, N extends NodeBacked> R NodeBacked.relateTo(N node, Class<R> relationshipType, String type) {
        Relationship rel = this.getUnderlyingState().createRelationshipTo(node.getUnderlyingState(), DynamicRelationshipType.withName(type));
        Neo4jNodeBacking.aspectOf().relationshipEntityInstantiator.createEntityFromState(rel, relationshipType);
    }
    */

    public RelationshipBacked NodeBacked.relateTo(NodeBacked node, Class<? extends RelationshipBacked> relationshipType, String type) {
        Relationship rel = this.getUnderlyingState().createRelationshipTo(node.getUnderlyingState(), DynamicRelationshipType.withName(type));
        return Neo4jNodeBacking.aspectOf().graphDatabaseContext.createEntityFromState(rel, relationshipType);
    }

    public void NodeBacked.removeRelationshipTo(NodeBacked node, String type) {
        Node myNode=this.getUnderlyingState();
        Node otherNode=node.getUnderlyingState();
        for (Relationship rel : this.getUnderlyingState().getRelationships(DynamicRelationshipType.withName(type))) {
            if (rel.getOtherNode(myNode).equals(otherNode)) {
                rel.delete();
                return;
            }
        }
        return;
    }

    public RelationshipBacked NodeBacked.getRelationshipTo(NodeBacked node, Class<? extends RelationshipBacked> relationshipType, String type) {
        Node myNode=this.getUnderlyingState();
        Node otherNode=node.getUnderlyingState();
        for (Relationship rel : this.getUnderlyingState().getRelationships(DynamicRelationshipType.withName(type))) {
            if (rel.getOtherNode(myNode).equals(otherNode)) return Neo4jNodeBacking.aspectOf().graphDatabaseContext.createEntityFromState(rel, relationshipType);
        }
        return null;
    }

	public final boolean NodeBacked.equals(Object obj) {
        if (obj == this) return true;
        if (!hasUnderlyingNode()) return false;
		if (obj instanceof NodeBacked) {
			return this.getUnderlyingState().equals(((NodeBacked) obj).getUnderlyingState());
		}
		return false;
	}

	public final int NodeBacked.hashCode() {
        if (!hasUnderlyingNode()) return System.identityHashCode(this);
		return getUnderlyingState().hashCode();
	}

    Object around(NodeBacked entity): entityFieldGet(entity) {
        Object result=entity.stateAccessors.getValue(field(thisJoinPoint));
        if (result instanceof DoReturn) return unwrap(result);
        return proceed(entity);
    }

    Object around(NodeBacked entity, Object newVal) : entityFieldSet(entity, newVal) {
        Object result=entity.stateAccessors.setValue(field(thisJoinPoint),newVal);
        if (result instanceof DoReturn) return unwrap(result);
        return proceed(entity,result);
	}

    Field field(JoinPoint joinPoint) {
        FieldSignature fieldSignature = (FieldSignature)joinPoint.getSignature();
        return fieldSignature.getField();
    }
}

package org.springframework.datastore.graph.neo4j.spi.node;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.reflect.FieldSignature;
import org.neo4j.graphdb.DynamicRelationshipType;
import org.neo4j.graphdb.Node;
import org.neo4j.graphdb.Relationship;
import org.neo4j.graphdb.RelationshipType;
import org.neo4j.graphdb.traversal.TraversalDescription;
import org.neo4j.graphdb.traversal.Traverser;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.datastore.graph.api.GraphEntity;
import org.springframework.datastore.graph.api.NodeBacked;
import org.springframework.datastore.graph.api.RelationshipBacked;
import org.springframework.datastore.graph.neo4j.fieldaccess.*;
import org.springframework.datastore.graph.neo4j.support.GraphDatabaseContext;
import org.springframework.persistence.support.AbstractTypeAnnotatingMixinFields;

import org.springframework.persistence.support.AbstractConstructorEntityInstantiator;
import org.springframework.datastore.graph.neo4j.spi.node.Neo4jConstructorGraphEntityInstantiator;
import org.springframework.datastore.graph.neo4j.spi.relationship.ConstructorBypassingGraphRelationshipInstantiator;

import java.lang.reflect.Field;

import static org.springframework.datastore.graph.neo4j.fieldaccess.DoReturn.unwrap;
import org.springframework.datastore.graph.neo4j.fieldaccess.NodeEntityStateAccessorsFactory;
import org.springframework.persistence.support.StateProvider;

/**
 * Aspect to turn an object annotated with GraphEntity into a graph entity using Neo4J.
 * Delegates all field access (except for fields assumed to be transient)
 * to an underlying Neo4 graph node.
 *
 * @author Rod Johnson
 */
public aspect Neo4jNodeBacking extends AbstractTypeAnnotatingMixinFields<GraphEntity, NodeBacked> {
    private GraphDatabaseContext graphDatabaseContext;
    private NodeEntityStateAccessorsFactory entityStateAccessorsFactory;

    @Autowired
    public void init(GraphDatabaseContext graphDatabaseContext, NodeEntityStateAccessorsFactory entityStateAccessorsFactory) {
        this.graphDatabaseContext = graphDatabaseContext;
        this.entityStateAccessorsFactory = entityStateAccessorsFactory;
    }
	//-------------------------------------------------------------------------
	// Advise user-defined constructors of NodeBacked objects to create a new Neo4J backing node
	//-------------------------------------------------------------------------
	pointcut arbitraryUserConstructorOfNodeBackedObject(NodeBacked entity) :
		execution((@GraphEntity *).new(..)) &&
		!execution((@GraphEntity *).new(Node)) &&
		this(entity) && !cflowbelow(call(* fromStateInternal(..)));

	// Create a new node in the Graph if no Node was passed in a constructor
    before(NodeBacked entity): arbitraryUserConstructorOfNodeBackedObject(entity) {
        if (entityStateAccessorsFactory == null) {
            log.error("entityStateAccessorsFactory not set, not creating accessors for " + entity.getClass());
        } else {
            entity.stateAccessors = entityStateAccessorsFactory.getEntityStateAccessors(entity);
            Node node = StateProvider.retrieveState();
            if (node != null) {
                entity.setUnderlyingState(node);
            } else {
                if (graphDatabaseContext.transactionIsRunning()) {
                    entity.stateAccessors.createAndAssignState();
                } else {
                    log.warn("New Nodebacked created outside of transaction " + entity.getClass());
                }
            }
        }
    }

    // Introduced field
	private transient Node NodeBacked.underlyingNode;
    private transient EntityStateAccessors<NodeBacked,Node> NodeBacked.stateAccessors;

    /*
    public NodeBacked.new(Node n) {
       this.setUnderlyingState(n);
    }
    */
	public void NodeBacked.setUnderlyingState(Node n) {
		this.underlyingNode = n;
        if (this.stateAccessors == null) {
            this.stateAccessors = Neo4jNodeBacking.aspectOf().entityStateAccessorsFactory.getEntityStateAccessors(this);
        } else {
            this.stateAccessors.setUnderlyingState(n);
        }
	}

	public Node NodeBacked.getUnderlyingState() {
		return underlyingNode;
	}

    public EntityStateAccessors NodeBacked.getStateAccessors() {
        return stateAccessors;
    }

    public boolean NodeBacked.hasUnderlyingNode() {
        return underlyingNode!=null;
    }

	public Relationship NodeBacked.relateTo(NodeBacked nb, RelationshipType type) {
		return this.underlyingNode.createRelationshipTo(nb.getUnderlyingState(), type);
	}

	public Long NodeBacked.getNodeId() {
        if (!hasUnderlyingNode()) return null;
		return underlyingNode.getId();
	}

    public  Iterable<? extends NodeBacked> NodeBacked.find(final Class<? extends NodeBacked> targetType, TraversalDescription traversalDescription) {
        if (!hasUnderlyingNode()) throw new IllegalStateException("No node attached to " + this);
        final Traverser traverser = traversalDescription.traverse(this.getUnderlyingState());
        return new NodeBackedNodeIterableWrapper(traverser, targetType, Neo4jNodeBacking.aspectOf().graphDatabaseContext);
    }
    /* todo Andy Clement
    public Iterable<? extends NodeBacked> NodeBacked.traverse(TraversalDescription traversalDescription) {
        final Class<? extends NodeBacked> target = this.getClass();
        return this.traverse(target,traversalDescription);
    }
    */

    /* todo Andy Clement
    public <R extends RelationshipBacked, N extends NodeBacked> R NodeBacked.relateTo(N node, Class<R> relationshipType, String type) {
        Relationship rel = this.getUnderlyingState().createRelationshipTo(node.getUnderlyingState(), DynamicRelationshipType.withName(type));
        Neo4jNodeBacking.aspectOf().relationshipEntityInstantiator.createEntityFromState(rel, relationshipType);
    }
    */

    public RelationshipBacked NodeBacked.relateTo(NodeBacked node, Class<? extends RelationshipBacked> relationshipType, String type) {
        Relationship rel = this.getUnderlyingState().createRelationshipTo(node.getUnderlyingState(), DynamicRelationshipType.withName(type));
        return Neo4jNodeBacking.aspectOf().graphDatabaseContext.createEntityFromState(rel, relationshipType);
    }

    public RelationshipBacked NodeBacked.getRelationshipTo(NodeBacked node, Class<? extends RelationshipBacked> relationshipType, String type) {
        Node myNode=this.getUnderlyingState();
        Node otherNode=node.getUnderlyingState();
        for (Relationship rel : this.getUnderlyingState().getRelationships(DynamicRelationshipType.withName(type))) {
            if (rel.getOtherNode(myNode).equals(otherNode)) return Neo4jNodeBacking.aspectOf().graphDatabaseContext.createEntityFromState(rel, relationshipType);
        }
        return null;
    }

	public final boolean NodeBacked.equals(Object obj) {
        if (obj == this) return true;
        if (!hasUnderlyingNode()) return false;
		if (obj instanceof NodeBacked) {
			return this.getUnderlyingState().equals(((NodeBacked) obj).getUnderlyingState());
		}
		return false;
	}

	public final int NodeBacked.hashCode() {
        if (!hasUnderlyingNode()) return System.identityHashCode(this);
		return getUnderlyingState().hashCode();
	}

    Object around(NodeBacked entity): entityFieldGet(entity) {
        Object result=entity.stateAccessors.getValue(field(thisJoinPoint));
        if (result instanceof DoReturn) return unwrap(result);
        return proceed(entity);
    }

    Object around(NodeBacked entity, Object newVal) : entityFieldSet(entity, newVal) {
        Object result=entity.stateAccessors.setValue(field(thisJoinPoint),newVal);
        if (result instanceof DoReturn) return unwrap(result);
        return proceed(entity,result);
	}

    Field field(JoinPoint joinPoint) {
        FieldSignature fieldSignature = (FieldSignature)joinPoint.getSignature();
        return fieldSignature.getField();
    }
}


Formatting

Formatting

/*
 * Copyright (c) 2013 original authors
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.eigengo.monitor.agent.akka;

import akka.actor.*;
import org.eigengo.monitor.agent.AgentConfiguration;
import org.eigengo.monitor.output.CounterInterface;
import scala.Option;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.ConcurrentHashMap;

/**
 * Contains advices for monitoring behaviour of an actor; typically imprisoned in an {@code ActorCell}.
 */
public aspect ActorCellMonitoringAspect extends AbstractMonitoringAspect issingleton() {
    private AkkaAgentConfiguration agentConfiguration;
    private final CounterInterface counterInterface;
    private final Option<String> noActorClazz = Option.empty();
    private final ConcurrentHashMap<Option<String>, AtomicLong> samplingCounters  = new ConcurrentHashMap<Option<String>, AtomicLong>();
    private final ConcurrentHashMap<Option<String>, AtomicInteger> numberOfActors = new ConcurrentHashMap<Option<String>, AtomicInteger>();

    /**
     * Constructs this aspect
     */
    public ActorCellMonitoringAspect() {
        AgentConfiguration<AkkaAgentConfiguration> configuration = getAgentConfiguration("akka", AkkaAgentConfigurationJapi.apply());
        this.agentConfiguration = configuration.agent();
        this.counterInterface = createCounterInterface(configuration.common());
    }

    /**
     * Injects the new {@code AkkaAgentConfiguration} instance.
     *
     * @param agentConfiguration the new configuration
     */
    synchronized final void setAgentConfiguration(AkkaAgentConfiguration agentConfiguration) {
        this.agentConfiguration = agentConfiguration;
    }

    /**
     * decide whether to include this ActorCell in our measurements
     * */
    private boolean includeActorPath(final PathAndClass pathAndClass) {
        // do not monitor our own output code
        if (pathAndClass.actorClassName().isDefined()) {
            if (pathAndClass.actorClassName().get().startsWith("org.eigengo.monitor.output")) return false;
        }

        // skip if not included
        if (!this.agentConfiguration.included().accept(pathAndClass)) return false;
        // skip if excluded
        if (this.agentConfiguration.excluded().accept(pathAndClass)) return false;

        // skip system actor checks if we wish to monitor them
        if (this.agentConfiguration.includeSystemAgents()) return true;

        String userOrSystem = pathAndClass.actorPath().getElements().iterator().next();
        return "user".equals(userOrSystem);
    }

    /**
     * get the sample rate for an actor
     * */
    private int getSampleRate(final PathAndClass pathAndClass) {
        return this.agentConfiguration.sampling().getRate(pathAndClass);
    }

    /**
     * decide whether to sample an actor on a particular occasion
     *
     * @param pathAndClass
     * @return true if we should sample this actor, otherwise false.
     */
    private final boolean sampleMessage(final PathAndClass pathAndClass) {
        int sampleRate = getSampleRate(pathAndClass);
        if (sampleRate == 1) return true;

        this.samplingCounters.putIfAbsent(pathAndClass.actorClassName(), new AtomicLong(0));
        long timesSeenSoFar = this.samplingCounters.get(pathAndClass.actorClassName()).incrementAndGet();
        return (timesSeenSoFar % sampleRate == 1); // == 1 to log first value (incrementAndGet returns updated value)
    }

    /**
     * get the tags for the cell
     * */
    private String[] getTags(final ActorPath actorPath, final Actor actor) {
        List<String> tags = new ArrayList<String>();

        // TODO: Improve detection of routed actors. This relies only on the naming :(.
        String lastPathElement = actorPath.elements().last();
        if (lastPathElement.startsWith("$")) {
            // this is routed actor.
            tags.add(actorPath.parent().toString());
            if (this.agentConfiguration.includeRoutees()) tags.add(actorPath.toString());
        } else {
            // there is no supervisor
            tags.add(actorPath.toString());
        }
        if (actor != null) {
            tags.add(String.format("akka:%s.%s", actor.context().system().name(), actor.getClass().getCanonicalName()));
        }

        return tags.toArray(new String[tags.size()]);
    }

    /**
     * returns the canonical name of the actor type associated with an ActorCell
     * */
    private String uncheckedActorNameFrom(final ActorCell actorCell) {
        return uncheckedActorNameFrom(actorCell.props());
    }

    /**
     * returns the canonical name of the actor type associated with a Props instance
     * */
    private String uncheckedActorNameFrom(final Props props) {
        return props.actorClass().getCanonicalName();
    }

    /**
     * Advises the {@code ActorCell.receiveMessage(message: Object): Unit}
     * We proceed with the pointcut if the actor is to be included in the monitoring *and* this is
     * the 'multiple-of-n'th time we've seen a message for an actor with a sample rate of n.
     *
     * Currently, we sample queue size, the fact that the message is delivered, the simple name of the class of the
     * message, and the time taken to complete the actor's reactive action.
     *
     * @param actorCell the ActorCell where the actor that receives the message "lives"
     * @param msg the incoming message
     */
    Object around(ActorCell actorCell, Object msg) : Pointcuts.actorCellReceiveMessage(actorCell, msg) {

        final ActorPath actorPath = actorCell.self().path();
        final PathAndClass pathAndClass = new PathAndClass(actorPath, Option.apply(uncheckedActorNameFrom(actorCell)));
        if (!includeActorPath(pathAndClass) || !sampleMessage(pathAndClass)) return proceed(actorCell, msg);

        int samplingRate = getSampleRate(pathAndClass);

        // we tag by actor name
        final String[] tags = getTags(actorPath, actorCell.actor());

        // record the queue size
        this.counterInterface.recordGaugeValue("akka.queue.size", actorCell.numberOfMessages(), tags);
        // record the message, general and specific
        this.counterInterface.incrementCounter("akka.actor.delivered", samplingRate, tags);
        this.counterInterface.incrementCounter("akka.actor.delivered." + msg.getClass().getSimpleName(), samplingRate, tags);

        // measure the time. we're using the ``nanoTime`` call to access the high-precision timer.
        // since we're not really interested in wall time, but just some increasing measure of
        // elapsed time
        Object result = null;
        final long start = System.nanoTime();
        // result will always be ``null``, because target returns ``Unit``
        result = proceed(actorCell, msg);
        // ns or µs is too fine. ms will do for now.
        final long duration = (System.nanoTime() - start) / 1000000;

        // record the actor duration
        this.counterInterface.recordExecutionTime("akka.actor.duration", (int)duration, tags);

        // return null would do the trick, but we want to be _proper_.
        return result;
    }

    /**
     * Advises the {@code ActorCell.handleInvokeFailure(_, failure: Throwable): Unit}
     *
     * @param actorCell the ActorCell where the error spreads from
     * @param failure the exception that escaped from the {@code receive} method
     */
    before(ActorCell actorCell, Throwable failure) : Pointcuts.actorCellHandleInvokeFailure(actorCell, failure) {
        // record the error, general and specific
        String[] tags = getTags(actorCell.self().path(), actorCell.actor());
        this.counterInterface.incrementCounter("akka.actor.error", tags);
        this.counterInterface.incrementCounter(String.format("akka.actor.error.%s", failure.getMessage()), tags);
    }

    /**
     * Advises the {@code EventStream.publish(event: Any): Unit}
     *
     * @param event the received event
     */
    before(Object event) : Pointcuts.eventStreamPublish(event) {
        if (event instanceof UnhandledMessage) {
            UnhandledMessage unhandledMessage = (UnhandledMessage)event;
            String[] tags = getTags(unhandledMessage.recipient().path(), null);
            this.counterInterface.incrementCounter("akka.actor.undelivered", tags);
            this.counterInterface.incrementCounter("akka.actor.undelivered." + unhandledMessage.getMessage().getClass().getSimpleName(), tags);
        }
    }

    /**
     * Advises the {@code actorOf} method of {@code ActorRefFactory} implementations
     *
     * @param props the {@code Props} instance used in the call
     * @param actor the {@code ActorRef} returned from the call
     */
    after(Props props) returning (ActorRef actor) : Pointcuts.anyActorOf(props) {
        if (!includeActorPath(new PathAndClass(actor.path(), this.noActorClazz))) return;
        final String uncheckedClassName = uncheckedActorNameFrom(props);
        final Option<String> className = Option.apply(uncheckedClassName);

        this.numberOfActors.putIfAbsent(className, new AtomicInteger(0));
        // increment and get the current number of actors of this type (if the value was 0, then this returns 1 -- which is correct)
        final int currentNumberOfActors = this.numberOfActors.get(className).incrementAndGet();

        // record the current number of actors of this type
        this.counterInterface.recordGaugeValue("akka.actor.count", currentNumberOfActors, uncheckedClassName);
    }

    /**
     * Advises the {@code LocalActorRef.stop} method
     *
     * @param actorCell the {@code ActorCell} of the actor being stopped
     */
    after(ActorCell actorCell) : Pointcuts.actorCellInternalStop(actorCell) {
        if (!includeActorPath(new PathAndClass(actorCell.self().path(), this.noActorClazz))) return;
        final String uncheckedClassName = uncheckedActorNameFrom(actorCell);
        final Option<String> className = Option.apply(uncheckedClassName);

        this.numberOfActors.putIfAbsent(className, new AtomicInteger(0));
        // decrement and get the current number of actors of this type (if the value was 1, then this returns 0 -- which is correct)
        final int currentNumberOfActors = this.numberOfActors.get(className).decrementAndGet();

        this.counterInterface.recordGaugeValue("akka.actor.count", currentNumberOfActors, uncheckedClassName);
    }

}

/*
 * Copyright (c) 2013 original authors
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.eigengo.monitor.agent.akka;

import akka.actor.*;
import org.eigengo.monitor.agent.AgentConfiguration;
import org.eigengo.monitor.output.CounterInterface;
import scala.Option;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.ConcurrentHashMap;

/**
 * Contains advices for monitoring behaviour of an actor; typically imprisoned in an {@code ActorCell}.
 */
public aspect ActorCellMonitoringAspect extends AbstractMonitoringAspect issingleton() {
    private AkkaAgentConfiguration agentConfiguration;
    private final CounterInterface counterInterface;
    private final Option<String> noActorClazz = Option.empty();
    private final ConcurrentHashMap<Option<String>, AtomicLong> samplingCounters  = new ConcurrentHashMap<Option<String>, AtomicLong>();
    private final ConcurrentHashMap<Option<String>, AtomicInteger> numberOfActors = new ConcurrentHashMap<Option<String>, AtomicInteger>();

    /**
     * Constructs this aspect
     */
    public ActorCellMonitoringAspect() {
        AgentConfiguration<AkkaAgentConfiguration> configuration = getAgentConfiguration("akka", AkkaAgentConfigurationJapi.apply());
        this.agentConfiguration = configuration.agent();
        this.counterInterface = createCounterInterface(configuration.common());
    }

    /**
     * Injects the new {@code AkkaAgentConfiguration} instance.
     *
     * @param agentConfiguration the new configuration
     */
    synchronized final void setAgentConfiguration(AkkaAgentConfiguration agentConfiguration) {
        this.agentConfiguration = agentConfiguration;
    }

    /**
     * decide whether to include this ActorCell in our measurements
     * */
    private boolean includeActorPath(final PathAndClass pathAndClass) {
        // do not monitor our own output code
        if (pathAndClass.actorClassName().isDefined()) {
            if (pathAndClass.actorClassName().get().startsWith("org.eigengo.monitor.output")) return false;
        }

        // skip if not included
        if (!this.agentConfiguration.included().accept(pathAndClass)) return false;
        // skip if excluded
        if (this.agentConfiguration.excluded().accept(pathAndClass)) return false;

        // skip system actor checks if we wish to monitor them
        if (this.agentConfiguration.includeSystemAgents()) return true;

        String userOrSystem = pathAndClass.actorPath().getElements().iterator().next();
        return "user".equals(userOrSystem);
    }

    /**
     * get the sample rate for an actor
     * */
    private int getSampleRate(final PathAndClass pathAndClass) {
        return this.agentConfiguration.sampling().getRate(pathAndClass);
    }

    /**
     * decide whether to sample an actor on a particular occasion
     *
     * @param pathAndClass
     * @return true if we should sample this actor, otherwise false.
     */
    private final boolean sampleMessage(final PathAndClass pathAndClass) {
        int sampleRate = getSampleRate(pathAndClass);
        if (sampleRate == 1) return true;

        this.samplingCounters.putIfAbsent(pathAndClass.actorClassName(), new AtomicLong(0));
        long timesSeenSoFar = this.samplingCounters.get(pathAndClass.actorClassName()).incrementAndGet();
        return (timesSeenSoFar % sampleRate == 1); // == 1 to log first value (incrementAndGet returns updated value)
    }

    /**
     * get the tags for the cell
     * */
    private String[] getTags(final ActorPath actorPath, final Actor actor) {
        List<String> tags = new ArrayList<String>();

        // TODO: Improve detection of routed actors. This relies only on the naming :(.
        String lastPathElement = actorPath.elements().last();
        if (lastPathElement.startsWith("$")) {
            // this is routed actor.
            tags.add(actorPath.parent().toString());
            if (this.agentConfiguration.includeRoutees()) tags.add(actorPath.toString());
        } else {
            // there is no supervisor
            tags.add(actorPath.toString());
        }
        if (actor != null) {
            tags.add(String.format("akka:%s.%s", actor.context().system().name(), actor.getClass().getCanonicalName()));
        }

        return tags.toArray(new String[tags.size()]);
    }

    /**
     * returns the canonical name of the actor type associated with an ActorCell
     * */
    private String uncheckedActorNameFrom(final ActorCell actorCell) {
        return uncheckedActorNameFrom(actorCell.props());
    }

    /**
     * returns the canonical name of the actor type associated with a Props instance
     * */
    private String uncheckedActorNameFrom(final Props props) {
        return props.actorClass().getCanonicalName();
    }

    /**
     * Advises the {@code ActorCell.receiveMessage(message: Object): Unit}
     * We proceed with the pointcut if the actor is to be included in the monitoring *and* this is
     * the 'multiple-of-n'th time we've seen a message for an actor with a sample rate of n.
     *
     * Currently, we sample queue size, the fact that the message is delivered, the simple name of the class of the
     * message, and the time taken to complete the actor's reactive action.
     *
     * @param actorCell the ActorCell where the actor that receives the message "lives"
     * @param msg the incoming message
     */
    Object around(ActorCell actorCell, Object msg) : Pointcuts.actorCellReceiveMessage(actorCell, msg) {

        final ActorPath actorPath = actorCell.self().path();
        final PathAndClass pathAndClass = new PathAndClass(actorPath, Option.apply(uncheckedActorNameFrom(actorCell)));
        if (!includeActorPath(pathAndClass) || !sampleMessage(pathAndClass)) return proceed(actorCell, msg);

        int samplingRate = getSampleRate(pathAndClass);

        // we tag by actor name
        final String[] tags = getTags(actorPath, actorCell.actor());

        // record the queue size
        this.counterInterface.recordGaugeValue("akka.queue.size", actorCell.numberOfMessages(), tags);
        // record the message, general and specific
        this.counterInterface.incrementCounter("akka.actor.delivered", samplingRate, tags);
        this.counterInterface.incrementCounter("akka.actor.delivered." + msg.getClass().getSimpleName(), samplingRate, tags);

        // measure the time. we're using the ``nanoTime`` call to access the high-precision timer.
        // since we're not really interested in wall time, but just some increasing measure of
        // elapsed time
        Object result = null;
        final long start = System.nanoTime();
        // result will always be ``null``, because target returns ``Unit``
        result = proceed(actorCell, msg);
        // ns or µs is too fine. ms will do for now.
        final long duration = (System.nanoTime() - start) / 1000000;

        // record the actor duration
        this.counterInterface.recordExecutionTime("akka.actor.duration", (int)duration, tags);

        // return null would do the trick, but we want to be _proper_.
        return result;
    }

    /**
     * Advises the {@code ActorCell.handleInvokeFailure(_, failure: Throwable): Unit}
     *
     * @param actorCell the ActorCell where the error spreads from
     * @param failure the exception that escaped from the {@code receive} method
     */
    before(ActorCell actorCell, Throwable failure) : Pointcuts.actorCellHandleInvokeFailure(actorCell, failure) {
        // record the error, general and specific
        String[] tags = getTags(actorCell.self().path(), actorCell.actor());
        this.counterInterface.incrementCounter("akka.actor.error", tags);
        this.counterInterface.incrementCounter(String.format("akka.actor.error.%s", failure.getMessage()), tags);
    }

    /**
     * Advises the {@code EventStream.publish(event: Any): Unit}
     *
     * @param event the received event
     */
    before(Object event) : Pointcuts.eventStreamPublish(event) {
        if (event instanceof UnhandledMessage) {
            UnhandledMessage unhandledMessage = (UnhandledMessage)event;
            String[] tags = getTags(unhandledMessage.recipient().path(), null);
            this.counterInterface.incrementCounter("akka.actor.undelivered", tags);
            this.counterInterface.incrementCounter("akka.actor.undelivered." + unhandledMessage.getMessage().getClass().getSimpleName(), tags);
        }
    }

    /**
     * Advises the {@code actorOf} method of {@code ActorRefFactory} implementations
     *
     * @param props the {@code Props} instance used in the call
     * @param actor the {@code ActorRef} returned from the call
     */
    after(Props props) returning (ActorRef actor): Pointcuts.anyActorOf(props) {
        recordActorCreation(props, actor);
    }

    /**
     * method containing the recording logic for advising actor creation
     * */
    private void recordActorCreation(Props props, ActorRef actor) {
        if (!includeActorPath(new PathAndClass(actor.path(), this.noActorClazz))) return;
        final String uncheckedClassName = uncheckedActorNameFrom(props);
        final Option<String> className = Option.apply(uncheckedClassName);

        this.numberOfActors.putIfAbsent(className, new AtomicInteger(0));
        // increment and get the current number of actors of this type (if the value was 0, then this returns 1 -- which is correct)
        final int currentNumberOfActors = this.numberOfActors.get(className).incrementAndGet();

        // record the current number of actors of this type
        this.counterInterface.recordGaugeValue("akka.actor.count", currentNumberOfActors, uncheckedClassName);
    }

    /**
     * Advises the {@code LocalActorRef.stop} method
     *
     * @param actorCell the {@code ActorCell} of the actor being stopped
     */
    after(ActorCell actorCell) : Pointcuts.actorCellInternalStop(actorCell) {
        if (!includeActorPath(new PathAndClass(actorCell.self().path(), this.noActorClazz))) return;
        final String uncheckedClassName = uncheckedActorNameFrom(actorCell);
        final Option<String> className = Option.apply(uncheckedClassName);

        this.numberOfActors.putIfAbsent(className, new AtomicInteger(0));
        // decrement and get the current number of actors of this type (if the value was 1, then this returns 0 -- which is correct)
        final int currentNumberOfActors = this.numberOfActors.get(className).decrementAndGet();

        this.counterInterface.recordGaugeValue("akka.actor.count", currentNumberOfActors, uncheckedClassName);
    }

}


fixed pointcut for type-level annotation to only apply to public methods (SPR-8890)

/*
 * Copyright 2002-2010 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.springframework.transaction.aspectj;

import org.springframework.transaction.annotation.AnnotationTransactionAttributeSource;
import org.springframework.transaction.annotation.Transactional;

/**
 * Concrete AspectJ transaction aspect using Spring's @Transactional annotation.
 *
 *
When using this aspect, you must annotate the implementation class
 * (and/or methods within that class), not the interface (if any) that
 * the class implements. AspectJ follows Java's rule that annotations on
 * interfaces are not inherited.
 *
 *
An @Transactional annotation on a class specifies the default transaction
 * semantics for the execution of any public operation in the class.
 *
 *
An @Transactional annotation on a method within the class overrides the
 * default transaction semantics given by the class annotation (if present).
 * Any method may be annotated (regardless of visibility).
 * Annotating non-public methods directly is the only way
 * to get transaction demarcation for the execution of such operations.
 *
 * @author Rod Johnson
 * @author Ramnivas Laddad
 * @author Adrian Colyer
 * @since 2.0
 * @see org.springframework.transaction.annotation.Transactional
 */
public aspect AnnotationTransactionAspect extends AbstractTransactionAspect {

	public AnnotationTransactionAspect() {
		super(new AnnotationTransactionAttributeSource(false));
	}

	/**
	 * Matches the execution of any public method in a type with the
	 * Transactional annotation, or any subtype of a type with the
	 * Transactional annotation.
	 */
	private pointcut executionOfAnyPublicMethodInAtTransactionalType() :
		execution(public * ((@Transactional *)+).*(..)) && within(@Transactional *);

	/**
	 * Matches the execution of any method with the
	 * Transactional annotation.
	 */
	private pointcut executionOfTransactionalMethod() :
		execution(@Transactional * *(..));

	/**
	 * Definition of pointcut from super aspect - matched join points
	 * will have Spring transaction management applied.
	 */
	protected pointcut transactionalMethodExecution(Object txObject) :
		(executionOfAnyPublicMethodInAtTransactionalType()
		 || executionOfTransactionalMethod() )
		 && this(txObject);

}

/*
 * Copyright 2002-2010 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.springframework.transaction.aspectj;

import org.springframework.transaction.annotation.AnnotationTransactionAttributeSource;
import org.springframework.transaction.annotation.Transactional;

/**
 * Concrete AspectJ transaction aspect using Spring's @Transactional annotation.
 *
 *
When using this aspect, you must annotate the implementation class
 * (and/or methods within that class), not the interface (if any) that
 * the class implements. AspectJ follows Java's rule that annotations on
 * interfaces are not inherited.
 *
 *
An @Transactional annotation on a class specifies the default transaction
 * semantics for the execution of any public operation in the class.
 *
 *
An @Transactional annotation on a method within the class overrides the
 * default transaction semantics given by the class annotation (if present).
 * Any method may be annotated (regardless of visibility).
 * Annotating non-public methods directly is the only way
 * to get transaction demarcation for the execution of such operations.
 *
 * @author Rod Johnson
 * @author Ramnivas Laddad
 * @author Adrian Colyer
 * @since 2.0
 * @see org.springframework.transaction.annotation.Transactional
 */
public aspect AnnotationTransactionAspect extends AbstractTransactionAspect {

	public AnnotationTransactionAspect() {
		super(new AnnotationTransactionAttributeSource(false));
	}

	/**
	 * Matches the execution of any public method in a type with the
	 * Transactional annotation, or any subtype of a type with the
	 * Transactional annotation.
	 */
	private pointcut executionOfAnyPublicMethodInAtTransactionalType() :
		execution(public * ((@Transactional *)+).*(..)) && within(@Transactional);

	/**
	 * Matches the execution of any method with the
	 * Transactional annotation.
	 */
	private pointcut executionOfTransactionalMethod() :
		execution(@Transactional * *(..));

	/**
	 * Definition of pointcut from super aspect - matched join points
	 * will have Spring transaction management applied.
	 */
	protected pointcut transactionalMethodExecution(Object txObject) :
		(executionOfAnyPublicMethodInAtTransactionalType()
		 || executionOfTransactionalMethod() )
		 && this(txObject);

}


Event printing commented

Event printing commented

package benchmark.competition.java.larva.monitoring;

import monitoring.impl.MonitorFactory;
import monitoring.intf.Monitor;
import properties.Property;
import properties.competition.Larva;
import structure.impl.other.Verdict;
import structure.intf.QEA;
import benchmark.competition.java.larva.transactionsystem.UserSession;

public aspect LarvaTenAspect {

	private final Monitor monitor;

	private final int OPEN_SESSION = 1;
	private final int CLOSE_SESSION = 2;
	private final int LOG = 3;

	public LarvaTenAspect() {
		QEA qea = new Larva().make(Property.LARVA_TEN);
		monitor = MonitorFactory.create(qea);
	}

	pointcut openSession(UserSession session) :
		call(void UserSession.openSession()) && target(session);

	pointcut closeSession(UserSession session) :
		call(void UserSession.closeSession()) && target(session);

	pointcut log(UserSession session) :
		call(void UserSession.log(String)) && target(session);

	before(UserSession session) : openSession(session) {
//		System.out.println(">> openSession(" + session.getOwner() + ")");
		Verdict verdict = monitor.step(OPEN_SESSION, session.getOwner());
		if (verdict == Verdict.FAILURE || verdict == Verdict.WEAK_FAILURE) {
			System.err.println("Violation in Larva 10. [userId="
					+ session.getOwner() + "] [sid=" + session.getId()
					+ "]. Logging can only be made to an active session.");
		}
	};

	before(UserSession session) : closeSession(session) {
//		System.out.println(">> closeSession(" + session.getOwner() + ")");
		Verdict verdict = monitor.step(CLOSE_SESSION, session.getOwner());
		if (verdict == Verdict.FAILURE || verdict == Verdict.WEAK_FAILURE) {
			System.err.println("Violation in Larva 10. [userId="
					+ session.getOwner() + "] [sid=" + session.getId()
					+ "]. Logging can only be made to an active session.");
		}
	};

	before(UserSession session) : log(session) {
//		System.out.println(">> log(" + session.getOwner() + ")");
		Verdict verdict = monitor.step(LOG, session.getOwner());
		if (verdict == Verdict.FAILURE || verdict == Verdict.WEAK_FAILURE) {
			System.err.println("Violation in Larva 10. [userId="
					+ session.getOwner() + "] [sid=" + session.getId()
					+ "]. Logging can only be made to an active session.");
		}
	};
}

package benchmark.competition.java.larva.monitoring;

import monitoring.impl.MonitorFactory;
import monitoring.intf.Monitor;
import properties.Property;
import properties.competition.Larva;
import structure.impl.other.Verdict;
import structure.intf.QEA;
import benchmark.competition.java.larva.transactionsystem.UserSession;

public aspect LarvaTenAspect {

	private final Monitor monitor;

	private final int OPEN_SESSION = 1;
	private final int CLOSE_SESSION = 2;
	private final int LOG = 3;

	public LarvaTenAspect() {
		QEA qea = new Larva().make(Property.LARVA_TEN);
		monitor = MonitorFactory.create(qea);
	}

	pointcut openSession(UserSession session) :
		call(void UserSession.openSession()) && target(session);

	pointcut closeSession(UserSession session) :
		call(void UserSession.closeSession()) && target(session);

	pointcut log(UserSession session) :
		call(void UserSession.log(String)) && target(session);

	before(UserSession session) : openSession(session) {
		System.out.println(">> openSession(" + session.getOwner() + ")");
		Verdict verdict = monitor.step(OPEN_SESSION, session.getOwner());
		if (verdict == Verdict.FAILURE || verdict == Verdict.WEAK_FAILURE) {
			System.err.println("Violation in Larva 10. [userId="
					+ session.getOwner() + "] [sid=" + session.getId()
					+ "]. Logging can only be made to an active session.");
		}
	};

	before(UserSession session) : closeSession(session) {
		System.out.println(">> closeSession(" + session.getOwner() + ")");
		Verdict verdict = monitor.step(CLOSE_SESSION, session.getOwner());
		if (verdict == Verdict.FAILURE || verdict == Verdict.WEAK_FAILURE) {
			System.err.println("Violation in Larva 10. [userId="
					+ session.getOwner() + "] [sid=" + session.getId()
					+ "]. Logging can only be made to an active session.");
		}
	};

	before(UserSession session) : log(session) {
		System.out.println(">> log(" + session.getOwner() + ")");
		Verdict verdict = monitor.step(LOG, session.getOwner());
		if (verdict == Verdict.FAILURE || verdict == Verdict.WEAK_FAILURE) {
			System.err.println("Violation in Larva 10. [userId="
					+ session.getOwner() + "] [sid=" + session.getId()
					+ "]. Logging can only be made to an active session.");
		}
	};
}


removed dead imports

removed dead imports

/*
 * Copyright (c) 2013 original authors
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.eigengo.monitor.agent.akka;

import com.typesafe.config.Config;
import org.eigengo.monitor.agent.AgentConfiguration;
import org.eigengo.monitor.agent.AgentConfigurationFactory;
import org.eigengo.monitor.agent.CommonAgentConfiguration;
import org.eigengo.monitor.output.CounterInterface;
import org.eigengo.monitor.output.NullCounterInterface;
import scala.Function1;

abstract aspect AbstractMonitoringAspect {

    protected final CounterInterface createCounterInterface(CommonAgentConfiguration configuration) {
        try {
            CounterInterface counterInterface = (CounterInterface)Class.forName(configuration.counterInterfaceClassName()).newInstance();
            return counterInterface;
        } catch (final ReflectiveOperationException e) {
            return new NullCounterInterface();
        } catch (final ClassCastException e) {
            return new NullCounterInterface();
        }
    }

    protected final <A> AgentConfiguration<A> getAgentConfiguration(String agentName, Function1<Config, A> agent) {
        return AgentConfigurationFactory.getAgentCofiguration(agentName, agent);
    }
}

/*
 * Copyright (c) 2013 original authors
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.eigengo.monitor.agent.akka;

import com.typesafe.config.Config;
import org.eigengo.monitor.agent.AgentConfiguration;
import org.eigengo.monitor.agent.AgentConfigurationFactory;
import org.eigengo.monitor.agent.CommonAgentConfiguration;
import org.eigengo.monitor.output.CounterInterface;
import org.eigengo.monitor.output.NullCounterInterface;
import scala.Function1;

import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicLong;

abstract aspect AbstractMonitoringAspect {

    protected final CounterInterface createCounterInterface(CommonAgentConfiguration configuration) {
        try {
            CounterInterface counterInterface = (CounterInterface)Class.forName(configuration.counterInterfaceClassName()).newInstance();
            return counterInterface;
        } catch (final ReflectiveOperationException e) {
            return new NullCounterInterface();
        } catch (final ClassCastException e) {
            return new NullCounterInterface();
        }
    }

    protected final <A> AgentConfiguration<A> getAgentConfiguration(String agentName, Function1<Config, A> agent) {
        return AgentConfigurationFactory.getAgentCofiguration(agentName, agent);
    }
}


Slight rewrite

Slight rewrite

import net.winstone.core.WinstoneConstant;
import org.slf4j.LoggerFactory;

// Java EE servlet API
import javax.servlet.Servlet;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServlet;

// Used to scope pointcuts
import net.winstone.core.listener.RequestHandlerThread;
import net.winstone.core.ServletConfiguration;
import net.winstone.core.SimpleRequestDispatcher;

// Rate Limiter
import edu.utwente.aop.tools.RateLimiter;
import java.util.concurrent.TimeUnit;

// exceptions from Servlet.service(..)
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletResponse;

// We redirect the call flow to error servlet
import net.winstone.servlet.ErrorServlet;

/**
 * Ensure that the request is allowed by the RateLimiter before continuing..
 */
public privileged aspect RateLimitingAspect  percflow(processRequest()) {
    private enum State { NOTICKET, TICKET, NOTICKET_AVAILABLE };

    public final int RESPONSE_TIME_MS = 1000;

    /** public because it is implicitly accessed from WebAppConfiguration */
    public State state = State.NOTICKET;

    static org.slf4j.Logger logger = LoggerFactory.getLogger(Servlet.class);
    // Allow 5 requests per limit
    private static RateLimiter limits = new RateLimiter(5, 1, TimeUnit.MINUTES);

    /**
     * Pointcut that captures all requests flows in which a request is handled.
     * This is used to instantiate this aspect per request that is handled.
     *
     * within(RequestHandlerThread) is not needed because processRequest is private, and the compositional
     * behaviour that would result from calling this using a priviliged aspect probably
     * would be sensible.
     */
    pointcut processRequest(): call(void RequestHandlerThread.processRequest(..));

    /**
     * Only service calls from within the configuration should be augmented
     */
    pointcut inWinstoneServletConfiguration(): within(ServletConfiguration);

    /**
     * When the control flow has reached HttpServletResponse.sendError we disable the ratelimiting
     */
    pointcut handlingError(): cflow(call(void HttpServletResponse.sendError(int, String)));

    /** Around did not work when trying to abstract call(...) && args into a seperate pointcut
     * @throws IOException when sendError throws an exception.
     */
    void around(ServletRequest req, ServletResponse resp) throws IOException:
            inWinstoneServletConfiguration() && !handlingError() &&
            call(void Servlet.service(..)) && args(req, resp) {
        if (!ensureTicket()) {
            // Instantiate new 503-UNAVAILABLE
            logger.info("No tickets: redirecting request to 503");
            ((HttpServletResponse) resp).sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE, "Rate limit reached - Throttled");
        } else {
            proceed(req, resp);
        }
    }

    /**
     * Try to get a ticket from the state machine
     * @return true when this aspect instance (control flow) has a valid ticket
     */
    private boolean ensureTicket() {
        switch (state) {
            case TICKET:
                logger.info("Re-using ticket");
                break;
            case NOTICKET:
                if (limits.acquire(RESPONSE_TIME_MS)) {
                    logger.info("Acquired new ticket for {}", this);
                    state = State.TICKET;
                } else {
                    state = State.NOTICKET_AVAILABLE;
                }
                break;
            case NOTICKET_AVAILABLE:
                logger.info("No tickets available");
                break;
        }

        return state == State.TICKET;
    }
}

import net.winstone.core.WinstoneConstant;
import org.slf4j.LoggerFactory;

// Java EE servlet API
import javax.servlet.Servlet;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServlet;

// Used to scope pointcuts
import net.winstone.core.listener.RequestHandlerThread;
import net.winstone.core.ServletConfiguration;
import net.winstone.core.SimpleRequestDispatcher;

// Rate Limiter
import edu.utwente.aop.tools.RateLimiter;
import java.util.concurrent.TimeUnit;

// exceptions from Servlet.service(..)
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletResponse;

// We redirect the call flow to error servlet
import net.winstone.servlet.ErrorServlet;

/**
 * Ensure that the request is allowed by the RateLimiter before continuing..
 */
public privileged aspect RateLimitingAspect  percflow(processRequest()) {
    private enum State { NOTICKET, TICKET, NOTICKET_AVAILABLE };

    public final int RESPONSE_TIME_MS = 1000;

    /** public because it is implicitly accessed from WebAppConfiguration */
    public State state = State.NOTICKET;

    static org.slf4j.Logger logger = LoggerFactory.getLogger(Servlet.class);
    // Allow 5 requests per limit
    private static RateLimiter limits = new RateLimiter(5, 1, TimeUnit.MINUTES);

    /**
     * Pointcut that captures all requests flows in which a request is handled.
     * This is used to instantiate this aspect per request that is handled.
     *
     * within(RequestHandlerThread) is not needed because processRequest is private, and the compositional
     * behaviour that would result from calling this using a priviliged aspect probably
     * would be sensible.
     */
    pointcut processRequest(): call(void RequestHandlerThread.processRequest(..));

    /**
     * Only service calls from within the configuration should be augmented
     */
    pointcut inWinstoneServletConfiguration(): within(ServletConfiguration);

    /**
     * When the control flow has reached HttpServletResponse.sendError we disable the ratelimiting
     */
    pointcut handlingError(): cflow(call(void HttpServletResponse.sendError(int, String)));

    /** Around did not work when trying to abstract call(...) && args into a seperate pointcut
     * @throws IOException when sendError throws an exception.
     */
    void around(ServletRequest req, ServletResponse resp) throws IOException: inWinstoneServletConfiguration() && !handlingError() && call(void Servlet.service(..)) && args(req, resp) {

        ensureTicket();

        if (state != State.TICKET) {
            // Instantiate new 503-UNAVAILABLE
            logger.info("Redirecting request to 503");
            ((HttpServletResponse) resp).sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE, "Rate limit reached - Throttled");
        } else {
            proceed(req, resp);
        }
    }

    /**
     * Try to get a ticket from the state machine
     */
    private void ensureTicket() {
        switch (state) {
            case TICKET:
                logger.info("Re-using ticket");
                break;
            case NOTICKET:
                logger.info("Trying to acquire ticket for " + this);
                if (limits.acquire(RESPONSE_TIME_MS)) {
                    logger.info("Acquired ticket");
                    state = State.TICKET;
                } else {
                    state = State.NOTICKET_AVAILABLE;
                }
                break;
            case NOTICKET_AVAILABLE:
                logger.info("No tickets available");
                break;
        }
    }
}