Check value for executor service manager to avoid NullPointerException if no executor is defined in configuration file

/*
 *     ParallelJ, framework for parallel computing
 *
 *     Copyright (C) 2010, 2011, 2012 Atos Worldline or third-party contributors as
 *     indicated by the @author tags or express copyright attribution
 *     statements applied by the authors.
 *
 *     This library is free software; you can redistribute it and/or
 *     modify it under the terms of the GNU Lesser General Public
 *     License as published by the Free Software Foundation; either
 *     version 2.1 of the License.
 *
 *     This library is distributed in the hope that it will be useful,
 *     but WITHOUT ANY WARRANTY; without even the implied warranty of
 *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 *     Lesser General Public License for more details.
 *
 *     You should have received a copy of the GNU Lesser General Public
 *     License along with this library; if not, write to the Free Software
 *     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 */
package org.parallelj.launching.internal;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;

import org.parallelj.Programs;
import org.parallelj.internal.conf.ConfigurationService;
import org.parallelj.internal.conf.ParalleljConfigurationManager;
import org.parallelj.internal.conf.pojos.CExecutors;
import org.parallelj.internal.conf.pojos.ParalleljConfiguration;
import org.parallelj.internal.kernel.KProcess;
import org.parallelj.internal.kernel.KProcessor;
import org.parallelj.internal.kernel.procedure.CallableProcedure.CallableCall.CallableCallRunnable;
import org.parallelj.internal.kernel.procedure.RunnableProcedure.RunnableCall.RunnableCallRunnable;
import org.parallelj.internal.kernel.procedure.SubProcessProcedure.SubProcessCall.SubProcessRunnable;
import org.parallelj.internal.reflect.ProcessHelperImpl;
import org.parallelj.launching.Launch;
import org.parallelj.launching.LaunchException;
import org.parallelj.launching.LaunchingMessageKind;
import org.parallelj.launching.ProgramReturnCodes;
import org.parallelj.launching.internal.LaunchManagement.LaunchAdapter.IKProcessorLaunch;
import org.parallelj.mirror.ParallelJThreadFactory;

import org.parallelj.launching.executors.ExecutorServiceManager;

/**
 * Implements the synchLaunch(..) and aSynchLaunch(..) methods of Launch.
 *
 *
 */
privileged aspect LaunchManagement {

	private static Map<KProcessor, LaunchImpl<?>> phl = new ConcurrentHashMap<>();

	static privileged public aspect LaunchAdapter percflow(launchInstance() ) {

		declare precedence :
			org.parallelj.internal.kernel.Identifiers,
			org.parallelj.internal.reflect.ProgramAdapter,
			org.parallelj.internal.util.sm.impl.KStateMachines,
			org.parallelj.internal.util.sm.impl.KStateMachines.PerMachine,
			org.parallelj.Executables$PerExecutable,
			org.parallelj.internal.reflect.ProgramAdapter.PerProgram,
			org.parallelj.internal.log.Logs;

		pointcut launchInstance() : execution(* Launch+.*(..));

		interface IKProcessorLaunch {}
		LaunchImpl<?> IKProcessorLaunch.launch;
		declare parents: KProcessor implements IKProcessorLaunch;

		interface ILaunchExecutors {}
		ExecutorServiceManager ILaunchExecutors.executorServiceManager;
		declare parents: LaunchImpl implements ILaunchExecutors;

		private LaunchingObservable observable = new LaunchingObservable();

		boolean stopExecutorServiceAfterExecution = false;

		private static class AsyncThread implements Runnable {

			private LaunchAdapter launchAdapter;
			private LaunchImpl<?> launch;

			public AsyncThread(LaunchImpl<?> launch, LaunchAdapter launchAdapter) {
				this.launch = launch;
				this.launchAdapter = launchAdapter;
			}

			@Override
			public void run() {
				this.launchAdapter.internalSyncLaunch(this.launch);
			}
		}

		@SuppressWarnings({ "rawtypes", "unchecked" })
		Launch around(LaunchImpl launch) throws LaunchException:
					execution(public Launch Launch+.synchLaunch(..) throws LaunchException)
						&& this(launch) {
			proceed(launch);
			launch.processHelper = Programs.as(launch.jobInstance);
			launch.getLaunchResult().setStatusCode(ProgramReturnCodes.RUNNING);
			this.observable.prepareLaunching(launch);

			return internalSyncLaunch(launch);
		}

		@SuppressWarnings({ "rawtypes", "unchecked" })
		Launch around(LaunchImpl launch) throws LaunchException:
					execution(public Launch Launch+.aSynchLaunch() throws LaunchException)
						&& this(launch) {
			proceed(launch);
			launch.processHelper = Programs.as(launch.jobInstance);
			launch.getLaunchResult().setStatusCode(ProgramReturnCodes.RUNNING);
			this.observable.prepareLaunching(launch);
			if (launch.getExecutorService()!=null) {
				launch.getExecutorService().execute(new AsyncThread(launch, this));
			} else {
				ParallelJThreadFactory.getInstance().newThread(new AsyncThread(launch, this)).start();
	 		}
			return launch;
		}

		@SuppressWarnings("rawtypes")
		private Launch<?> internalSyncLaunch(LaunchImpl<?> launch) {
			LaunchingMessageKind.ILAUNCH0002.format(launch.getJobInstance()
					.getClass().getCanonicalName(), launch.getLaunchId());
			initializeExecutors(launch);
			ProcessHelperImpl processHelper = (ProcessHelperImpl)launch.getProcessHelper();
			KProcessor processor = processHelper.processor;
			if (processor == null) {
				if ( ((ILaunchExecutors)launch).executorServiceManager!=null
						&& ((ILaunchExecutors)launch).executorServiceManager.getDefaultExecutor()!=null) {
					processor = new KProcessor(((ILaunchExecutors)launch).executorServiceManager.getDefaultExecutor());
				} else {
					processor = new KProcessor(launch.getExecutorService());
				}
				((IKProcessorLaunch)processor).launch = launch;
				phl.put(processor, launch);
			}
			processor.execute(processHelper.process);
			launch.getProcessHelper().join();
			this.observable.finalizeLaunching(launch);
			launch.complete();
			if(launch.executorServiceManager!=null) {
				launch.executorServiceManager.clean();
			}
			phl.remove(processor);

			if (launch.getLaunchResult().getStatusCode()!=ProgramReturnCodes.FAILURE
					&&launch.getLaunchResult().getStatusCode()!=ProgramReturnCodes.ABORTED) {
				launch.getLaunchResult().setStatusCode(ProgramReturnCodes.SUCCESS);
			}
			LaunchingMessageKind.ILAUNCH0003.format(launch.getJobInstance()
					.getClass().getCanonicalName(), launch.getLaunchId(), launch
					.getLaunchResult().getStatusCode(), launch.getLaunchResult()
					.getReturnCode());
			return launch;
		}

		private void initializeExecutors(LaunchImpl<?> launch) {
			ParalleljConfiguration configuration = (ParalleljConfiguration) ConfigurationService
					.getConfigurationService().getConfigurationManager()
					.get(ParalleljConfigurationManager.class)
					.getConfiguration();
			CExecutors cExecutors = configuration != null ? configuration
					.getExecutorServices() : null;
			if (cExecutors != null) {
				launch.executorServiceManager = new ExecutorServiceManager(cExecutors);
			}
		}
	}

    void around(KProcessor kProcessor, SubProcessRunnable runnable):execution(public void submit(..))
                && args(runnable)
                &&this(kProcessor){
		LaunchImpl<?> launch = phl.get(kProcessor);
		KProcess process = runnable.getSubProcessCall().getProcess();
		if (launch!=null && launch.executorServiceManager!=null) {
			ExecutorService service = launch.executorServiceManager.get(process);
			if (service!=null) {
				kProcessor.submit(runnable, service);
			} else {
				proceed(kProcessor, runnable);
			}
		} else {
			proceed(kProcessor, runnable);
		}
    }

    void around(KProcessor kProcessor, CallableCallRunnable runnable):execution(public void submit(..))
            && args(runnable)
            &&this(kProcessor){
		LaunchImpl<?> launch = phl.get(kProcessor);
		KProcess process = runnable.getCallableCall().getProcess();
		if (launch!=null && launch.executorServiceManager!=null) {
			ExecutorService service = launch.executorServiceManager.get(process);
			if (service!=null) {
				kProcessor.submit(runnable, service);
			} else {
				proceed(kProcessor, runnable);
			}
		} else {
			proceed(kProcessor, runnable);
		}
    }

    void around(KProcessor kProcessor, RunnableCallRunnable runnable):execution(public void submit(..))
            && args(runnable)
            &&this(kProcessor) {
		LaunchImpl<?> launch = phl.get(kProcessor);
		KProcess process = runnable.getRunnableCall().getProcess();
		if (launch!=null && launch.executorServiceManager!=null) {
			ExecutorService service = launch.executorServiceManager.get(process);
			if (service!=null) {
				kProcessor.submit(runnable, service);
			} else {
				proceed(kProcessor, runnable);
			}
		} else {
			proceed(kProcessor, runnable);
		}
    }

    void around(KProcess kProcess):execution(private void complete(..))
    	&& this(kProcess) {
    	proceed(kProcess);
		LaunchImpl<?> launch = ((IKProcessorLaunch)kProcess.getProcessor()).launch;
		if(launch.executorServiceManager!=null) {
			launch.executorServiceManager.complete(kProcess);
		}
    }

 }

/*
 *     ParallelJ, framework for parallel computing
 *
 *     Copyright (C) 2010, 2011, 2012 Atos Worldline or third-party contributors as
 *     indicated by the @author tags or express copyright attribution
 *     statements applied by the authors.
 *
 *     This library is free software; you can redistribute it and/or
 *     modify it under the terms of the GNU Lesser General Public
 *     License as published by the Free Software Foundation; either
 *     version 2.1 of the License.
 *
 *     This library is distributed in the hope that it will be useful,
 *     but WITHOUT ANY WARRANTY; without even the implied warranty of
 *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 *     Lesser General Public License for more details.
 *
 *     You should have received a copy of the GNU Lesser General Public
 *     License along with this library; if not, write to the Free Software
 *     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 */
package org.parallelj.launching.internal;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;

import org.parallelj.Programs;
import org.parallelj.internal.conf.ConfigurationService;
import org.parallelj.internal.conf.ParalleljConfigurationManager;
import org.parallelj.internal.conf.pojos.CExecutors;
import org.parallelj.internal.conf.pojos.ParalleljConfiguration;
import org.parallelj.internal.kernel.KProcess;
import org.parallelj.internal.kernel.KProcessor;
import org.parallelj.internal.kernel.procedure.CallableProcedure.CallableCall.CallableCallRunnable;
import org.parallelj.internal.kernel.procedure.RunnableProcedure.RunnableCall.RunnableCallRunnable;
import org.parallelj.internal.kernel.procedure.SubProcessProcedure.SubProcessCall.SubProcessRunnable;
import org.parallelj.internal.reflect.ProcessHelperImpl;
import org.parallelj.launching.Launch;
import org.parallelj.launching.LaunchException;
import org.parallelj.launching.LaunchingMessageKind;
import org.parallelj.launching.ProgramReturnCodes;
import org.parallelj.launching.internal.LaunchManagement.LaunchAdapter.IKProcessorLaunch;
import org.parallelj.mirror.ParallelJThreadFactory;

import org.parallelj.launching.executors.ExecutorServiceManager;

/**
 * Implements the synchLaunch(..) and aSynchLaunch(..) methods of Launch.
 *
 *
 */
privileged aspect LaunchManagement {

	private static Map<KProcessor, LaunchImpl<?>> phl = new ConcurrentHashMap<>();

	static privileged public aspect LaunchAdapter percflow(launchInstance() ) {

		declare precedence :
			org.parallelj.internal.kernel.Identifiers,
			org.parallelj.internal.reflect.ProgramAdapter,
			org.parallelj.internal.util.sm.impl.KStateMachines,
			org.parallelj.internal.util.sm.impl.KStateMachines.PerMachine,
			org.parallelj.Executables$PerExecutable,
			org.parallelj.internal.reflect.ProgramAdapter.PerProgram,
			org.parallelj.internal.log.Logs;

		pointcut launchInstance() : execution(* Launch+.*(..));

		interface IKProcessorLaunch {}
		LaunchImpl<?> IKProcessorLaunch.launch;
		declare parents: KProcessor implements IKProcessorLaunch;

		interface ILaunchExecutors {}
		ExecutorServiceManager ILaunchExecutors.executorServiceManager;
		declare parents: LaunchImpl implements ILaunchExecutors;

		private LaunchingObservable observable = new LaunchingObservable();

		boolean stopExecutorServiceAfterExecution = false;

		private static class AsyncThread implements Runnable {

			private LaunchAdapter launchAdapter;
			private LaunchImpl<?> launch;

			public AsyncThread(LaunchImpl<?> launch, LaunchAdapter launchAdapter) {
				this.launch = launch;
				this.launchAdapter = launchAdapter;
			}

			@Override
			public void run() {
				this.launchAdapter.internalSyncLaunch(this.launch);
			}
		}

		@SuppressWarnings({ "rawtypes", "unchecked" })
		Launch around(LaunchImpl launch) throws LaunchException:
					execution(public Launch Launch+.synchLaunch(..) throws LaunchException)
						&& this(launch) {
			proceed(launch);
			launch.processHelper = Programs.as(launch.jobInstance);
			launch.getLaunchResult().setStatusCode(ProgramReturnCodes.RUNNING);
			this.observable.prepareLaunching(launch);

			return internalSyncLaunch(launch);
		}

		@SuppressWarnings({ "rawtypes", "unchecked" })
		Launch around(LaunchImpl launch) throws LaunchException:
					execution(public Launch Launch+.aSynchLaunch() throws LaunchException)
						&& this(launch) {
			proceed(launch);
			launch.processHelper = Programs.as(launch.jobInstance);
			launch.getLaunchResult().setStatusCode(ProgramReturnCodes.RUNNING);
			this.observable.prepareLaunching(launch);
			if (launch.getExecutorService()!=null) {
				launch.getExecutorService().execute(new AsyncThread(launch, this));
			} else {
				ParallelJThreadFactory.getInstance().newThread(new AsyncThread(launch, this)).start();
	 		}
			return launch;
		}

		@SuppressWarnings("rawtypes")
		private Launch<?> internalSyncLaunch(LaunchImpl<?> launch) {
			LaunchingMessageKind.ILAUNCH0002.format(launch.getJobInstance()
					.getClass().getCanonicalName(), launch.getLaunchId());
			initializeExecutors(launch);
			ProcessHelperImpl processHelper = (ProcessHelperImpl)launch.getProcessHelper();
			KProcessor processor = processHelper.processor;
			if (processor == null) {
				if ( ((ILaunchExecutors)launch).executorServiceManager!=null
						&& ((ILaunchExecutors)launch).executorServiceManager.getDefaultExecutor()!=null) {
					processor = new KProcessor(((ILaunchExecutors)launch).executorServiceManager.getDefaultExecutor());
				} else {
					processor = new KProcessor(launch.getExecutorService());
				}
				((IKProcessorLaunch)processor).launch = launch;
				phl.put(processor, launch);
			}
			processor.execute(processHelper.process);
			launch.getProcessHelper().join();
			this.observable.finalizeLaunching(launch);
			launch.complete();
			if(launch.executorServiceManager!=null) {
				launch.executorServiceManager.clean();
			}
			phl.remove(processor);

			if (launch.getLaunchResult().getStatusCode()!=ProgramReturnCodes.FAILURE
					&&launch.getLaunchResult().getStatusCode()!=ProgramReturnCodes.ABORTED) {
				launch.getLaunchResult().setStatusCode(ProgramReturnCodes.SUCCESS);
			}
			LaunchingMessageKind.ILAUNCH0003.format(launch.getJobInstance()
					.getClass().getCanonicalName(), launch.getLaunchId(), launch
					.getLaunchResult().getStatusCode(), launch.getLaunchResult()
					.getReturnCode());
			return launch;
		}

		private void initializeExecutors(LaunchImpl<?> launch) {
			ParalleljConfiguration configuration = (ParalleljConfiguration) ConfigurationService
					.getConfigurationService().getConfigurationManager()
					.get(ParalleljConfigurationManager.class)
					.getConfiguration();
			CExecutors cExecutors = configuration != null ? configuration
					.getExecutorServices() : null;
			if (cExecutors != null) {
				launch.executorServiceManager = new ExecutorServiceManager(cExecutors);
			}
		}
	}

    void around(KProcessor kProcessor, SubProcessRunnable runnable):execution(public void submit(..))
                && args(runnable)
                &&this(kProcessor){
		LaunchImpl<?> launch = phl.get(kProcessor);
		KProcess process = runnable.getSubProcessCall().getProcess();
		if (launch!=null && launch.executorServiceManager!=null) {
			ExecutorService service = launch.executorServiceManager.get(process);
			if (service!=null) {
				kProcessor.submit(runnable, service);
			} else {
				proceed(kProcessor, runnable);
			}
		} else {
			proceed(kProcessor, runnable);
		}
    }

    void around(KProcessor kProcessor, CallableCallRunnable runnable):execution(public void submit(..))
            && args(runnable)
            &&this(kProcessor){
		LaunchImpl<?> launch = phl.get(kProcessor);
		KProcess process = runnable.getCallableCall().getProcess();
		if (launch!=null && launch.executorServiceManager!=null) {
			ExecutorService service = launch.executorServiceManager.get(process);
			if (service!=null) {
				kProcessor.submit(runnable, service);
			} else {
				proceed(kProcessor, runnable);
			}
		} else {
			proceed(kProcessor, runnable);
		}
    }

    void around(KProcessor kProcessor, RunnableCallRunnable runnable):execution(public void submit(..))
            && args(runnable)
            &&this(kProcessor) {
		LaunchImpl<?> launch = phl.get(kProcessor);
		KProcess process = runnable.getRunnableCall().getProcess();
		if (launch!=null && launch.executorServiceManager!=null) {
			ExecutorService service = launch.executorServiceManager.get(process);
			if (service!=null) {
				kProcessor.submit(runnable, service);
			} else {
				proceed(kProcessor, runnable);
			}
		} else {
			proceed(kProcessor, runnable);
		}
    }

    void around(KProcess kProcess):execution(private void complete(..))
    	&& this(kProcess) {
    	proceed(kProcess);
		LaunchImpl<?> launch = ((IKProcessorLaunch)kProcess.getProcessor()).launch;
		launch.executorServiceManager.complete(kProcess);
    }

 }


Aspect J tryouts

Aspect J tryouts

import java.util.*;

public aspect MethodInterceptor {
      Stack<String> st = new Stack<String>();

	//pointcut traced() : !within(MethodInterceptor) && execution(public * *.*(..)) ;
	pointcut traced() : !within(MethodInterceptor) && execution( * *.*(..));

private String previousClass;
private String currentClass;
private int callDepth=1;
private String activate;
private String deactivate;

	before() : traced() {

        String prefix=null;
			if(callDepth==1){
        prefix="NEW_CALL:";

		}
		else{
		prefix="NESTED_CALL:";

		}
		Object o = thisJoinPoint.getThis();
        String currentClassName=null;
        if(o!=null){
        currentClassName=o.getClass().getName();
        }
        else{
      String signature=  thisJoinPointStaticPart.getSignature().toString();

      String array[]=signature.split("\\s");
         String methodName=array[1];
        System.out.println(signature);
         if(methodName.contains("."))
         currentClassName=methodName.split("\\.")[0];
        System.out.println(currentClassName);

        }
        String sender=null;

        if(previousClass==null){
        previousClass=currentClassName;
        sender=currentClassName;
	    }
       else  if(previousClass.equals(currentClassName)){
        sender=currentClassName;

        }
        else{
        sender=previousClass;
        }

      String call=  thisJoinPointStaticPart.getSignature().toString();

      String array[]=call.split("\\s");
      String methodName=array[1];
      String returnType=array[0];

      if(methodName.contains(".")){
      methodName=methodName.split("\\.")[1];
      }
      if(st.size()>0){

      sender=st.peek();
      }

      String methodCall=methodName+":"+returnType;

//System.out.println(callDepth+"::::"+prefix+":::: "+sender+"------>" + methodCall+"---->"+currentClassName);
System.out.println(sender+"->"+currentClassName+":"+methodCall);

if(currentClassName !=null && !currentClassName.equals(activate)){

                System.out.println("activate "+currentClassName);
                activate=currentClassName;

}
currentClass=currentClassName;

       st.push(currentClass);

previousClass=currentClass;
		callDepth++;

	}

	after() : traced() {
		callDepth--;
if(currentClass !=null){
if(currentClass.equals(activate)){
activate=null;
}
                System.out.println("deactivate "+currentClass);
                deactivate=currentClass;

}

      st.pop();

	}

	private void print(String prefix, Object message) {

		if(callDepth==1){

					System.out.print("NEW_CALL:   "+callDepth+":"+message);

		}
		else{
				System.out.print("NESTED_CALL"+callDepth+message);

		}

		//System.out.println(prefix + ": " + message);
	}
}

import java.util.*;

public aspect MethodInterceptor {
      Stack<String> st = new Stack<String>();

	//pointcut traced() : !within(MethodInterceptor) && execution(public * *.*(..)) ;
	pointcut traced() : !within(MethodInterceptor) && execution( * *.*(..));

private String previousClass;
private String currentClass;
private int callDepth=1;

	before() : traced() {

        String prefix=null;
			if(callDepth==1){
        prefix="NEW_CALL:";

		}
		else{
		prefix="NESTED_CALL:";

		}
		Object o = thisJoinPoint.getThis();
        String currentClassName=null;
        if(o!=null){
        currentClassName=o.getClass().getName();
        }
        else{
      String signature=  thisJoinPointStaticPart.getSignature().toString();

      String array[]=signature.split("\\s");
         String methodName=array[1];
        System.out.println(signature);
         if(methodName.contains("."))
         currentClassName=methodName.split("\\.")[0];
        System.out.println(currentClassName);

        }
        String sender=null;

        if(previousClass==null){
        previousClass=currentClassName;
        sender=currentClassName;
	    }
       else  if(previousClass.equals(currentClassName)){
        sender=currentClassName;

        }
        else{
        sender=previousClass;
        }

      String call=  thisJoinPointStaticPart.getSignature().toString();

      String array[]=call.split("\\s");
      String methodName=array[1];
      String returnType=array[0];

      if(methodName.contains(".")){
      methodName=methodName.split("\\.")[1];
      }
      if(st.size()>0){

      sender=st.peek();
      }

      String methodCall=methodName+":"+returnType;

//System.out.println(callDepth+"::::"+prefix+":::: "+sender+"------>" + methodCall+"---->"+currentClassName);
System.out.println(sender+"->"+currentClassName+":"+methodCall);

currentClass=currentClassName;

       st.push(currentClass);

previousClass=currentClass;
		callDepth++;

	}

	after() : traced() {
		callDepth--;

      st.pop();

	}

	private void print(String prefix, Object message) {

		if(callDepth==1){

					System.out.print("NEW_CALL:   "+callDepth+":"+message);

		}
		else{
				System.out.print("NESTED_CALL"+callDepth+message);

		}

		//System.out.println(prefix + ": " + message);
	}
}


check filters before monitoring

check filters before monitoring

/*
 * 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 akka.japi.Creator;
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> anonymousActorClassName = Option.empty();
    // we sample based on actor type (i.e. a sampling rate of 'n' for a filter means 'every nth actor with type k, for each type k that matches that filter')
    private final ConcurrentHashMap<Option<String>, AtomicLong> samplingCounters  = new ConcurrentHashMap<Option<String>, AtomicLong>();
    // we count actors by actor type (any 'anonymous' or 'generic' actors are treated as the same type)
    private final ConcurrentHashMap<Option<String>, AtomicInteger> numberOfActors = new ConcurrentHashMap<Option<String>, AtomicInteger>();
    private final ConcurrentHashMap<ActorPath, String> pathTags                   = new ConcurrentHashMap<ActorPath, String>();

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

    /**
     * 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 Option<String> className = getActorClassName(actorCell, actorPath);
        final PathAndClass pathAndClass = new PathAndClass(actorPath, className);
        if (!includeActorPath(pathAndClass) || !sampleMessage(pathAndClass)) return proceed(actorCell, msg);

        int samplingRate = getSampleRate(pathAndClass);

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

        // record the queue size
        this.counterInterface.recordGaugeValue(Aspects.queueSize(), actorCell.numberOfMessages(), tags);
        // record the message, general and specific
        this.counterInterface.incrementCounter(Aspects.delivered(), samplingRate, tags);
        this.counterInterface.incrementCounter(Aspects.delivered(msg), 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(Aspects.actorDuration(), (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(), getActorClassName(actorCell, actorCell.self().path()));
        this.counterInterface.incrementCounter(Aspects.actorError(), tags);
        this.counterInterface.incrementCounter(Aspects.actorError(failure), 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(), this.anonymousActorClassName);
            this.counterInterface.incrementCounter(Aspects.undelivered(), tags);
            this.counterInterface.incrementCounter(Aspects.undelivered(unhandledMessage.getMessage()), 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) {
        recordActorCount(actor.path(), props, CountType.Increment);
    }

    /**
     * Advises the {@code LocalActorRef.stop} method
     *
     * @param actorCell the {@code ActorCell} of the actor being stopped
     */
    after(ActorCell actorCell) : Pointcuts.actorCellInternalStop(actorCell) {
        recordActorCount(actorCell.self().path(), actorCell.props(), CountType.Decrement);
    }

    /**
     * Records the actor count increment or decrement
     *
     * @param path the ActorPath being created or destroyed
     * @param props the Props of the actor at the {@code path}
     * @param countType the increment or decrement
     */
     private void recordActorCount(ActorPath path, Props props, CountType countType) {
         final Option<String> className = getActorClassName(props, path);
         final PathAndClass pac = new PathAndClass(path, getActorClassName(props, path));
         if (!includeActorPath(pac)) return;

         final String[] tags = getTags(path, className);
         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;
         switch (countType) {
             case Increment:
                 currentNumberOfActors = this.numberOfActors.get(className).incrementAndGet();
                 break;
             case Decrement:
                 currentNumberOfActors = this.numberOfActors.get(className).decrementAndGet();
                 break;
             default:
                 currentNumberOfActors = 0;
                 break;
         }

         this.counterInterface.recordGaugeValue(Aspects.actorCount(), currentNumberOfActors, tags);
     }

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

    /**
     * The count type for exhaustive matching
     */
    private static enum CountType {
        Increment, Decrement
    }

    /**
     * Decide whether to include this ActorCell in our measurements
     *
     * @param pathAndClass the PAC container
     * @return whether to include the given actor in the metrics
     */
    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;
        }

        // include actor if it's in 'included' list
        if (this.agentConfiguration.included().accept(pathAndClass)) return true;
        // exclude actor if excluded (i.e. is in 'excluded' list, or excludeAllNotIncluded = true, and actor is not included)
        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);
    }

    /**
     * Lookup the sample rate for an actor from the configuration
     *
     * @param pathAndClass the PAC container
     * @return the sampling rate as non-negative integer
     */
    private int getSampleRate(final PathAndClass pathAndClass) {
        return this.agentConfiguration.sampling().getRate(pathAndClass);
    }

    /**
     * Decide whether to sample an actor on a particular occasion
     *
     * @param pathAndClass the PAC container
     * @return {@code true} if we should sample this actor
     */
    private boolean sampleMessage(final PathAndClass pathAndClass) {
        final Option<String> className = pathAndClass.actorClassName();
        int sampleRate = getSampleRate(pathAndClass);
        if (sampleRate == 1) return true;

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

    /**
     * Formats the given {@code path} in a way that can be used for tagging. For example, for
     * path akka://default/user/a/b/c, it returns akka.path:/default/user/a/b/c
     *
     * @param path the path
     * @return the tag-ready path
     */
    private String actorPathToString(ActorPath path) {
        StringBuilder sb = new StringBuilder();
        sb.append("akka.path:/");
        sb.append(path.address().system());
        for (String element : path.getElements()) {
            sb.append("/");
            sb.append(element);
        }
        return sb.toString();
    }

    /**
     * Decides whether {@code path} represents the "root" of the user actors
     *
     * @param path the actor path
     * @return {@code true} if root of user actors
     */
    private boolean isUserRoot(ActorPath path) {
        return "user".equals(path.elements().mkString("/"));
    }

    /**
     * Computes the tags for the given {@code actorPath} and {@code actor} instances.
     *
     * @param actorPath the actor path; never {@code null}
     * @param actorClassName the actor instance; may be {@code null}
     * @return non-{@code null} array of tags
     */
    private String[] getTags(final ActorPath actorPath, final Option<String> actorClassName) {
        List<String> tags = new ArrayList<String>();

        // TODO: Improve detection of routed actors. This only detects "root" unnamed actors
        String lastPathElement = actorPath.elements().last();
        if (lastPathElement.startsWith("$")) {
            // this is routed actor.
            final ActorPath parent = actorPath.parent();
            if (isUserRoot(parent)) {
                // the parent is akka://xxx/user
                tags.add(actorPathToString(actorPath));
            } else {
                // the parent is some other actor, akka://xxx/user/foo
                tags.add(actorPathToString(parent));
                if (this.agentConfiguration.includeRoutees()) tags.add(actorPathToString(actorPath));
            }
        } else {
            // there is no supervisor
            tags.add(actorPathToString(actorPath));
        }
        if (actorClassName.isDefined()) {
            tags.add(String.format("akka.type:%s.%s", actorPath.address().system(), actorClassName.get()));
        }

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

    /**
     * returns the canonical name of the actor type associated with an ActorCell
     *
     * @param actorCell the actor cell to get the name for
     * @return the Option of actor class, never {@code null}.
     */
    private Option<String> getActorClassName(final ActorCell actorCell, final ActorPath actorPath) {
        return getActorClassName(actorCell.props(), actorPath);
    }

    /**
     * Returns the canonical name of the actor type associated with a Props instance
     *
     * Returns null if props.actorClass does not have a canonical name (i.e., if
     * it is a local or anonymous class)
     *
     * Notably, when the actor class is anonymous, the {@code getCanonicalName} returns {@code null},
     * which we deal with here.
     *
     * @param props the ActorRef {@code Props} instance
     * @return the Option of actor class, never {@code null}.
     */
    private Option<String> getActorClassName(final Props props, final ActorPath actorPath) {
        final String canonicalName = props.actorClass().getCanonicalName();
        if (canonicalName == null /*if actor is anonymous*/||/*OR actor is generic*/
                canonicalName.endsWith("akka.actor.Actor")) {
            // then we check to see if our path->className map can help...
            if (this.pathTags.containsKey(actorPath)) return Option.apply(this.pathTags.get(actorPath));
            return this.anonymousActorClassName;
        }
        return Option.apply(canonicalName);
    }

    /**
     * Catches actors created by the japi.Creator.create(), at a point when their type is visible to the runtime.
     * We use the visibility of the type at this one pointcut to create a map from the actor path to its type,
     * which we then access within the other pointcuts (notably on actor death and message receipt) to get the
     * 'real' type at those points.
     *
     * Since the 'ActorRefFactory.actorOf' method is guaranteed to be called on the creation of any actors after the
     * initialisation of the actorSystem itself, and since we consider anonymous/untyped actors in our count, we also
     * decrement the count of 'anonymous' actors here, for we know their type to be invisible to other pointcuts, and
     * that the element mapping path to type will not have been created (i.e. we generally assume actor paths are unique,
     * and that the actorOf advice will access the 'pathTags' map before we've inserted this new value). This approach
     * may not be robust enough -- in particular, with regards the second assumption.
     *
     * Regardless, we still double check that the map element doesn't already exist. If it does, we make the assumption
     * that it was created 'a while ago', and that the path referred to an actor of the same type.
     *
     * @param actor the Actor returned by the {@code Creator.create()} method
     * */
    after() returning(final Actor actor) : Pointcuts.actorCreator() {
        final String className = actor.getClass().getCanonicalName();
        final ActorPath actorPath = actor.self().path();
        final PathAndClass pac = new PathAndClass(actorPath, Option.apply(className));
        if (this.pathTags.containsKey(actorPath)) return; // the key is there, we need do nothing.

        if (includeActorPath(pac)) {
            // add the path -> type pair to the pathTags map
            this.pathTags.putIfAbsent(actorPath, className);

            // safe increment of the count of actors of this type
            this.numberOfActors.putIfAbsent(pac.actorClassName(), new AtomicInteger(0));
            final int currentNumberOfActors = this.numberOfActors.get(pac.actorClassName()).incrementAndGet();
            this.counterInterface.recordGaugeValue(Aspects.actorCount(), currentNumberOfActors, getTags(actorPath, pac.actorClassName()));
        }

        if (includeActorPath(new PathAndClass(actorPath, this.anonymousActorClassName))) {
            // safe decrement of the count of anonymous/untyped actors
            this.numberOfActors.putIfAbsent(this.anonymousActorClassName, new AtomicInteger(0));
            final int currentNumberOfActors = this.numberOfActors.get(this.anonymousActorClassName).decrementAndGet();
            this.counterInterface.recordGaugeValue(Aspects.actorCount(), currentNumberOfActors, getTags(actorPath, this.anonymousActorClassName));
        }
    }

}

/*
 * 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 akka.japi.Creator;
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> anonymousActorClassName = Option.empty();
    // we sample based on actor type (i.e. a sampling rate of 'n' for a filter means 'every nth actor with type k, for each type k that matches that filter')
    private final ConcurrentHashMap<Option<String>, AtomicLong> samplingCounters  = new ConcurrentHashMap<Option<String>, AtomicLong>();
    // we count actors by actor type (any 'anonymous' or 'generic' actors are treated as the same type)
    private final ConcurrentHashMap<Option<String>, AtomicInteger> numberOfActors = new ConcurrentHashMap<Option<String>, AtomicInteger>();
    private final ConcurrentHashMap<ActorPath, String> pathTags                   = new ConcurrentHashMap<ActorPath, String>();

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

    /**
     * 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 Option<String> className = getActorClassName(actorCell, actorPath);
        final PathAndClass pathAndClass = new PathAndClass(actorPath, className);
        if (!includeActorPath(pathAndClass) || !sampleMessage(pathAndClass)) return proceed(actorCell, msg);

        int samplingRate = getSampleRate(pathAndClass);

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

        // record the queue size
        this.counterInterface.recordGaugeValue(Aspects.queueSize(), actorCell.numberOfMessages(), tags);
        // record the message, general and specific
        this.counterInterface.incrementCounter(Aspects.delivered(), samplingRate, tags);
        this.counterInterface.incrementCounter(Aspects.delivered(msg), 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(Aspects.actorDuration(), (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(), getActorClassName(actorCell, actorCell.self().path()));
        this.counterInterface.incrementCounter(Aspects.actorError(), tags);
        this.counterInterface.incrementCounter(Aspects.actorError(failure), 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(), this.anonymousActorClassName);
            this.counterInterface.incrementCounter(Aspects.undelivered(), tags);
            this.counterInterface.incrementCounter(Aspects.undelivered(unhandledMessage.getMessage()), 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) {
        recordActorCount(actor.path(), props, CountType.Increment);
    }

    /**
     * Advises the {@code LocalActorRef.stop} method
     *
     * @param actorCell the {@code ActorCell} of the actor being stopped
     */
    after(ActorCell actorCell) : Pointcuts.actorCellInternalStop(actorCell) {
        recordActorCount(actorCell.self().path(), actorCell.props(), CountType.Decrement);
    }

    /**
     * Records the actor count increment or decrement
     *
     * @param path the ActorPath being created or destroyed
     * @param props the Props of the actor at the {@code path}
     * @param countType the increment or decrement
     */
     private void recordActorCount(ActorPath path, Props props, CountType countType) {
         final Option<String> className = getActorClassName(props, path);
         final PathAndClass pac = new PathAndClass(path, getActorClassName(props, path));
         if (!includeActorPath(pac)) return;

         final String[] tags = getTags(path, className);
         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;
         switch (countType) {
             case Increment:
                 currentNumberOfActors = this.numberOfActors.get(className).incrementAndGet();
                 break;
             case Decrement:
                 currentNumberOfActors = this.numberOfActors.get(className).decrementAndGet();
                 break;
             default:
                 currentNumberOfActors = 0;
                 break;
         }

         this.counterInterface.recordGaugeValue(Aspects.actorCount(), currentNumberOfActors, tags);
     }

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

    /**
     * The count type for exhaustive matching
     */
    private static enum CountType {
        Increment, Decrement
    }

    /**
     * Decide whether to include this ActorCell in our measurements
     *
     * @param pathAndClass the PAC container
     * @return whether to include the given actor in the metrics
     */
    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;
        }

        // include actor if it's in 'included' list
        if (this.agentConfiguration.included().accept(pathAndClass)) return true;
        // exclude actor if excluded (i.e. is in 'excluded' list, or excludeAllNotIncluded = true, and actor is not included)
        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);
    }

    /**
     * Lookup the sample rate for an actor from the configuration
     *
     * @param pathAndClass the PAC container
     * @return the sampling rate as non-negative integer
     */
    private int getSampleRate(final PathAndClass pathAndClass) {
        return this.agentConfiguration.sampling().getRate(pathAndClass);
    }

    /**
     * Decide whether to sample an actor on a particular occasion
     *
     * @param pathAndClass the PAC container
     * @return {@code true} if we should sample this actor
     */
    private boolean sampleMessage(final PathAndClass pathAndClass) {
        final Option<String> className = pathAndClass.actorClassName();
        int sampleRate = getSampleRate(pathAndClass);
        if (sampleRate == 1) return true;

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

    /**
     * Formats the given {@code path} in a way that can be used for tagging. For example, for
     * path akka://default/user/a/b/c, it returns akka.path:/default/user/a/b/c
     *
     * @param path the path
     * @return the tag-ready path
     */
    private String actorPathToString(ActorPath path) {
        StringBuilder sb = new StringBuilder();
        sb.append("akka.path:/");
        sb.append(path.address().system());
        for (String element : path.getElements()) {
            sb.append("/");
            sb.append(element);
        }
        return sb.toString();
    }

    /**
     * Decides whether {@code path} represents the "root" of the user actors
     *
     * @param path the actor path
     * @return {@code true} if root of user actors
     */
    private boolean isUserRoot(ActorPath path) {
        return "user".equals(path.elements().mkString("/"));
    }

    /**
     * Computes the tags for the given {@code actorPath} and {@code actor} instances.
     *
     * @param actorPath the actor path; never {@code null}
     * @param actorClassName the actor instance; may be {@code null}
     * @return non-{@code null} array of tags
     */
    private String[] getTags(final ActorPath actorPath, final Option<String> actorClassName) {
        List<String> tags = new ArrayList<String>();

        // TODO: Improve detection of routed actors. This only detects "root" unnamed actors
        String lastPathElement = actorPath.elements().last();
        if (lastPathElement.startsWith("$")) {
            // this is routed actor.
            final ActorPath parent = actorPath.parent();
            if (isUserRoot(parent)) {
                // the parent is akka://xxx/user
                tags.add(actorPathToString(actorPath));
            } else {
                // the parent is some other actor, akka://xxx/user/foo
                tags.add(actorPathToString(parent));
                if (this.agentConfiguration.includeRoutees()) tags.add(actorPathToString(actorPath));
            }
        } else {
            // there is no supervisor
            tags.add(actorPathToString(actorPath));
        }
        if (actorClassName.isDefined()) {
            tags.add(String.format("akka.type:%s.%s", actorPath.address().system(), actorClassName.get()));
        }

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

    /**
     * returns the canonical name of the actor type associated with an ActorCell
     *
     * @param actorCell the actor cell to get the name for
     * @return the Option of actor class, never {@code null}.
     */
    private Option<String> getActorClassName(final ActorCell actorCell, final ActorPath actorPath) {
        return getActorClassName(actorCell.props(), actorPath);
    }

    /**
     * Returns the canonical name of the actor type associated with a Props instance
     *
     * Returns null if props.actorClass does not have a canonical name (i.e., if
     * it is a local or anonymous class)
     *
     * Notably, when the actor class is anonymous, the {@code getCanonicalName} returns {@code null},
     * which we deal with here.
     *
     * @param props the ActorRef {@code Props} instance
     * @return the Option of actor class, never {@code null}.
     */
    private Option<String> getActorClassName(final Props props, final ActorPath actorPath) {
        final String canonicalName = props.actorClass().getCanonicalName();
        if (canonicalName == null /*if actor is anonymous*/||/*OR actor is generic*/
                canonicalName.endsWith("akka.actor.Actor")) {
            // then we check to see if our path->className map can help...
            if (this.pathTags.containsKey(actorPath)) return Option.apply(this.pathTags.get(actorPath));
            return this.anonymousActorClassName;
        }
        return Option.apply(canonicalName);
    }

    /**
     * Catches actors created by the japi.Creator.create(), at a point when their type is visible to the runtime.
     * We use the visibility of the type at this one pointcut to create a map from the actor path to its type,
     * which we then access within the other pointcuts (notably on actor death and message receipt) to get the
     * 'real' type at those points.
     *
     * Since the 'ActorRefFactory.actorOf' method is guaranteed to be called on the creation of any actors after the
     * initialisation of the actorSystem itself, and since we consider anonymous/untyped actors in our count, we also
     * decrement the count of 'anonymous' actors here, for we know their type to be invisible to other pointcuts, and
     * that the element mapping path to type will not have been created (i.e. we generally assume actor paths are unique,
     * and that the actorOf advice will access the 'pathTags' map before we've inserted this new value). This approach
     * may not be robust enough -- in particular, with regards the second assumption.
     *
     * Regardless, we still double check that the map element doesn't already exist. If it does, we make the assumption
     * that it was created 'a while ago', and that the path referred to an actor of the same type.
     *
     * @param actor the Actor returned by the {@code Creator.create()} method
     * */
    after() returning(final Actor actor) : Pointcuts.actorCreator() {
        final String className = actor.getClass().getCanonicalName();
        final ActorPath actorPath = actor.self().path();
        if (this.pathTags.containsKey(actorPath)) return;
        // add the path -> type pair to the pathTags map
        this.pathTags.putIfAbsent(actorPath, className);

        // safe increment of the count of actors of this type
        this.numberOfActors.putIfAbsent(Option.apply(className), new AtomicInteger(0));
        int currentNumberOfActors = this.numberOfActors.get(Option.apply(className)).incrementAndGet();
        this.counterInterface.recordGaugeValue(Aspects.actorCount(), currentNumberOfActors, getTags(actorPath, Option.apply(className)));

        // safe decrement of the count of anonymous/untyped actors
        this.numberOfActors.putIfAbsent(this.anonymousActorClassName, new AtomicInteger(0));
                // reuse of int for (almost-?)invisible performance improvement
        currentNumberOfActors = this.numberOfActors.get(this.anonymousActorClassName).decrementAndGet();
        this.counterInterface.recordGaugeValue(Aspects.actorCount(), currentNumberOfActors, getTags(actorPath, this.anonymousActorClassName));
    }

}


Fix merge error in previous commit

Fix merge error in previous commit

Issue: SPR-11385

/*
 * Copyright 2002-2014 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.mock.staticmock;

import java.util.Arrays;
import java.util.LinkedList;

import org.springframework.util.ObjectUtils;

/**
 * Abstract aspect to enable mocking of methods picked out by a pointcut.
 *
 *
Sub-aspects must define:
 *

 *
the {@link #mockStaticsTestMethod()} pointcut to indicate call stacks
 * when mocking should be triggered
 *
the {@link #methodToMock()} pointcut to pick out method invocations to mock
 *

 *
 * @author Rod Johnson
 * @author Ramnivas Laddad
 * @author Sam Brannen
 */
public abstract aspect AbstractMethodMockingControl percflow(mockStaticsTestMethod()) {

	protected abstract pointcut mockStaticsTestMethod();

	protected abstract pointcut methodToMock();

	private boolean recording = true;

	static enum CallResponse {
		nothing, return_, throw_
	};

	/**
	 * Represents a list of expected calls to methods.
	 */
	// Public to allow inserted code to access: is this normal??
	public class Expectations {

		/**
		 * Represents an expected call to a method.
		 */
		private class Call {

			private final String signature;
			private final Object[] args;

			private Object responseObject; // return value or throwable
			private CallResponse responseType = CallResponse.nothing;

			public Call(String signature, Object[] args) {
				this.signature = signature;
				this.args = args;
			}

			public boolean hasResponseSpecified() {
				return responseType != CallResponse.nothing;
			}

			public void setReturnVal(Object retVal) {
				this.responseObject = retVal;
				responseType = CallResponse.return_;
			}

			public void setThrow(Throwable throwable) {
				this.responseObject = throwable;
				responseType = CallResponse.throw_;
			}

			public Object returnValue(String lastSig, Object[] args) {
				checkSignature(lastSig, args);
				return responseObject;
			}

			public Object throwException(String lastSig, Object[] args) {
				checkSignature(lastSig, args);
				throw (RuntimeException) responseObject;
			}

			private void checkSignature(String lastSig, Object[] args) {
				if (!signature.equals(lastSig)) {
					throw new IllegalArgumentException("Signature doesn't match");
				}
				if (!Arrays.equals(this.args, args)) {
					throw new IllegalArgumentException("Arguments don't match");
				}
			}

			@Override
			public String toString() {
				return String.format("Call with signature [%s] and arguments %s", this.signature,
					ObjectUtils.nullSafeToString(args));
			}
		}

		/**
		 * The list of recorded calls.
		 */
		private final LinkedList<Call> calls = new LinkedList<Call>();

		/**
		 * The number of calls already verified.
		 */
		private int verified;

		public void verify() {
			if (verified != calls.size()) {
				throw new IllegalStateException("Expected " + calls.size() + " calls, but received " + verified);
			}
		}

		/**
		 * Validate the call and provide the expected return value.
		 */
		public Object respond(String lastSig, Object[] args) {
			Call c = nextCall();

			switch (c.responseType) {
				case return_: {
					return c.returnValue(lastSig, args);
				}
				case throw_: {
					return c.throwException(lastSig, args);
				}
				default: {
					throw new IllegalStateException("Behavior of " + c + " not specified");
				}
			}
		}

		private Call nextCall() {
			verified++;
			if (verified > calls.size()) {
				throw new IllegalStateException("Expected " + calls.size() + " calls, but received " + verified);
			}
			// The 'verified' count is 1-based; whereas, 'calls' is 0-based.
			return calls.get(verified - 1);
		}

		public void expectCall(String lastSig, Object[] lastArgs) {
			calls.add(new Call(lastSig, lastArgs));
		}

		public boolean hasCalls() {
			return !calls.isEmpty();
		}

		public void expectReturn(Object retVal) {
			Call c = calls.getLast();
			if (c.hasResponseSpecified()) {
				throw new IllegalStateException("No method invoked before setting return value");
			}
			c.setReturnVal(retVal);
		}

		public void expectThrow(Throwable throwable) {
			Call c = calls.getLast();
			if (c.hasResponseSpecified()) {
				throw new IllegalStateException("No method invoked before setting throwable");
			}
			c.setThrow(throwable);
		}
	}

	private final Expectations expectations = new Expectations();

	after() returning : mockStaticsTestMethod() {
		if (recording && (expectations.hasCalls())) {
			throw new IllegalStateException(
				"Calls recorded, yet playback state never reached: Create expectations then call "
						+ this.getClass().getSimpleName() + ".playback()");
		}
		expectations.verify();
	}

	Object around() : methodToMock() && cflowbelow(mockStaticsTestMethod()) {
		if (recording) {
			expectations.expectCall(thisJoinPointStaticPart.toLongString(), thisJoinPoint.getArgs());
			// Return value doesn't matter
			return null;
		}
		else {
			return expectations.respond(thisJoinPointStaticPart.toLongString(), thisJoinPoint.getArgs());
		}
	}

	public void expectReturnInternal(Object retVal) {
		if (!recording) {
			throw new IllegalStateException("Not recording: Cannot set return value");
		}
		expectations.expectReturn(retVal);
	}

	public void expectThrowInternal(Throwable throwable) {
		if (!recording) {
			throw new IllegalStateException("Not recording: Cannot set throwable value");
		}
		expectations.expectThrow(throwable);
	}

	public void playbackInternal() {
		recording = false;
	}

}

/*
 * Copyright 2002-2014 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.mock.staticmock;

import java.util.Arrays;
import java.util.LinkedList;

import org.springframework.util.ObjectUtils;

/**
 * Abstract aspect to enable mocking of methods picked out by a pointcut.
 *
 *
Sub-aspects must define:
 *

 *
the {@link #mockStaticsTestMethod()} pointcut to indicate call stacks
 * when mocking should be triggered
 *
the {@link #methodToMock()} pointcut to pick out method invocations to mock
 *

 *
 * @author Rod Johnson
 * @author Ramnivas Laddad
 * @author Sam Brannen
 */
public abstract aspect AbstractMethodMockingControl percflow(mockStaticsTestMethod()) {

	protected abstract pointcut mockStaticsTestMethod();

	protected abstract pointcut methodToMock();

	private boolean recording = true;

	static enum CallResponse {
		nothing, return_, throw_
	};

	/**
	 * Represents a list of expected calls to methods.
	 */
	// Public to allow inserted code to access: is this normal??
	public class Expectations {

		/**
		 * Represents an expected call to a method.
		 */
		private class Call {

			private final String signature;
			private final Object[] args;

			private Object responseObject; // return value or throwable
			private CallResponse responseType = CallResponse.nothing;

			public Call(String signature, Object[] args) {
				this.signature = signature;
				this.args = args;
			}

			public boolean hasResponseSpecified() {
				return responseType != CallResponse.nothing;
			}

			public void setReturnVal(Object retVal) {
				this.responseObject = retVal;
				responseType = CallResponse.return_;
			}

			public void setThrow(Throwable throwable) {
				this.responseObject = throwable;
				responseType = CallResponse.throw_;
			}

			public Object returnValue(String lastSig, Object[] args) {
				checkSignature(lastSig, args);
				return responseObject;
			}

			public Object throwException(String lastSig, Object[] args) {
				checkSignature(lastSig, args);
				throw (RuntimeException) responseObject;
			}

			private void checkSignature(String lastSig, Object[] args) {
				if (!signature.equals(lastSig)) {
					throw new IllegalArgumentException("Signature doesn't match");
				}
				if (!Arrays.equals(this.args, args)) {
					throw new IllegalArgumentException("Arguments don't match");
				}
			}

			@Override
			public String toString() {
				return String.format("Call with signature [%s] and arguments %s", this.signature,
					ObjectUtils.nullSafeToString(args));
			}
		}

		/**
		 * The list of recorded calls.
		 */
		private final LinkedList<Call> calls = new LinkedList<Call>();

		/**
		 * The number of calls already verified.
		 */
		private int verified;

		public void verify() {
			if (verified != calls.size()) {
				throw new IllegalStateException("Expected " + calls.size() + " calls, but received " + verified);
			}
		}

		/**
		 * Validate the call and provide the expected return value.
		 */
		public Object respond(String lastSig, Object[] args) {
			Call c = nextCall();

			switch (c.responseType) {
				case return_: {
					return c.returnValue(lastSig, args);
				}
				case throw_: {
					return c.throwException(lastSig, args);
				}
				default: {
					throw new IllegalStateException("Behavior of " + c + " not specified");
				}
			}
		}

		private Call nextCall() {
			verified++;
			if (verified > calls.size()) {
				throw new IllegalStateException("Expected " + calls.size() + " calls, but received " + verified);
			}
			// The 'verified' count is 1-based; whereas, 'calls' is 0-based.
			return calls.get(verified - 1);
		}

		public void expectCall(String lastSig, Object[] lastArgs) {
			calls.add(new Call(lastSig, lastArgs));
		}

		public boolean hasCalls() {
			return !calls.isEmpty();
		}

		public void expectReturn(Object retVal) {
			Call c = calls.getLast();
			if (c.hasResponseSpecified()) {
				throw new IllegalStateException("No method invoked before setting return value");
			}
			c.setReturnVal(retVal);
		}

		public void expectThrow(Throwable throwable) {
			Call c = calls.getLast();
			if (c.hasResponseSpecified()) {
				throw new IllegalStateException("No method invoked before setting throwable");
			}
			c.setThrow(throwable);
		}
		}
	}

	private final Expectations expectations = new Expectations();

	after() returning : mockStaticsTestMethod() {
		if (recording && (expectations.hasCalls())) {
			throw new IllegalStateException(
				"Calls recorded, yet playback state never reached: Create expectations then call "
						+ this.getClass().getSimpleName() + ".playback()");
		}
		expectations.verify();
	}

	Object around() : methodToMock() && cflowbelow(mockStaticsTestMethod()) {
		if (recording) {
			expectations.expectCall(thisJoinPointStaticPart.toLongString(), thisJoinPoint.getArgs());
			// Return value doesn't matter
			return null;
		}
		else {
			return expectations.respond(thisJoinPointStaticPart.toLongString(), thisJoinPoint.getArgs());
		}
	}

	public void expectReturnInternal(Object retVal) {
		if (!recording) {
			throw new IllegalStateException("Not recording: Cannot set return value");
		}
		expectations.expectReturn(retVal);
	}

	public void expectThrowInternal(Throwable throwable) {
		if (!recording) {
			throw new IllegalStateException("Not recording: Cannot set throwable value");
		}
		expectations.expectThrow(throwable);
	}

	public void playbackInternal() {
		recording = false;
	}

}


Dirty fix for issue #16

Dirty fix for issue #16

/**
 * Copyright (C) 2011-2014 Thales Services - ThereSIS - All rights reserved.
 *
 * 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 com.thalesgroup.authzforce.audit.aspect;

import java.net.URI;
import java.text.DateFormat;
import java.text.SimpleDateFormat;

import oasis.names.tc.xacml._3_0.core.schema.wd_17.Policy;
import oasis.names.tc.xacml._3_0.core.schema.wd_17.Result;
import oasis.names.tc.xacml._3_0.core.schema.wd_17.Rule;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.sun.xacml.BasicEvaluationCtx;
import com.sun.xacml.attr.DateTimeAttribute;
import com.sun.xacml.attr.xacmlv3.AttributeValue;
import com.sun.xacml.cond.xacmlv3.EvaluationResult;
import com.thalesgroup.authzforce.audit.AttributesResolved;
import com.thalesgroup.authzforce.audit.AuditLogs;
import com.thalesgroup.authzforce.audit.annotations.Audit;
import com.thalesgroup.authzforce.audit.schema.pdp.AuditLog;
import com.thalesgroup.authzforce.audit.schema.pdp.AuditedPolicy;
import com.thalesgroup.authzforce.audit.schema.pdp.AuditedResult;
import com.thalesgroup.authzforce.audit.schema.pdp.AuditedRule;
import com.thalesgroup.authzforce.xacml.schema.XACMLAttributeId;
import com.thalesgroup.authzforce.xacml.schema.XACMLDatatypes;

@Aspect
public class AuditAspect {

	private static final Logger LOGGER = LoggerFactory
			.getLogger(AuditAspect.class);
	private static final DateFormat dateFormat = new SimpleDateFormat(
			"dd-MM-yyyy HH:mm:ss.SSSSSSSSS");
	private static final DateFormat dateFormatWOoNano = new SimpleDateFormat(
			"dd-MM-yyyy HH:mm:ss");

	@Pointcut("execution (@com.thalesgroup.authzforce.audit.annotations * *.*(..))")
	public void searchAnnotation() {

	}

	@AfterReturning(value = "@annotation(annotation)", returning = "result")
	public void doAuditLog(Audit annotation, JoinPoint jp, Object result) {
		switch (annotation.type()) {
		case RULE:
			AuditLog audit = new AuditLog();
			AuditedRule auditedRule = new AuditedRule();
			auditedRule.setId(((Rule) jp.getTarget()).getRuleId());
			auditedRule.setResult(AuditedResult.fromValue(((Result) result)
					.getDecision().value()));
			for (Object arg : jp.getArgs()) {
				if (arg instanceof BasicEvaluationCtx) {
					audit.setDate(this.formatDate(((BasicEvaluationCtx) arg)
							.getCurrentDateTime()));
					audit.setRequest(((BasicEvaluationCtx) arg).getRequest());
					audit = this.setAttributes(audit, ((BasicEvaluationCtx) arg));
				}
			}

			audit.getRules().add(auditedRule);

			synchronized (audit) {
				AuditLogs.getInstance().addAudit(audit);
			}
			break;

		case POLICY:
			audit = new AuditLog();
			AuditedPolicy auditedPolicy = new AuditedPolicy();
			auditedPolicy.setId(((Policy) jp.getTarget()).getPolicyId());
			auditedPolicy.setRuleCombiningAlgorithm(((Policy) jp.getTarget())
					.getRuleCombiningAlgId());
			auditedPolicy.setResult(AuditedResult.fromValue(((Result) result)
					.getDecision().value()));
			for (Object arg : jp.getArgs()) {
				if (arg instanceof BasicEvaluationCtx) {
					audit.setDate(this.formatDate(((BasicEvaluationCtx) arg)
							.getCurrentDateTime()));
					audit.setRequest(((BasicEvaluationCtx) arg).getRequest());
					audit = this.setAttributes(audit, ((BasicEvaluationCtx) arg));;
				}
			}
			audit.getMatchedPolicies().add(auditedPolicy);
			synchronized (audit) {
				AuditLogs.getInstance().addAudit(audit);
			}
			break;

		case ATTRIBUTE:
			audit = new AuditLog();
			AttributesResolved attrResolved = new AttributesResolved();
			// FIXME: jump subject-id research due to infinite call loop (issue #16)
			if(jp.getArgs()[1] != null && jp.getArgs()[1] instanceof URI) {
				URI attributeId = (URI) jp.getArgs()[1];
				if(attributeId.equals(URI.create(XACMLAttributeId.XACML_SUBJECT_SUBJECT_ID.value()))) {
					break;
				}
			}
			for (Object arg : jp.getArgs()) {
				if (arg instanceof BasicEvaluationCtx) {
					audit.setDate(this.formatDate(((BasicEvaluationCtx) arg)
							.getCurrentDateTime()));
					audit.setRequest(((BasicEvaluationCtx) arg).getRequest());
					audit = this.setAttributes(audit, ((BasicEvaluationCtx) arg));
				}
			}
			attrResolved.setAttributeValue(((EvaluationResult) result)
					.getAttributeValue());
			break;

		// Used to display and clean the audit log pool
		case DISPLAY:
			LOGGER.info(AuditLogs.getInstance().toString());
			break;
		default:
			LOGGER.error("Type unknown: " + annotation.type());
		}
	}

	private AuditLog setAttributes(AuditLog audit,
			BasicEvaluationCtx basicEvaluationCtx) {
		if (((BasicEvaluationCtx) basicEvaluationCtx).getSubjectAttribute(
				URI.create(XACMLDatatypes.XACML_DATATYPE_STRING.value()),
				URI.create(XACMLAttributeId.XACML_SUBJECT_SUBJECT_ID.value()),
				URI.create(XACMLAttributeId.SUBJECT_CATEGORY.value())) != null) {
			if(((BasicEvaluationCtx) basicEvaluationCtx).getSubjectAttribute(
						URI.create(XACMLDatatypes.XACML_DATATYPE_STRING.value()),
						URI.create(XACMLAttributeId.XACML_SUBJECT_SUBJECT_ID.value()),
						URI.create(XACMLAttributeId.SUBJECT_CATEGORY.value())).getAttributeValue().getContent().size() > 0) {
							String subjectId = (String) ((BasicEvaluationCtx) basicEvaluationCtx).getSubjectAttribute(
									URI.create(XACMLDatatypes.XACML_DATATYPE_STRING.value()),
									URI.create(XACMLAttributeId.XACML_SUBJECT_SUBJECT_ID.value()),
									URI.create(XACMLAttributeId.SUBJECT_CATEGORY.value())).getAttributeValue().getContent().get(0);

							audit.setSubjectId(subjectId);
						}
		}

		AttributeValue resourceId = ((BasicEvaluationCtx) basicEvaluationCtx).getResourceId();
		if(resourceId.getContent().size() > 0) {
			audit.setResourceId(String.valueOf(resourceId.getContent().get(0)));
		}

		// FIXME: Set the ActionId attribute
		// audit.setActionId();

		return audit;
	}

	private String formatDate(DateTimeAttribute currentDateTime) {
		String date = dateFormatWOoNano.format(currentDateTime.getValue())
				+ "." + String.valueOf(currentDateTime.getNanoseconds());

		return date;
	}
}

/**
 * Copyright (C) 2011-2014 Thales Services - ThereSIS - All rights reserved.
 *
 * 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 com.thalesgroup.authzforce.audit.aspect;

import java.net.URI;
import java.text.DateFormat;
import java.text.SimpleDateFormat;

import oasis.names.tc.xacml._3_0.core.schema.wd_17.Policy;
import oasis.names.tc.xacml._3_0.core.schema.wd_17.Result;
import oasis.names.tc.xacml._3_0.core.schema.wd_17.Rule;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.sun.xacml.BasicEvaluationCtx;
import com.sun.xacml.attr.DateTimeAttribute;
import com.sun.xacml.attr.xacmlv3.AttributeValue;
import com.sun.xacml.cond.xacmlv3.EvaluationResult;
import com.thalesgroup.authzforce.audit.AttributesResolved;
import com.thalesgroup.authzforce.audit.AuditLogs;
import com.thalesgroup.authzforce.audit.annotations.Audit;
import com.thalesgroup.authzforce.audit.schema.pdp.AuditLog;
import com.thalesgroup.authzforce.audit.schema.pdp.AuditedPolicy;
import com.thalesgroup.authzforce.audit.schema.pdp.AuditedResult;
import com.thalesgroup.authzforce.audit.schema.pdp.AuditedRule;
import com.thalesgroup.authzforce.xacml.schema.XACMLAttributeId;
import com.thalesgroup.authzforce.xacml.schema.XACMLDatatypes;

@Aspect
public class AuditAspect {

	private static final Logger LOGGER = LoggerFactory
			.getLogger(AuditAspect.class);
	private static final DateFormat dateFormat = new SimpleDateFormat(
			"dd-MM-yyyy HH:mm:ss.SSSSSSSSS");
	private static final DateFormat dateFormatWOoNano = new SimpleDateFormat(
			"dd-MM-yyyy HH:mm:ss");

	@Pointcut("execution (@com.thalesgroup.authzforce.audit.annotations * *.*(..))")
	public void searchAnnotation() {

	}

	@AfterReturning(value = "@annotation(annotation)", returning = "result")
	public void doAuditLog(Audit annotation, JoinPoint jp, Object result) {
		switch (annotation.type()) {
		case RULE:
			AuditLog audit = new AuditLog();
			AuditedRule auditedRule = new AuditedRule();
			auditedRule.setId(((Rule) jp.getTarget()).getRuleId());
			auditedRule.setResult(AuditedResult.fromValue(((Result) result)
					.getDecision().value()));
			for (Object arg : jp.getArgs()) {
				if (arg instanceof BasicEvaluationCtx) {
					audit.setDate(this.formatDate(((BasicEvaluationCtx) arg)
							.getCurrentDateTime()));
					audit.setRequest(((BasicEvaluationCtx) arg).getRequest());
					audit = this.setAttributes(audit, ((BasicEvaluationCtx) arg));
				}
			}

			audit.getRules().add(auditedRule);

			synchronized (audit) {
				AuditLogs.getInstance().addAudit(audit);
			}
			break;

		case POLICY:
			audit = new AuditLog();
			AuditedPolicy auditedPolicy = new AuditedPolicy();
			auditedPolicy.setId(((Policy) jp.getTarget()).getPolicyId());
			auditedPolicy.setRuleCombiningAlgorithm(((Policy) jp.getTarget())
					.getRuleCombiningAlgId());
			auditedPolicy.setResult(AuditedResult.fromValue(((Result) result)
					.getDecision().value()));
			for (Object arg : jp.getArgs()) {
				if (arg instanceof BasicEvaluationCtx) {
					audit.setDate(this.formatDate(((BasicEvaluationCtx) arg)
							.getCurrentDateTime()));
					audit.setRequest(((BasicEvaluationCtx) arg).getRequest());
					audit = this.setAttributes(audit, ((BasicEvaluationCtx) arg));;
				}
			}
			audit.getMatchedPolicies().add(auditedPolicy);
			synchronized (audit) {
				AuditLogs.getInstance().addAudit(audit);
			}
			break;

		case ATTRIBUTE:
			audit = new AuditLog();
			AttributesResolved attrResolved = new AttributesResolved();
			for (Object arg : jp.getArgs()) {
				if (arg instanceof BasicEvaluationCtx) {
					audit.setDate(this.formatDate(((BasicEvaluationCtx) arg)
							.getCurrentDateTime()));
					audit.setRequest(((BasicEvaluationCtx) arg).getRequest());
					audit = this.setAttributes(audit, ((BasicEvaluationCtx) arg));
				}
			}
			attrResolved.setAttributeValue(((EvaluationResult) result)
					.getAttributeValue());
			break;

		// Used to display and clean the audit log pool
		case DISPLAY:
			LOGGER.info(AuditLogs.getInstance().toString());
			break;
		default:
			LOGGER.error("Type unknown: " + annotation.type());
		}
	}

	private AuditLog setAttributes(AuditLog audit,
			BasicEvaluationCtx basicEvaluationCtx) {
		if (((BasicEvaluationCtx) basicEvaluationCtx).getSubjectAttribute(
				URI.create(XACMLDatatypes.XACML_DATATYPE_STRING.value()),
				URI.create(XACMLAttributeId.XACML_SUBJECT_SUBJECT_ID.value()),
				URI.create(XACMLAttributeId.SUBJECT_CATEGORY.value())) != null) {
			if(((BasicEvaluationCtx) basicEvaluationCtx).getSubjectAttribute(
						URI.create(XACMLDatatypes.XACML_DATATYPE_STRING.value()),
						URI.create(XACMLAttributeId.XACML_SUBJECT_SUBJECT_ID.value()),
						URI.create(XACMLAttributeId.SUBJECT_CATEGORY.value())).getAttributeValue().getContent().size() > 0) {
							String subjectId = (String) ((BasicEvaluationCtx) basicEvaluationCtx).getSubjectAttribute(
									URI.create(XACMLDatatypes.XACML_DATATYPE_STRING.value()),
									URI.create(XACMLAttributeId.XACML_SUBJECT_SUBJECT_ID.value()),
									URI.create(XACMLAttributeId.SUBJECT_CATEGORY.value())).getAttributeValue().getContent().get(0);

							audit.setSubjectId(subjectId);
						}
		}

		AttributeValue resourceId = ((BasicEvaluationCtx) basicEvaluationCtx).getResourceId();
		if(resourceId.getContent().size() > 0) {
			audit.setResourceId(String.valueOf(resourceId.getContent().get(0)));
		}

		// FIXME: Set the ActionId attribute
		// audit.setActionId();

		return audit;
	}

	private String formatDate(DateTimeAttribute currentDateTime) {
		String date = dateFormatWOoNano.format(currentDateTime.getValue())
				+ "." + String.valueOf(currentDateTime.getNanoseconds());

		return date;
	}
}


Fix possible null pointer issues with around advice

package sample;

import static java.lang.Thread.sleep;

import akka.dispatch.verification.Instrumenter;

import akka.actor.ActorRef;
import akka.actor.ScalaActorRef;
import akka.actor.Actor;
import akka.actor.Props;
import akka.actor.ActorCell;
import akka.actor.ActorSystem;
import akka.actor.ActorContext;
import akka.actor.Scheduler;
import akka.actor.Cancellable;
import akka.actor.LightArrayRevolverScheduler;

import akka.pattern.AskSupport;

import akka.dispatch.Envelope;
import akka.dispatch.MessageQueue;
import akka.dispatch.MessageDispatcher;

import scala.concurrent.impl.CallbackRunnable;
import scala.concurrent.duration.FiniteDuration;
import scala.concurrent.ExecutionContext;
import java.lang.Runnable;

privileged public aspect WeaveActor {

  Instrumenter inst = Instrumenter.apply();

  pointcut enqueueOperation(MessageQueue me, ActorRef receiver, Envelope handle):
  execution(public * akka.dispatch.MessageQueue.enqueue(ActorRef, Envelope)) &&
  args(receiver, handle) && this(me);

  Object around(MessageQueue me, ActorRef receiver, Envelope handle):
  enqueueOperation(me, receiver, handle) {
	return proceed(me, receiver, handle);
  }

  before(ActorCell me, Object msg):
  execution(* akka.actor.ActorCell.receiveMessage(Object)) &&
  args(msg, ..) && this(me) {
	inst.beforeMessageReceive(me, msg);
  }

  after(ActorCell me, Object msg):
  execution(* akka.actor.ActorCell.receiveMessage(Object)) &&
  args(msg, ..) && this(me) {
	inst.afterMessageReceive(me, msg);
  }

  pointcut dispatchOperation(MessageDispatcher me, ActorCell receiver, Envelope handle):
  execution(* akka.dispatch.MessageDispatcher.dispatch(..)) &&
  args(receiver, handle, ..) && this(me);

  Object around(MessageDispatcher me, ActorCell receiver, Envelope handle):
  dispatchOperation(me, receiver, handle) {
  	if (inst.aroundDispatch(me, receiver, handle))
   		return proceed(me, receiver, handle);
   	else
   		return null;
  }

  after(ActorSystem me, Props props) returning(ActorRef actor):
  execution(ActorRef akka.actor.ActorSystem.actorOf(Props)) &&
  args(props) && this(me) {
  	inst.new_actor(me, props, actor);
  }

  after(ActorSystem me, Props props, String name) returning(ActorRef actor):
  execution(ActorRef akka.actor.ActorSystem.actorOf(Props, String)) &&
  args(props, name) && this(me) {
  	inst.new_actor(me, props, name, actor);
  }

  after(ActorContext me, Props props) returning(ActorRef actor):
  execution(ActorRef akka.actor.ActorContext.actorOf(Props)) &&
  args(props) && this(me) {
  	inst.new_actor(me.system(), props, actor);
  }

  after(ActorContext me, Props props, String name) returning(ActorRef actor):
  execution(ActorRef akka.actor.ActorContext.actorOf(Props, String)) &&
  args(props, name) && this(me) {
  	inst.new_actor(me.system(), props, name, actor);
  }

  before(ActorRef me, Object msg, ActorRef sender):
  execution(* akka.actor.ScalaActorRef.$bang(Object, ActorRef)) &&
  args(msg, sender) && this(me) {
  	inst.tell(me, msg, sender);
  }

  before(ActorRef me, Object msg, ActorRef sender):
  execution(* akka.actor.ActorRef.tell(Object, ActorRef)) &&
  args(msg, sender, ..) && this(me) {
  }

  // Override akka.actor.Scheduler.schedulerOnce
  pointcut scheduleOnce(LightArrayRevolverScheduler me, FiniteDuration delay, ActorRef receiver, Object msg, ExecutionContext exc, ActorRef sender):
  execution(public * akka.actor.LightArrayRevolverScheduler.scheduleOnce(FiniteDuration,ActorRef,Object,ExecutionContext,ActorRef)) &&
  args(delay, receiver, msg, exc, sender) && this(me);

  // Never actually proceed(), just schedule our own timer, which does not use
  // ! directly, but instead calls enqueue_message..
  Object around(LightArrayRevolverScheduler me, FiniteDuration delay, ActorRef receiver, Object msg, ExecutionContext exc, ActorRef sender):
  scheduleOnce(me, delay, receiver, msg, exc, sender) {
    class MyRunnable implements java.lang.Runnable {
      ActorRef rcv;
      Object m;
      Instrumenter i;

      public MyRunnable(ActorRef receiver, Object msg, Instrumenter inst) {
        rcv = receiver;
        m = msg;
        i = inst;
      }

      public void run() {
        // Use essentially the same scheduleOnce implementation, but don't use !
        // See:
        //   https://github.com/akka/akka/blob/cb05725c1ec8a09e9bfd57dd093911dd41c7b288/akka-actor/src/main/scala/akka/actor/Scheduler.scala#L105
        i.handleTick(rcv, m);
      }
    }
    Cancellable c = me.scheduleOnce(delay, new MyRunnable(receiver, msg, inst), exc);
    inst.registerCancellable(c);
	  return c;
  }

  // Override akka.actor.Scheduler.scheduler
  pointcut schedule(LightArrayRevolverScheduler me, FiniteDuration delay, FiniteDuration interval, ActorRef receiver, Object msg, ExecutionContext exc, ActorRef sender):
  execution(public * akka.actor.LightArrayRevolverScheduler.schedule(FiniteDuration,FiniteDuration,ActorRef,Object,ExecutionContext,ActorRef)) &&
  args(delay, interval, receiver, msg, exc, sender) && this(me);

  // Never actually proceed(), just schedule our own timer, which does not use
  // ! directly, but instead calls enqueue_message..
  Object around(LightArrayRevolverScheduler me, FiniteDuration delay, FiniteDuration interval, ActorRef receiver, Object msg, ExecutionContext exc, ActorRef sender):
  schedule(me, delay, interval, receiver, msg, exc, sender) {
    class MyRunnable implements java.lang.Runnable {
      ActorRef rcv;
      Object m;
      Instrumenter i;

      public MyRunnable(ActorRef receiver, Object msg, Instrumenter inst) {
        rcv = receiver;
        m = msg;
        i = inst;
      }

      public void run() {
        // Use essentially the same scheduleOnce implementation, but don't use !
        // See:
        //   https://github.com/akka/akka/blob/cb05725c1ec8a09e9bfd57dd093911dd41c7b288/akka-actor/src/main/scala/akka/actor/Scheduler.scala#L105
        i.handleTick(rcv, m);
      }
    }
    Cancellable c = me.schedule(delay, interval, new MyRunnable(receiver, msg, inst), exc);
    inst.registerCancellable(c);
	  return c;
  }
}

package sample;

import static java.lang.Thread.sleep;

import akka.dispatch.verification.Instrumenter;

import akka.actor.ActorRef;
import akka.actor.ScalaActorRef;
import akka.actor.Actor;
import akka.actor.Props;
import akka.actor.ActorCell;
import akka.actor.ActorSystem;
import akka.actor.ActorContext;
import akka.actor.Scheduler;
import akka.actor.Cancellable;
import akka.actor.LightArrayRevolverScheduler;

import akka.pattern.AskSupport;

import akka.dispatch.Envelope;
import akka.dispatch.MessageQueue;
import akka.dispatch.MessageDispatcher;

import scala.concurrent.impl.CallbackRunnable;
import scala.concurrent.duration.FiniteDuration;
import scala.concurrent.ExecutionContext;
import java.lang.Runnable;

privileged public aspect WeaveActor {

  Instrumenter inst = Instrumenter.apply();

  pointcut enqueueOperation(MessageQueue me, ActorRef receiver, Envelope handle):
  execution(public * akka.dispatch.MessageQueue.enqueue(ActorRef, Envelope)) &&
  args(receiver, handle) && this(me);

  Object around(MessageQueue me, ActorRef receiver, Envelope handle):
  enqueueOperation(me, receiver, handle) {
	return proceed(me, receiver, handle);
  }

  before(ActorCell me, Object msg):
  execution(* akka.actor.ActorCell.receiveMessage(Object)) &&
  args(msg, ..) && this(me) {
	inst.beforeMessageReceive(me, msg);
  }

  after(ActorCell me, Object msg):
  execution(* akka.actor.ActorCell.receiveMessage(Object)) &&
  args(msg, ..) && this(me) {
	inst.afterMessageReceive(me, msg);
  }

  pointcut dispatchOperation(MessageDispatcher me, ActorCell receiver, Envelope handle):
  execution(* akka.dispatch.MessageDispatcher.dispatch(..)) &&
  args(receiver, handle, ..) && this(me);

  Object around(MessageDispatcher me, ActorCell receiver, Envelope handle):
  dispatchOperation(me, receiver, handle) {
  	if (inst.aroundDispatch(me, receiver, handle))
   		return proceed(me, receiver, handle);
   	else
   		return null;
  }

  after(ActorSystem me, Props props) returning(ActorRef actor):
  execution(ActorRef akka.actor.ActorSystem.actorOf(Props)) &&
  args(props) && this(me) {
  	inst.new_actor(me, props, actor);
  }

  after(ActorSystem me, Props props, String name) returning(ActorRef actor):
  execution(ActorRef akka.actor.ActorSystem.actorOf(Props, String)) &&
  args(props, name) && this(me) {
  	inst.new_actor(me, props, name, actor);
  }

  after(ActorContext me, Props props) returning(ActorRef actor):
  execution(ActorRef akka.actor.ActorContext.actorOf(Props)) &&
  args(props) && this(me) {
  	inst.new_actor(me.system(), props, actor);
  }

  after(ActorContext me, Props props, String name) returning(ActorRef actor):
  execution(ActorRef akka.actor.ActorContext.actorOf(Props, String)) &&
  args(props, name) && this(me) {
  	inst.new_actor(me.system(), props, name, actor);
  }

  before(ActorRef me, Object msg, ActorRef sender):
  execution(* akka.actor.ScalaActorRef.$bang(Object, ActorRef)) &&
  args(msg, sender) && this(me) {
  	inst.tell(me, msg, sender);
  }

  before(ActorRef me, Object msg, ActorRef sender):
  execution(* akka.actor.ActorRef.tell(Object, ActorRef)) &&
  args(msg, sender, ..) && this(me) {
  }

  // Override akka.actor.Scheduler.schedulerOnce
  pointcut scheduleOnce(LightArrayRevolverScheduler me, FiniteDuration delay, ActorRef receiver, Object msg, ExecutionContext exc, ActorRef sender):
  execution(public * akka.actor.LightArrayRevolverScheduler.scheduleOnce(FiniteDuration,ActorRef,Object,ExecutionContext,ActorRef)) &&
  args(delay, receiver, msg, exc, sender) && this(me);

  // Never actually proceed(), just schedule our own timer, which does not use
  // ! directly, but instead calls enqueue_message..
  Object around(LightArrayRevolverScheduler me, FiniteDuration delay, ActorRef receiver, Object msg, ExecutionContext exc, ActorRef sender):
  scheduleOnce(me, delay, receiver, msg, exc, sender) {
    class MyRunnable implements java.lang.Runnable {
      ActorRef rcv;
      Object m;
      Instrumenter i;

      public MyRunnable(ActorRef receiver, Object msg, Instrumenter inst) {
        rcv = receiver;
        m = msg;
        i = inst;
      }

      public void run() {
        // Use essentially the same scheduleOnce implementation, but don't use !
        // See:
        //   https://github.com/akka/akka/blob/cb05725c1ec8a09e9bfd57dd093911dd41c7b288/akka-actor/src/main/scala/akka/actor/Scheduler.scala#L105
        i.handleTick(rcv, m);
      }
    }
    inst.registerCancellable(me.scheduleOnce(delay, new MyRunnable(receiver, msg, inst), exc));
	  return null;
  }

  // Override akka.actor.Scheduler.scheduler
  pointcut schedule(LightArrayRevolverScheduler me, FiniteDuration delay, FiniteDuration interval, ActorRef receiver, Object msg, ExecutionContext exc, ActorRef sender):
  execution(public * akka.actor.LightArrayRevolverScheduler.schedule(FiniteDuration,FiniteDuration,ActorRef,Object,ExecutionContext,ActorRef)) &&
  args(delay, interval, receiver, msg, exc, sender) && this(me);

  // Never actually proceed(), just schedule our own timer, which does not use
  // ! directly, but instead calls enqueue_message..
  Object around(LightArrayRevolverScheduler me, FiniteDuration delay, FiniteDuration interval, ActorRef receiver, Object msg, ExecutionContext exc, ActorRef sender):
  schedule(me, delay, interval, receiver, msg, exc, sender) {
    class MyRunnable implements java.lang.Runnable {
      ActorRef rcv;
      Object m;
      Instrumenter i;

      public MyRunnable(ActorRef receiver, Object msg, Instrumenter inst) {
        rcv = receiver;
        m = msg;
        i = inst;
      }

      public void run() {
        // Use essentially the same scheduleOnce implementation, but don't use !
        // See:
        //   https://github.com/akka/akka/blob/cb05725c1ec8a09e9bfd57dd093911dd41c7b288/akka-actor/src/main/scala/akka/actor/Scheduler.scala#L105
        i.handleTick(rcv, m);
      }
    }
    inst.registerCancellable(me.schedule(delay, interval, new MyRunnable(receiver, msg, inst), exc));
	  return null;
  }
}