Improve error reporting bindings for two exception cases, to get away from handler(..) pointcuts which are less versatile for later debugging aspects to manipulate.

git-svn-id: 2c527eb49caa05e19d6b2be874bf74fa9d7ea670@614317 13f79535-47bb-0310-9956-ffa450edef68

package org.apache.maven.errors;

import org.codehaus.plexus.logging.console.ConsoleLogger;
import org.codehaus.plexus.logging.Logger;
import org.codehaus.plexus.util.introspection.ReflectionValueExtractor;
import org.apache.maven.project.path.PathTranslator;
import org.apache.maven.plugin.MojoExecution;
import org.apache.maven.artifact.resolver.ArtifactResolutionException;
import org.apache.maven.artifact.resolver.MultipleArtifactsNotFoundException;
import org.apache.maven.plugin.PluginConfigurationException;
import org.apache.maven.plugin.descriptor.Parameter;
import org.apache.maven.plugin.loader.PluginLoaderException;
import org.apache.maven.plugin.loader.PluginLoader;
import org.apache.maven.plugin.PluginExecutionException;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.artifact.resolver.ArtifactNotFoundException;
import org.apache.maven.execution.MavenSession;
import org.apache.maven.lifecycle.model.MojoBinding;
import org.apache.maven.lifecycle.statemgmt.StateManagementUtils;
import org.apache.maven.lifecycle.DefaultLifecycleExecutor;
import org.apache.maven.lifecycle.LifecycleException;
import org.apache.maven.lifecycle.LifecycleExecutionException;
import org.apache.maven.project.MavenProject;
import org.apache.maven.plugin.DefaultPluginManager;
import org.apache.maven.plugin.PluginManager;
import org.apache.maven.plugin.PluginParameterExpressionEvaluator;
import org.apache.maven.plugin.PluginParameterException;
import org.apache.maven.plugin.Mojo;
import org.codehaus.plexus.component.configurator.expression.ExpressionEvaluationException;
import org.codehaus.plexus.component.repository.exception.ComponentLookupException;
import org.codehaus.plexus.configuration.PlexusConfiguration;
import org.codehaus.plexus.PlexusContainer;
import org.codehaus.plexus.util.introspection.ReflectionValueExtractor;

import java.util.List;

public privileged aspect LifecycleErrorReporterAspect
    extends AbstractCoreReporterAspect
{

    private pointcut le_executeGoalAndHandleFailures( MojoBinding binding ):
        execution( void DefaultLifecycleExecutor.executeGoalAndHandleFailures( MojoBinding, .. ) )
        && args( binding, .. );

    private pointcut le_executeGoalAndHandleFailures_withSession( MojoBinding binding, MavenSession session ):
        execution( void DefaultLifecycleExecutor.executeGoalAndHandleFailures( MojoBinding, MavenSession, .. ) )
        && args( binding, session, .. );

    private pointcut pm_executeMojo( MavenProject project ):
        execution( void PluginManager+.executeMojo( MavenProject, .. ) )
        && args( project, .. );

    private pointcut within_pm_executeMojo( MavenProject project ):
        withincode( void PluginManager+.executeMojo( MavenProject, .. ) )
        && args( project, .. );

    after( MojoBinding binding, MavenProject project ) throwing ( PluginLoaderException cause ):
        cflow( le_executeGoalAndHandleFailures( MojoBinding ) )
        && call( * PluginLoader+.loadPlugin( MojoBinding, MavenProject, .. ) )
        && args( binding, project, .. )
    {
        getReporter().reportErrorLoadingPlugin( binding, project, cause );
    }

    after( MojoBinding binding, MavenProject project ) throwing ( MojoExecutionException cause ):
        cflow( le_executeGoalAndHandleFailures( binding ) )
        && cflow( pm_executeMojo( project ) )
        && call( void Mojo+.execute() )
    {
        // this will be covered by the reportErrorLoadingPlugin(..) method.
        if ( !StateManagementUtils.RESOLVE_LATE_BOUND_PLUGIN_GOAL.equals( binding.getGoal() ) )
        {
            getReporter().reportMojoExecutionException( binding, project, cause );
        }
    }

    PluginExecutionException around( MojoBinding binding, MavenProject project ):
        cflow( le_executeGoalAndHandleFailures( binding ) )
        && cflow( pm_executeMojo( project ) )
        && call( PluginExecutionException.new( .., String ) )
    {
        PluginExecutionException cause = proceed( binding, project );
        getReporter().reportInvalidPluginExecutionEnvironment( binding, project, cause );

        return cause;
    }

    after( MojoBinding binding, MavenProject project ) throwing ( ComponentLookupException cause ):
        cflow( le_executeGoalAndHandleFailures( binding ) )
        && cflow( pm_executeMojo( project ) )
        && withincode( Mojo DefaultPluginManager.getConfiguredMojo( .. ) )
        && call( Object PlexusContainer+.lookup( .. ) )
    {
        getReporter().reportMojoLookupError( binding, project, cause );
    }

    Parameter currentParameter;

    private pointcut paramGetName( Parameter parameter ):
        call( String Parameter.getName() )
        && target( parameter );

    private pointcut pm_validatePomConfig():
        execution( void DefaultPluginManager.validatePomConfiguration( .. ) );

    private pointcut within_pm_validatePomConfig():
        withincode( void DefaultPluginManager.validatePomConfiguration( .. ) );

    private pointcut pm_checkRequiredParameters():
        execution( void DefaultPluginManager.checkRequiredParameters( .. ) );

    private pointcut within_pm_checkRequiredParameters():
        withincode( void DefaultPluginManager.checkRequiredParameters( .. ) );

    before( Parameter parameter ):
        ( within_pm_validatePomConfig()
          || within_pm_checkRequiredParameters() )
        && paramGetName( parameter )
    {
        currentParameter = parameter;
    }

    after() returning:
        pm_validatePomConfig() ||
        pm_checkRequiredParameters()
    {
        currentParameter = null;
    }

    private pointcut pm_executeMojoWithSessionAndExec( MavenProject project, MojoExecution exec, MavenSession session, DefaultPluginManager manager ):
        execution( void DefaultPluginManager.executeMojo( MavenProject, MojoExecution, MavenSession ) )
        && args( project, exec, session )
        && this( manager );

    after( MojoBinding binding, MavenProject project, MojoExecution exec, MavenSession session, DefaultPluginManager manager ) throwing( PluginConfigurationException cause ):
        cflow( le_executeGoalAndHandleFailures( binding ) )
        && cflow( pm_executeMojoWithSessionAndExec( project, exec, session, manager ) )
        && pm_validatePomConfig()
    {
        PathTranslator translator = manager.pathTranslator;
        Logger logger = new ConsoleLogger( Logger.LEVEL_INFO, "error reporting" );
        getReporter().reportAttemptToOverrideUneditableMojoParameter( currentParameter, binding, project, session, exec, translator, logger, cause );
    }

    PluginParameterException around( MojoBinding binding, MavenProject project, List invalidParameters ):
        cflow( le_executeGoalAndHandleFailures( binding ) )
        && cflow( pm_executeMojo( project ) )
        && cflow( pm_checkRequiredParameters() )
        && call( PluginParameterException.new( .., List ) )
        && args( .., invalidParameters )
    {
        PluginParameterException err = proceed( binding, project, invalidParameters );

        getReporter().reportMissingRequiredMojoParameter( binding, project, invalidParameters, err );

        return err;
    }

    private pointcut ppee_evaluate( String expression ):
        execution( Object PluginParameterExpressionEvaluator.evaluate( String ) )
        && args( expression );

    private pointcut within_ppee_evaluate( String expression ):
        withincode( Object PluginParameterExpressionEvaluator.evaluate( String ) )
        && args( expression );

    before( MojoBinding binding, MavenProject project, String expression, ExpressionEvaluationException err ):
        cflow( le_executeGoalAndHandleFailures( binding ) )
        && cflow( pm_executeMojo( project ) )
        && cflow( pm_checkRequiredParameters() )
        && cflow( ppee_evaluate( expression ) )
        && execution( ExpressionEvaluationException.new( String ) )
        && this( err )
    {
        getReporter().reportUseOfBannedMojoParameter( currentParameter,
                                                      binding,
                                                      project,
                                                      expression,
                                                      (String) PluginParameterExpressionEvaluator.BANNED_EXPRESSIONS.get( expression ),
                                                      err );
    }

    after( MojoBinding binding, MavenProject project, String expression ) throwing ( Exception cause ):
        cflow( le_executeGoalAndHandleFailures( binding ) )
        && cflow( pm_executeMojo( project ) )
        && cflow( pm_checkRequiredParameters() )
        && cflow( ppee_evaluate( expression ) )
        && within( PluginParameterExpressionEvaluator )
        && call( Object ReflectionValueExtractor.evaluate( String, Object ) )
    {
        getReporter().reportReflectionErrorWhileEvaluatingMojoParameter( currentParameter,
                                                      binding,
                                                      project,
                                                      expression,
                                                      cause );
    }

    after( MojoBinding binding, MavenProject project, PlexusConfiguration config ) throwing( PluginConfigurationException cause ):
        cflow( le_executeGoalAndHandleFailures( binding ) )
        && cflow( pm_executeMojo( project ) )
        && execution( void DefaultPluginManager.populatePluginFields( *, *, PlexusConfiguration, .. ) )
        && args( *, *, config, .. )
    {
        getReporter().reportErrorApplyingMojoConfiguration( binding, project, config, cause );
    }

    private pointcut pm_resolveTransitiveDependencies( MavenProject project, String scope ):
        execution( void DefaultPluginManager.resolveTransitiveDependencies( *, *, String, *, MavenProject ) )
        && args( *, *, scope, *, project );

    after( MavenProject project, String scope ) throwing( ArtifactNotFoundException cause ):
        pm_resolveTransitiveDependencies( project, scope )
    {
        getReporter().reportProjectDependenciesNotFound( project, scope, cause );
    }

    after( MavenProject project, String scope ) throwing( ArtifactResolutionException cause ):
        pm_resolveTransitiveDependencies( project, scope )
    {
        if ( cause instanceof MultipleArtifactsNotFoundException )
        {
            getReporter().reportProjectDependenciesNotFound( project, scope, (MultipleArtifactsNotFoundException) cause );
        }
        else
        {
            getReporter().reportProjectDependenciesUnresolvable( project, scope, cause );
        }
    }

    private pointcut le_getLifecycleBindings( List tasks, MavenProject configuringProject, String targetDescription ):
        execution( List DefaultLifecycleExecutor.getLifecycleBindings( List, MavenProject, *, String ) )
        && args( tasks, configuringProject, *, targetDescription );

    before( List tasks, MavenProject configuringProject, String targetDescription, LifecycleException cause ):
        cflow( le_getLifecycleBindings( tasks, configuringProject, targetDescription ) )
        && call( LifecycleExecutionException.new( .., LifecycleException ) )
        && args( .., cause )
    {
        getReporter().reportErrorFormulatingBuildPlan( tasks, configuringProject, targetDescription, cause );
    }

}

package org.apache.maven.errors;

import org.codehaus.plexus.logging.console.ConsoleLogger;
import org.codehaus.plexus.logging.Logger;
import org.apache.maven.project.path.PathTranslator;
import org.apache.maven.plugin.MojoExecution;
import org.apache.maven.artifact.resolver.ArtifactResolutionException;
import org.apache.maven.artifact.resolver.MultipleArtifactsNotFoundException;
import org.apache.maven.plugin.PluginConfigurationException;
import org.apache.maven.plugin.descriptor.Parameter;
import org.apache.maven.plugin.loader.PluginLoaderException;
import org.apache.maven.plugin.loader.PluginLoader;
import org.apache.maven.plugin.PluginExecutionException;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.artifact.resolver.ArtifactNotFoundException;
import org.apache.maven.execution.MavenSession;
import org.apache.maven.lifecycle.model.MojoBinding;
import org.apache.maven.lifecycle.statemgmt.StateManagementUtils;
import org.apache.maven.lifecycle.DefaultLifecycleExecutor;
import org.apache.maven.lifecycle.LifecycleException;
import org.apache.maven.lifecycle.LifecycleExecutionException;
import org.apache.maven.project.MavenProject;
import org.apache.maven.plugin.DefaultPluginManager;
import org.apache.maven.plugin.PluginManager;
import org.apache.maven.plugin.PluginParameterExpressionEvaluator;
import org.apache.maven.plugin.PluginParameterException;
import org.apache.maven.plugin.Mojo;
import org.codehaus.plexus.component.configurator.expression.ExpressionEvaluationException;
import org.codehaus.plexus.component.repository.exception.ComponentLookupException;
import org.codehaus.plexus.configuration.PlexusConfiguration;
import org.codehaus.plexus.PlexusContainer;

import java.util.List;

public privileged aspect LifecycleErrorReporterAspect
    extends AbstractCoreReporterAspect
{

    private pointcut le_executeGoalAndHandleFailures( MojoBinding binding ):
        execution( void DefaultLifecycleExecutor.executeGoalAndHandleFailures( MojoBinding, .. ) )
        && args( binding, .. );

    private pointcut le_executeGoalAndHandleFailures_withSession( MojoBinding binding, MavenSession session ):
        execution( void DefaultLifecycleExecutor.executeGoalAndHandleFailures( MojoBinding, MavenSession, .. ) )
        && args( binding, session, .. );

    private pointcut pm_executeMojo( MavenProject project ):
        execution( void PluginManager+.executeMojo( MavenProject, .. ) )
        && args( project, .. );

    private pointcut within_pm_executeMojo( MavenProject project ):
        withincode( void PluginManager+.executeMojo( MavenProject, .. ) )
        && args( project, .. );

    after( MojoBinding binding, MavenProject project ) throwing ( PluginLoaderException cause ):
        cflow( le_executeGoalAndHandleFailures( MojoBinding ) )
        && call( * PluginLoader+.loadPlugin( MojoBinding, MavenProject, .. ) )
        && args( binding, project, .. )
    {
        getReporter().reportErrorLoadingPlugin( binding, project, cause );
    }

    before( MojoBinding binding, MavenProject project, MojoExecutionException cause ):
        cflow( le_executeGoalAndHandleFailures( binding ) )
        && cflow( pm_executeMojo( project ) )
        && handler( MojoExecutionException )
        && args( cause )
    {
        // this will be covered by the reportErrorLoadingPlugin(..) method.
        if ( !StateManagementUtils.RESOLVE_LATE_BOUND_PLUGIN_GOAL.equals( binding.getGoal() ) )
        {
            getReporter().reportMojoExecutionException( binding, project, cause );
        }
    }

    PluginExecutionException around( MojoBinding binding, MavenProject project ):
        cflow( le_executeGoalAndHandleFailures( binding ) )
        && cflow( pm_executeMojo( project ) )
        && call( PluginExecutionException.new( .., String ) )
    {
        PluginExecutionException cause = proceed( binding, project );
        getReporter().reportInvalidPluginExecutionEnvironment( binding, project, cause );

        return cause;
    }

    after( MojoBinding binding, MavenProject project ) throwing ( ComponentLookupException cause ):
        cflow( le_executeGoalAndHandleFailures( binding ) )
        && cflow( pm_executeMojo( project ) )
        && withincode( Mojo DefaultPluginManager.getConfiguredMojo( .. ) )
        && call( Object PlexusContainer+.lookup( .. ) )
    {
        getReporter().reportMojoLookupError( binding, project, cause );
    }

    Parameter currentParameter;

    private pointcut paramGetName( Parameter parameter ):
        call( String Parameter.getName() )
        && target( parameter );

    private pointcut pm_validatePomConfig():
        execution( void DefaultPluginManager.validatePomConfiguration( .. ) );

    private pointcut within_pm_validatePomConfig():
        withincode( void DefaultPluginManager.validatePomConfiguration( .. ) );

    private pointcut pm_checkRequiredParameters():
        execution( void DefaultPluginManager.checkRequiredParameters( .. ) );

    private pointcut within_pm_checkRequiredParameters():
        withincode( void DefaultPluginManager.checkRequiredParameters( .. ) );

    before( Parameter parameter ):
        ( within_pm_validatePomConfig()
          || within_pm_checkRequiredParameters() )
        && paramGetName( parameter )
    {
        currentParameter = parameter;
    }

    after() returning:
        pm_validatePomConfig() ||
        pm_checkRequiredParameters()
    {
        currentParameter = null;
    }

    private pointcut pm_executeMojoWithSessionAndExec( MavenProject project, MojoExecution exec, MavenSession session, DefaultPluginManager manager ):
        execution( void DefaultPluginManager.executeMojo( MavenProject, MojoExecution, MavenSession ) )
        && args( project, exec, session )
        && this( manager );

    after( MojoBinding binding, MavenProject project, MojoExecution exec, MavenSession session, DefaultPluginManager manager ) throwing( PluginConfigurationException cause ):
        cflow( le_executeGoalAndHandleFailures( binding ) )
        && cflow( pm_executeMojoWithSessionAndExec( project, exec, session, manager ) )
        && pm_validatePomConfig()
    {
        PathTranslator translator = manager.pathTranslator;
        Logger logger = new ConsoleLogger( Logger.LEVEL_INFO, "error reporting" );
        getReporter().reportAttemptToOverrideUneditableMojoParameter( currentParameter, binding, project, session, exec, translator, logger, cause );
    }

    PluginParameterException around( MojoBinding binding, MavenProject project, List invalidParameters ):
        cflow( le_executeGoalAndHandleFailures( binding ) )
        && cflow( pm_executeMojo( project ) )
        && cflow( pm_checkRequiredParameters() )
        && call( PluginParameterException.new( .., List ) )
        && args( .., invalidParameters )
    {
        PluginParameterException err = proceed( binding, project, invalidParameters );

        getReporter().reportMissingRequiredMojoParameter( binding, project, invalidParameters, err );

        return err;
    }

    private pointcut ppee_evaluate( String expression ):
        execution( Object PluginParameterExpressionEvaluator.evaluate( String ) )
        && args( expression );

    private pointcut within_ppee_evaluate( String expression ):
        withincode( Object PluginParameterExpressionEvaluator.evaluate( String ) )
        && args( expression );

    before( MojoBinding binding, MavenProject project, String expression, ExpressionEvaluationException err ):
        cflow( le_executeGoalAndHandleFailures( binding ) )
        && cflow( pm_executeMojo( project ) )
        && cflow( pm_checkRequiredParameters() )
        && cflow( ppee_evaluate( expression ) )
        && execution( ExpressionEvaluationException.new( String ) )
        && this( err )
    {
        getReporter().reportUseOfBannedMojoParameter( currentParameter,
                                                      binding,
                                                      project,
                                                      expression,
                                                      (String) PluginParameterExpressionEvaluator.BANNED_EXPRESSIONS.get( expression ),
                                                      err );
    }

    before( MojoBinding binding, MavenProject project, String expression, Exception cause ):
        cflow( le_executeGoalAndHandleFailures( binding ) )
        && cflow( pm_executeMojo( project ) )
        && cflow( pm_checkRequiredParameters() )
        && cflow( ppee_evaluate( expression ) )
        && within( PluginParameterExpressionEvaluator )
        && handler( Exception )
        && args( cause )
    {
        getReporter().reportReflectionErrorWhileEvaluatingMojoParameter( currentParameter,
                                                      binding,
                                                      project,
                                                      expression,
                                                      cause );
    }

    after( MojoBinding binding, MavenProject project, PlexusConfiguration config ) throwing( PluginConfigurationException cause ):
        cflow( le_executeGoalAndHandleFailures( binding ) )
        && cflow( pm_executeMojo( project ) )
        && execution( void DefaultPluginManager.populatePluginFields( *, *, PlexusConfiguration, .. ) )
        && args( *, *, config, .. )
    {
        getReporter().reportErrorApplyingMojoConfiguration( binding, project, config, cause );
    }

    private pointcut pm_resolveTransitiveDependencies( MavenProject project, String scope ):
        execution( void DefaultPluginManager.resolveTransitiveDependencies( *, *, String, *, MavenProject ) )
        && args( *, *, scope, *, project );

    after( MavenProject project, String scope ) throwing( ArtifactNotFoundException cause ):
        pm_resolveTransitiveDependencies( project, scope )
    {
        getReporter().reportProjectDependenciesNotFound( project, scope, cause );
    }

    after( MavenProject project, String scope ) throwing( ArtifactResolutionException cause ):
        pm_resolveTransitiveDependencies( project, scope )
    {
        if ( cause instanceof MultipleArtifactsNotFoundException )
        {
            getReporter().reportProjectDependenciesNotFound( project, scope, (MultipleArtifactsNotFoundException) cause );
        }
        else
        {
            getReporter().reportProjectDependenciesUnresolvable( project, scope, cause );
        }
    }

    private pointcut le_getLifecycleBindings( List tasks, MavenProject configuringProject, String targetDescription ):
        execution( List DefaultLifecycleExecutor.getLifecycleBindings( List, MavenProject, *, String ) )
        && args( tasks, configuringProject, *, targetDescription );

    before( List tasks, MavenProject configuringProject, String targetDescription, LifecycleException cause ):
        cflow( le_getLifecycleBindings( tasks, configuringProject, targetDescription ) )
        && call( LifecycleExecutionException.new( .., LifecycleException ) )
        && args( .., cause )
    {
        getReporter().reportErrorFormulatingBuildPlan( tasks, configuringProject, targetDescription, cause );
    }

}


Cleaned up some imports.

Cleaned up some imports.

package org.springframework.persistence.graph.neo4j;

import java.lang.reflect.Field;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;

import org.aspectj.lang.reflect.FieldSignature;
import org.neo4j.graphdb.Direction;
import org.neo4j.graphdb.DynamicRelationshipType;
import org.neo4j.graphdb.GraphDatabaseService;
import org.neo4j.graphdb.Node;
import org.neo4j.graphdb.NotInTransactionException;
import org.neo4j.graphdb.RelationshipType;
import org.neo4j.util.GraphDatabaseUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.InvalidDataAccessApiUsageException;
import org.springframework.dao.InvalidDataAccessResourceUsageException;
import org.springframework.persistence.graph.GraphEntity;
import org.springframework.persistence.graph.Relationship;
import org.springframework.persistence.support.AbstractTypeAnnotatingMixinFields;
import org.springframework.persistence.support.EntityInstantiator;

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

	//-------------------------------------------------------------------------
	// Configure aspect for whole system.
	// init() method can be invoked automatically if the aspect is a Spring
	// bean, or called in user code.
	//-------------------------------------------------------------------------
	// Aspect shared Neo4J Graph Database Service
	private GraphDatabaseService graphDatabaseService;

	private EntityInstantiator<NodeBacked,Node> graphEntityInstantiator;

	private GraphDatabaseUtil graphDatabaseUtil;

	private RelationshipInfoFactory relationshipInfoFactory;

	@Autowired
	public void init(GraphDatabaseService gds, EntityInstantiator<NodeBacked, Node> gei) {
		this.graphDatabaseService = gds;
		this.graphEntityInstantiator = gei;
		this.graphDatabaseUtil = new GraphDatabaseUtil(gds);
		this.relationshipInfoFactory = new RelationshipInfoFactory(graphEntityInstantiator);
	}

	//-------------------------------------------------------------------------
	// Advise user-defined constructors of NodeBacked objects to create a new
	// Neo4J backing node
	//-------------------------------------------------------------------------
	pointcut arbitraryUserConstructorOfNodeBackedObject(NodeBacked entity) :
		execution((@GraphEntity *).new(..)) &&
		!execution((@GraphEntity *).new(Node)) &&
		this(entity);

	// Create a new node in the Graph if no Node was passed in a constructor
	before(NodeBacked entity) : arbitraryUserConstructorOfNodeBackedObject(entity) {
		try {
			entity.setUnderlyingNode(graphDatabaseService.createNode());
			log.info("User-defined constructor called on class " + entity.getClass() + "; created Node [" + entity.getUnderlyingNode() +"]; " +
					"Updating metamodel");
			// TODO pull naming out into a strategy interface, todo a separate one, or the Entity Instatiator
			// graphEntityInstantiator.postEntityCreation(entity);
			postEntityCreation(entity);
		} catch(NotInTransactionException e) {
			throw new InvalidDataAccessResourceUsageException("Not in a Neo4j transaction.", e);
		}
	}

	private void postEntityCreation(NodeBacked entity) {
		Node subReference = Neo4jHelper.findSubreferenceNode(entity.getClass(), graphDatabaseService);
		entity.getUnderlyingNode().createRelationshipTo(subReference, Neo4jHelper.INSTANCE_OF_RELATIONSHIP_TYPE);
		graphDatabaseUtil.incrementAndGetCounter(subReference, Neo4jHelper.SUBREFERENCE_NODE_COUNTER_KEY);
	}

	// Introduced field
	private Node NodeBacked.underlyingNode;

	public void NodeBacked.setUnderlyingNode(Node n) {
		this.underlyingNode = n;
	}

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

	//-------------------------------------------------------------------------
	// Equals and hashCode for Neo4j entities.
	// Final to prevent overriding.
	//-------------------------------------------------------------------------
	// TODO could use template method for further checks if needed
	public final boolean NodeBacked.equals(Object obj) {
		if (obj instanceof NodeBacked) {
			return this.getUnderlyingNode().equals(((NodeBacked) obj).getUnderlyingNode());
		}
		return false;
	}

	public final int NodeBacked.hashCode() {
		return getUnderlyingNode().hashCode();
	}

	Object around(NodeBacked entity) : entityFieldGet(entity) {
		FieldSignature fieldSignature=(FieldSignature) thisJoinPoint.getSignature();
		Field f = fieldSignature.getField();

		// TODO fix arrays, TODO serialize other types as byte[] or string (for indexing, querying) via Annotation
		if (f.getType().isPrimitive() || f.getType().equals(String.class)) {
			String propName = getNeo4jPropertyName(f);
			log.info("GET " + f + " <- Neo4J simple node property [" + propName + "]");
			return entity.getUnderlyingNode().getProperty(propName, null);
		}

		RelationshipInfo relInfo = relationshipInfoFactory.forField(f);
		Object obj = relInfo.readObject(entity);
		if (obj == null) {
			log.info("Ignored GET " + f + ": " + f.getType().getName() + " not primitive or GraphEntity");
			return proceed(entity);
		}
		return obj;
	}

	Object around(NodeBacked entity, Object newVal) : entityFieldSet(entity, newVal) {
		try {
			FieldSignature fieldSignature=(FieldSignature) thisJoinPoint.getSignature();
			Field f = fieldSignature.getField();
			// TODO fix arrays
			if (f.getType().isPrimitive() || f.getType().equals(String.class)) {
				String propName = getNeo4jPropertyName(f);
				entity.getUnderlyingNode().setProperty(propName, newVal);
				log.info("SET " + f + " -> Neo4J simple node property [" + propName + "] with value=[" + newVal + "]");
				return null;
			}

			RelationshipInfo relInfo = relationshipInfoFactory.forField(f);
			if (relInfo == null) {
				log.info("Ignored SET " + f + ": " + f.getType().getName() + " not primitive or GraphEntity");
				return proceed(entity, newVal);
			}
			log.info("SET " + f + " -> Neo4J relationship with value=[" + newVal + "]");
			relInfo.apply(entity, newVal);
			return null;
		} catch(NotInTransactionException e) {
			throw new InvalidDataAccessResourceUsageException("Not in a Neo4j transaction.", e);
		}
	}

	private boolean isSingleRelationshipField(Field f) {
		return f.getType().isAnnotationPresent(GraphEntity.class);
		//return NodeBacked.class.isAssignableFrom(f.getType());
	}

	private static String getNeo4jPropertyName(Field field) {
		return String.format("%s.%s",field.getDeclaringClass().getSimpleName(),field.getName());
	}

	public static interface RelationshipInfo {
		// Set entity field to newVal
		void apply(NodeBacked entity, Object newVal);

		// Read object from entity field
		Object readObject(NodeBacked entity);
	}

	public static class RelationshipInfoFactory {
		private final EntityInstantiator<NodeBacked, Node> graphEntityInstantiator;

		public RelationshipInfoFactory(EntityInstantiator<NodeBacked,Node> graphEntityInstantiator) {
			this.graphEntityInstantiator = graphEntityInstantiator;
		}

		public RelationshipInfo forField(Field field) {
			if (isSingleRelationshipField(field)) {
				final Relationship relAnnotation = field.getAnnotation(Relationship.class);
				if (relAnnotation != null) {
					return new SingleRelationshipInfo(DynamicRelationshipType.withName(relAnnotation.type()),
							relAnnotation.direction().toNeo4jDir(), field.getType(), graphEntityInstantiator);
				}
				return new SingleRelationshipInfo(DynamicRelationshipType.withName(getNeo4jPropertyName(field)),
						Direction.OUTGOING, field.getType(), graphEntityInstantiator);
			}
			if (isOneToNRelationshipField(field)) {
				final Relationship relAnnotation = field.getAnnotation(Relationship.class);
				return new OneToNRelationshipInfo(DynamicRelationshipType.withName(relAnnotation.type()),
						relAnnotation.direction().toNeo4jDir(), relAnnotation.elementClass(), graphEntityInstantiator);
			}
			throw new IllegalArgumentException("Not a Neo4j relationship field.");
		}

		private static boolean isSingleRelationshipField(Field f) {
//			return f.getType().isAnnotationPresent(GraphEntity.class);
			return NodeBacked.class.isAssignableFrom(f.getType());
		}

		private static boolean isOneToNRelationshipField(Field f) {
			return f.isAnnotationPresent(Relationship.class)
				&& !Void.class.isAssignableFrom(f.getAnnotation(Relationship.class).elementClass())
				&& Collection.class.isAssignableFrom(f.getType());
		}
	}

	public static class SingleRelationshipInfo implements RelationshipInfo {

		private final RelationshipType type;
		private final Direction direction;
		private final Class<?> clazz;
		private final EntityInstantiator<NodeBacked, Node> graphEntityInstantiator;

		public SingleRelationshipInfo(RelationshipType type, Direction direction, Class<?> clazz, EntityInstantiator<NodeBacked,Node> graphEntityInstantiator) {
			this.type = type;
			this.direction = direction;
			this.clazz = clazz;
			this.graphEntityInstantiator = graphEntityInstantiator;
		}

		public void apply(NodeBacked entity, Object newVal) {
			if (newVal != null && !(newVal instanceof NodeBacked)) {
				throw new IllegalArgumentException("New value must be NodeBacked.");
			}
			Node entityNode = entity.getUnderlyingNode();
			for ( org.neo4j.graphdb.Relationship relationship : entityNode.getRelationships(type, direction) ) {
				relationship.delete();
			}
			if (newVal == null) {
				return;
			}
			Node targetNode = ((NodeBacked) newVal).getUnderlyingNode();
			if (entityNode.equals(targetNode)) {
				throw new InvalidDataAccessApiUsageException("Cannot create circular reference.");
			}
			switch(direction) {
				case OUTGOING : entityNode.createRelationshipTo(targetNode, type); break;
				case INCOMING : targetNode.createRelationshipTo(entityNode, type); break;
				default : throw new IllegalArgumentException("invalid direction " + direction);
			}
		}

		@Override
		public Object readObject(NodeBacked entity) {
			// does it have to be there, isn't it enough to have a nodebacked as target?
			Node entityNode = entity.getUnderlyingNode();
			if (entityNode == null) {
				throw new IllegalStateException("Entity must have a backing Node");
			}
			org.neo4j.graphdb.Relationship singleRelationship = entityNode.getSingleRelationship(type, direction);

			// TODO is this correct
			// [mh] i assume only for null
			if (singleRelationship == null) {
				return null;
			}
			Node targetNode = singleRelationship.getOtherNode(entityNode);
//			log.info("GET " + f + ": " + f.getType().getName() + ": setting from relationship " + singleRelationship);
			return graphEntityInstantiator.createEntityFromState(targetNode, (Class<NodeBacked>) clazz);
		}

	}

	public static class OneToNRelationshipInfo implements RelationshipInfo {

		private final RelationshipType type;
		private final Direction direction;
		private final Class<?> elementClass;
		private final EntityInstantiator<NodeBacked, Node> graphEntityInstantiator;

		public OneToNRelationshipInfo(RelationshipType type, Direction direction, Class<?> elementClass, EntityInstantiator<NodeBacked, Node> graphEntityInstantiator) {
			this.type = type;
			this.direction = direction;
			this.elementClass = elementClass;
			this.graphEntityInstantiator = graphEntityInstantiator;
		}

		public void apply(NodeBacked entity, Object newVal) {
			Node entityNode = entity.getUnderlyingNode();

			if (newVal != null) {
				if (!(newVal instanceof Set)) {
					throw new IllegalArgumentException("New value must be a Set, was: " + newVal.getClass());
				}
				Set<Object> set = (Set<Object>) newVal;
				for (Object obj : set) {
					if (!(obj instanceof NodeBacked)) {
						throw new IllegalArgumentException("New value elements must be NodeBacked.");
					}
					if (entityNode.equals(((NodeBacked) obj).getUnderlyingNode())) {
						throw new InvalidDataAccessApiUsageException("Cannot create circular reference.");
					}
				}
			}
			for ( org.neo4j.graphdb.Relationship relationship : entityNode.getRelationships(type, direction) ) {
				relationship.delete();
			}
			if (newVal == null) {
				return;
			}

			for (Object obj : (Set<Object>) newVal) {
				NodeBacked nb = (NodeBacked) obj;
				Node targetNode = nb.getUnderlyingNode();
				switch(direction) {
					case OUTGOING : entityNode.createRelationshipTo(targetNode, type); break;
					case INCOMING : targetNode.createRelationshipTo(entityNode, type); break;
					default : throw new IllegalArgumentException("invalid direction " + direction);
				}
			}

		}

		@Override
		public Object readObject(NodeBacked entity) {
			Node entityNode = entity.getUnderlyingNode();
			if (entityNode == null) {
				throw new IllegalStateException("Entity must have a backing Node");
			}
			Iterable<org.neo4j.graphdb.Relationship> rels = entityNode.getRelationships(type, direction);
			Set<Object> result = new HashSet<Object>();
			for (org.neo4j.graphdb.Relationship rel : rels) {
				result.add(graphEntityInstantiator.createEntityFromState(rel.getOtherNode(entityNode), (Class<NodeBacked>) elementClass));
			}
			return result;
		}

	}

}

package org.springframework.persistence.graph.neo4j;

import java.lang.reflect.Field;
import java.lang.reflect.Type;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.reflect.FieldSignature;
import org.neo4j.graphdb.Direction;
import org.neo4j.graphdb.DynamicRelationshipType;
import org.neo4j.graphdb.GraphDatabaseService;
import org.neo4j.graphdb.Node;
import org.neo4j.graphdb.NotInTransactionException;
import org.neo4j.graphdb.RelationshipType;
import org.neo4j.util.GraphDatabaseUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.InvalidDataAccessApiUsageException;
import org.springframework.dao.InvalidDataAccessResourceUsageException;
import org.springframework.persistence.graph.GraphEntity;
import org.springframework.persistence.graph.Relationship;
import org.springframework.persistence.graph.neo4j.Neo4jNodeBacking.RelationshipInfoFactory;
import org.springframework.persistence.support.AbstractTypeAnnotatingMixinFields;
import org.springframework.persistence.support.EntityInstantiator;

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

	//-------------------------------------------------------------------------
	// Configure aspect for whole system.
	// init() method can be invoked automatically if the aspect is a Spring
	// bean, or called in user code.
	//-------------------------------------------------------------------------
	// Aspect shared Neo4J Graph Database Service
	private GraphDatabaseService graphDatabaseService;

	private EntityInstantiator<NodeBacked,Node> graphEntityInstantiator;

	private GraphDatabaseUtil graphDatabaseUtil;

	private RelationshipInfoFactory relationshipInfoFactory;

	@Autowired
	public void init(GraphDatabaseService gds, EntityInstantiator<NodeBacked, Node> gei) {
		this.graphDatabaseService = gds;
		this.graphEntityInstantiator = gei;
		this.graphDatabaseUtil = new GraphDatabaseUtil(gds);
		this.relationshipInfoFactory = new RelationshipInfoFactory(graphEntityInstantiator);
	}

	//-------------------------------------------------------------------------
	// Advise user-defined constructors of NodeBacked objects to create a new
	// Neo4J backing node
	//-------------------------------------------------------------------------
	pointcut arbitraryUserConstructorOfNodeBackedObject(NodeBacked entity) :
		execution((@GraphEntity *).new(..)) &&
		!execution((@GraphEntity *).new(Node)) &&
		this(entity);

	// Create a new node in the Graph if no Node was passed in a constructor
	before(NodeBacked entity) : arbitraryUserConstructorOfNodeBackedObject(entity) {
		try {
			entity.setUnderlyingNode(graphDatabaseService.createNode());
			log.info("User-defined constructor called on class " + entity.getClass() + "; created Node [" + entity.getUnderlyingNode() +"]; " +
					"Updating metamodel");
			// TODO pull naming out into a strategy interface, todo a separate one, or the Entity Instatiator
			// graphEntityInstantiator.postEntityCreation(entity);
			postEntityCreation(entity);
		} catch(NotInTransactionException e) {
			throw new InvalidDataAccessResourceUsageException("Not in a Neo4j transaction.", e);
		}
	}

	private void postEntityCreation(NodeBacked entity) {
		Node subReference = Neo4jHelper.findSubreferenceNode(entity.getClass(), graphDatabaseService);
		entity.getUnderlyingNode().createRelationshipTo(subReference, Neo4jHelper.INSTANCE_OF_RELATIONSHIP_TYPE);
		graphDatabaseUtil.incrementAndGetCounter(subReference, Neo4jHelper.SUBREFERENCE_NODE_COUNTER_KEY);
	}

	// Introduced field
	private Node NodeBacked.underlyingNode;

	public void NodeBacked.setUnderlyingNode(Node n) {
		this.underlyingNode = n;
	}

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

	//-------------------------------------------------------------------------
	// Equals and hashCode for Neo4j entities.
	// Final to prevent overriding.
	//-------------------------------------------------------------------------
	// TODO could use template method for further checks if needed
	public final boolean NodeBacked.equals(Object obj) {
		if (obj instanceof NodeBacked) {
			return this.getUnderlyingNode().equals(((NodeBacked) obj).getUnderlyingNode());
		}
		return false;
	}

	public final int NodeBacked.hashCode() {
		return getUnderlyingNode().hashCode();
	}

	Object around(NodeBacked entity) : entityFieldGet(entity) {
		FieldSignature fieldSignature=(FieldSignature) thisJoinPoint.getSignature();
		Field f = fieldSignature.getField();

		// TODO fix arrays, TODO serialize other types as byte[] or string (for indexing, querying) via Annotation
		if (f.getType().isPrimitive() || f.getType().equals(String.class)) {
			String propName = getNeo4jPropertyName(f);
			log.info("GET " + f + " <- Neo4J simple node property [" + propName + "]");
			return entity.getUnderlyingNode().getProperty(propName, null);
		}

		RelationshipInfo relInfo = relationshipInfoFactory.forField(f);
		Object obj = relInfo.readObject(entity);
		if (obj == null) {
			log.info("Ignored GET " + f + ": " + f.getType().getName() + " not primitive or GraphEntity");
			return proceed(entity);
		}
		return obj;
	}

	Object around(NodeBacked entity, Object newVal) : entityFieldSet(entity, newVal) {
		try {
			FieldSignature fieldSignature=(FieldSignature) thisJoinPoint.getSignature();
			Field f = fieldSignature.getField();
			// TODO fix arrays
			if (f.getType().isPrimitive() || f.getType().equals(String.class)) {
				String propName = getNeo4jPropertyName(f);
				entity.getUnderlyingNode().setProperty(propName, newVal);
				log.info("SET " + f + " -> Neo4J simple node property [" + propName + "] with value=[" + newVal + "]");
				return null;
			}

			RelationshipInfo relInfo = relationshipInfoFactory.forField(f);
			if (relInfo == null) {
				log.info("Ignored SET " + f + ": " + f.getType().getName() + " not primitive or GraphEntity");
				return proceed(entity, newVal);
			}
			log.info("SET " + f + " -> Neo4J relationship with value=[" + newVal + "]");
			relInfo.apply(entity, newVal);
			return null;
		} catch(NotInTransactionException e) {
			throw new InvalidDataAccessResourceUsageException("Not in a Neo4j transaction.", e);
		}
	}

	private boolean isSingleRelationshipField(Field f) {
		return f.getType().isAnnotationPresent(GraphEntity.class);
		//return NodeBacked.class.isAssignableFrom(f.getType());
	}

	private static String getNeo4jPropertyName(Field field) {
		return String.format("%s.%s",field.getDeclaringClass().getSimpleName(),field.getName());
	}

	public static interface RelationshipInfo {
		// Set entity field to newVal
		void apply(NodeBacked entity, Object newVal);

		// Read object from entity field
		Object readObject(NodeBacked entity);
	}

	public static class RelationshipInfoFactory {
		private final EntityInstantiator<NodeBacked, Node> graphEntityInstantiator;

		public RelationshipInfoFactory(EntityInstantiator<NodeBacked,Node> graphEntityInstantiator) {
			this.graphEntityInstantiator = graphEntityInstantiator;
		}

		public RelationshipInfo forField(Field field) {
			if (isSingleRelationshipField(field)) {
				final Relationship relAnnotation = field.getAnnotation(Relationship.class);
				if (relAnnotation != null) {
					return new SingleRelationshipInfo(DynamicRelationshipType.withName(relAnnotation.type()),
							relAnnotation.direction().toNeo4jDir(), field.getType(), graphEntityInstantiator);
				}
				return new SingleRelationshipInfo(DynamicRelationshipType.withName(getNeo4jPropertyName(field)),
						Direction.OUTGOING, field.getType(), graphEntityInstantiator);
			}
			if (isOneToNRelationshipField(field)) {
				final Relationship relAnnotation = field.getAnnotation(Relationship.class);
				return new OneToNRelationshipInfo(DynamicRelationshipType.withName(relAnnotation.type()),
						relAnnotation.direction().toNeo4jDir(), relAnnotation.elementClass(), graphEntityInstantiator);
			}
			throw new IllegalArgumentException("Not a Neo4j relationship field.");
		}

		private static boolean isSingleRelationshipField(Field f) {
//			return f.getType().isAnnotationPresent(GraphEntity.class);
			return NodeBacked.class.isAssignableFrom(f.getType());
		}

		private static boolean isOneToNRelationshipField(Field f) {
			return f.isAnnotationPresent(Relationship.class)
				&& !Void.class.isAssignableFrom(f.getAnnotation(Relationship.class).elementClass())
				&& Collection.class.isAssignableFrom(f.getType());
		}
	}

	public static class SingleRelationshipInfo implements RelationshipInfo {

		private final RelationshipType type;
		private final Direction direction;
		private final Class<?> clazz;
		private final EntityInstantiator<NodeBacked, Node> graphEntityInstantiator;

		public SingleRelationshipInfo(RelationshipType type, Direction direction, Class<?> clazz, EntityInstantiator<NodeBacked,Node> graphEntityInstantiator) {
			this.type = type;
			this.direction = direction;
			this.clazz = clazz;
			this.graphEntityInstantiator = graphEntityInstantiator;
		}

		public void apply(NodeBacked entity, Object newVal) {
			if (newVal != null && !(newVal instanceof NodeBacked)) {
				throw new IllegalArgumentException("New value must be NodeBacked.");
			}
			Node entityNode = entity.getUnderlyingNode();
			for ( org.neo4j.graphdb.Relationship relationship : entityNode.getRelationships(type, direction) ) {
				relationship.delete();
			}
			if (newVal == null) {
				return;
			}
			Node targetNode = ((NodeBacked) newVal).getUnderlyingNode();
			if (entityNode.equals(targetNode)) {
				throw new InvalidDataAccessApiUsageException("Cannot create circular reference.");
			}
			switch(direction) {
				case OUTGOING : entityNode.createRelationshipTo(targetNode, type); break;
				case INCOMING : targetNode.createRelationshipTo(entityNode, type); break;
				default : throw new IllegalArgumentException("invalid direction " + direction);
			}
		}

		@Override
		public Object readObject(NodeBacked entity) {
			// does it have to be there, isn't it enough to have a nodebacked as target?
			Node entityNode = entity.getUnderlyingNode();
			if (entityNode == null) {
				throw new IllegalStateException("Entity must have a backing Node");
			}
			org.neo4j.graphdb.Relationship singleRelationship = entityNode.getSingleRelationship(type, direction);

			// TODO is this correct
			// [mh] i assume only for null
			if (singleRelationship == null) {
				return null;
			}
			Node targetNode = singleRelationship.getOtherNode(entityNode);
//			log.info("GET " + f + ": " + f.getType().getName() + ": setting from relationship " + singleRelationship);
			return graphEntityInstantiator.createEntityFromState(targetNode, (Class<NodeBacked>) clazz);
		}

	}

	public static class OneToNRelationshipInfo implements RelationshipInfo {

		private final RelationshipType type;
		private final Direction direction;
		private final Class<?> elementClass;
		private final EntityInstantiator<NodeBacked, Node> graphEntityInstantiator;

		public OneToNRelationshipInfo(RelationshipType type, Direction direction, Class<?> elementClass, EntityInstantiator<NodeBacked, Node> graphEntityInstantiator) {
			this.type = type;
			this.direction = direction;
			this.elementClass = elementClass;
			this.graphEntityInstantiator = graphEntityInstantiator;
		}

		public void apply(NodeBacked entity, Object newVal) {
			Node entityNode = entity.getUnderlyingNode();

			if (newVal != null) {
				if (!(newVal instanceof Set)) {
					throw new IllegalArgumentException("New value must be a Set, was: " + newVal.getClass());
				}
				Set<Object> set = (Set<Object>) newVal;
				for (Object obj : set) {
					if (!(obj instanceof NodeBacked)) {
						throw new IllegalArgumentException("New value elements must be NodeBacked.");
					}
					if (entityNode.equals(((NodeBacked) obj).getUnderlyingNode())) {
						throw new InvalidDataAccessApiUsageException("Cannot create circular reference.");
					}
				}
			}
			for ( org.neo4j.graphdb.Relationship relationship : entityNode.getRelationships(type, direction) ) {
				relationship.delete();
			}
			if (newVal == null) {
				return;
			}

			for (Object obj : (Set<Object>) newVal) {
				NodeBacked nb = (NodeBacked) obj;
				Node targetNode = nb.getUnderlyingNode();
				switch(direction) {
					case OUTGOING : entityNode.createRelationshipTo(targetNode, type); break;
					case INCOMING : targetNode.createRelationshipTo(entityNode, type); break;
					default : throw new IllegalArgumentException("invalid direction " + direction);
				}
			}

		}

		@Override
		public Object readObject(NodeBacked entity) {
			Node entityNode = entity.getUnderlyingNode();
			if (entityNode == null) {
				throw new IllegalStateException("Entity must have a backing Node");
			}
			Iterable<org.neo4j.graphdb.Relationship> rels = entityNode.getRelationships(type, direction);
			Set<Object> result = new HashSet<Object>();
			for (org.neo4j.graphdb.Relationship rel : rels) {
				result.add(graphEntityInstantiator.createEntityFromState(rel.getOtherNode(entityNode), (Class<NodeBacked>) elementClass));
			}
			return result;
		}

	}

}


Don't allow akka internal `ask` timer to expire

package sample;

import static java.lang.Thread.sleep;

import akka.dispatch.verification.Instrumenter;
import akka.dispatch.verification.WrappedCancellable;

import akka.actor.ActorRef;
import akka.actor.ScalaActorRef;
import akka.actor.InternalActorRef;
import akka.actor.Actor;
import akka.actor.Props;
import akka.actor.ActorCell;
import akka.actor.ActorSystem;
import akka.actor.ActorSystemImpl;
import akka.actor.ActorContext;
import akka.actor.Scheduler;
import akka.actor.Cancellable;
import akka.actor.LightArrayRevolverScheduler;
import akka.actor.LocalActorRefProvider;
import akka.actor.VirtualPathContainer;
import akka.actor.ActorPath;

import akka.pattern.AskSupport;
import akka.pattern.PromiseActorRef;

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

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

privileged public aspect WeaveActor {
  Instrumenter inst = Instrumenter.apply();

  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);
  }

  after(Mailbox me):
  execution(* akka.dispatch.Mailbox.run()) && this(me) {
    inst.mailboxIdle(me);
  }

  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;
  }

  // Interposition on `ask`. See this doc for more details:
  // https://docs.google.com/document/d/1_LUceHvQoamlBtNNbqA4CxBH-zvUKlZhnSjLwTc16q4/edit#
  pointcut receiveAnswer(PromiseActorRef me, Object message, ActorRef sender):
  execution(public void akka.pattern.PromiseActorRef.$bang(Object, ActorRef)) &&
  args(message, sender) && this(me);

  Object around(PromiseActorRef me, Object message, ActorRef sender):
  receiveAnswer(me, message, sender) {
    if (inst.receiveAskAnswer(me, message, sender)) {
      Object ret = proceed(me, message, sender);
      inst.afterReceiveAskAnswer(me, message, sender);
      return ret;
    }
    return null;
  }

  // Sanity check: make sure we don't allocate two temp actors at the same
  // time with the same name.
  before(VirtualPathContainer me, String name, InternalActorRef ref):
  execution(* VirtualPathContainer.addChild(String, InternalActorRef)) &&
  this(me) && args(name, ref) {
    if (me.children.containsKey(name)) {
      System.err.println("WARNING: temp name already taken: " + name);
    }
  }

  /*
   For debugging redundant temp names:
  before(VirtualPathContainer me, String name, InternalActorRef ref):
  execution(* VirtualPathContainer.removeChild(String, InternalActorRef)) &&
  this(me) && args(name, ref) {
    System.out.println("removeChild: " + name);
  }

  before(VirtualPathContainer me, String name):
  execution(* VirtualPathContainer.removeChild(String)) &&
  this(me) && args(name) {
    System.out.println("removeChild: " + name);
  }
  */

  // Interposition on the code that assigns IDs to temporary actors. See this
  // doc for more details:
  // https://docs.google.com/document/d/1rAM8EEy3WnLRhhPROvHmBhAREv0rmihz0Gw0GgF1xC4/edit#
  pointcut tempPath(LocalActorRefProvider me):
  execution(public ActorPath LocalActorRefProvider.tempPath()) && this(me);

  Object around(LocalActorRefProvider me): tempPath(me) {
    return inst.assignTempPath(me.tempNode);
  }

  // newActor is invoked before preStart is called!
  after(ActorCell me):
  execution(* akka.actor.ActorCell.newActor()) &&
  this(me) {
    inst.new_actor(me.system(), me.props, me.self);
  }

  // after preStart has been called, i.e. actor is ready to receive messages.
  after(ActorCell me):
  execution(* akka.actor.ActorCell.create(..)) &&
  this(me) {
    inst.preStartCalled(me.self);
  }

  // Block until the actor has actually been created and preStart has been invoked!
  // (which is done asynchronously)
  after(ActorSystem me, Props props) returning(ActorRef actor):
  execution(ActorRef akka.actor.ActorSystem.actorOf(Props)) &&
  args(props) && this(me) {
    inst.blockUntilPreStartCalled(actor);
  }

  // Block until the actor has actually been created and preStart has been invoked!
  // (which is done asynchronously)
  after(ActorSystem me, Props props, String name) returning(ActorRef actor):
  execution(ActorRef akka.actor.ActorSystem.actorOf(Props, String)) &&
  args(props, name) && this(me) {
    inst.blockUntilPreStartCalled(actor);
  }

  // Block until the actor has actually been created and preStart has been invoked!
  // (which is done asynchronously)
  after(ActorContext me, Props props) returning(ActorRef actor):
  execution(ActorRef akka.actor.ActorContext.actorOf(Props)) &&
  args(props) && this(me) {
    inst.blockUntilPreStartCalled(actor);
  }

  // Block until the actor has actually been created and preStart has been invoked!
  // (which is done asynchronously)
  after(ActorContext me, Props props, String name) returning(ActorRef actor):
  execution(ActorRef akka.actor.ActorContext.actorOf(Props, String)) &&
  args(props, name) && this(me) {
    inst.blockUntilPreStartCalled(actor);
  }

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

  // 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) {
    if (!inst.actorKnown(receiver)) {
      return proceed(me,delay,receiver,msg,exc,sender);
    }
    class MyRunnable implements java.lang.Runnable {
      // Make it a no-op!
      public void run() {
      }
    }
    MyRunnable runnable = new MyRunnable();
    Cancellable c = new WrappedCancellable(
      me.scheduleOnce(delay, runnable, exc), receiver.path().name(), msg);
    inst.registerCancellable(c, false, receiver.path().name(), msg);
    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) {
    if (!inst.actorKnown(receiver)) {
      return proceed(me,delay,interval,receiver,msg,exc,sender);
    }
    class MyRunnable implements java.lang.Runnable {
      // Make it a no-op!
      public void run() {
      }
    }
    MyRunnable runnable = new MyRunnable();
    Cancellable c = new WrappedCancellable(
      me.schedule(delay, interval, runnable, exc), receiver.path().name(), msg);
    inst.registerCancellable(c, true, receiver.path().name(), msg);
    return c;
  }

  // Override akka.actor.Scheduler.scheduler(block)
  pointcut scheduleBlock(LightArrayRevolverScheduler me, FiniteDuration delay, FiniteDuration interval, scala.Function0<scala.runtime.BoxedUnit> block, ExecutionContext exc):
  execution(public * schedule(scala.concurrent.duration.FiniteDuration, scala.concurrent.duration.FiniteDuration, scala.Function0<scala.runtime.BoxedUnit>, scala.concurrent.ExecutionContext)) &&
  args(delay, interval, block, exc) && this(me);

  // Wrap the function in a special "Message" wrapper, that appears to
  // STS2 schedulers like a message, but which the Instrumenter understands as a
  // function to be invoked rather than a message to be sent.
  // TODO(cs): issue: no receiver.
  Object around(LightArrayRevolverScheduler me, FiniteDuration delay, FiniteDuration interval, scala.Function0<scala.runtime.BoxedUnit> block, ExecutionContext exc):
  scheduleBlock(me, delay, interval, block, exc) {
    if (!inst.actorSystemInitialized() || Instrumenter.akkaInternalCodeBlockSchedule()) {
      return proceed(me, delay, interval, block, exc);
    }

    class MyRunnable implements java.lang.Runnable {
      // Make it a no-op!
      public void run() {
      }
    }
    MyRunnable runnable = new MyRunnable();
    Cancellable c = new WrappedCancellable(me.schedule(delay, interval, runnable, exc), "ScheduleFunction", block);
    inst.registerCancellable(c, true, "ScheduleFunction", block);
    return c;
  }

  // Override akka.actor.Scheduler.scheduler(block)
  pointcut scheduleOnceBlock(LightArrayRevolverScheduler me, FiniteDuration delay, scala.Function0<scala.runtime.BoxedUnit> block, ExecutionContext exc):
  execution(public * scheduleOnce(scala.concurrent.duration.FiniteDuration, scala.Function0<scala.runtime.BoxedUnit>, scala.concurrent.ExecutionContext)) &&
  args(delay, block, exc) && this(me);

  // Wrap the function in a special "Message" wrapper, that appears to
  // STS2 schedulers like a message, but which the Instrumenter understands as a
  // function to be invoked rather than a message to be sent.
  // TODO(cs): issue: no receiver.
  Object around(LightArrayRevolverScheduler me, FiniteDuration delay, scala.Function0<scala.runtime.BoxedUnit> block, ExecutionContext exc):
  scheduleOnceBlock(me, delay, block, exc) {
    if (!inst.actorSystemInitialized()) {
      return proceed(me, delay, block, exc);
    }

    class MyRunnable implements java.lang.Runnable {
      // Make it a no-op!
      public void run() {
      }
    }
    MyRunnable runnable = new MyRunnable();
    if (Instrumenter.akkaInternalCodeBlockSchedule()) {
      // Don't ever allow the `ask` timer to expire
      return me.scheduleOnce(delay, runnable, exc);
    }
    Cancellable c = new WrappedCancellable(me.scheduleOnce(delay, runnable, exc), "ScheduleFunction", block);
    inst.registerCancellable(c, false, "ScheduleFunction", block);
    return c;
  }
}

package sample;

import static java.lang.Thread.sleep;

import akka.dispatch.verification.Instrumenter;
import akka.dispatch.verification.WrappedCancellable;

import akka.actor.ActorRef;
import akka.actor.ScalaActorRef;
import akka.actor.InternalActorRef;
import akka.actor.Actor;
import akka.actor.Props;
import akka.actor.ActorCell;
import akka.actor.ActorSystem;
import akka.actor.ActorSystemImpl;
import akka.actor.ActorContext;
import akka.actor.Scheduler;
import akka.actor.Cancellable;
import akka.actor.LightArrayRevolverScheduler;
import akka.actor.LocalActorRefProvider;
import akka.actor.VirtualPathContainer;
import akka.actor.ActorPath;

import akka.pattern.AskSupport;
import akka.pattern.PromiseActorRef;

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

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

privileged public aspect WeaveActor {
  Instrumenter inst = Instrumenter.apply();

  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);
  }

  after(Mailbox me):
  execution(* akka.dispatch.Mailbox.run()) && this(me) {
    inst.mailboxIdle(me);
  }

  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;
  }

  // Interposition on `ask`. See this doc for more details:
  // https://docs.google.com/document/d/1_LUceHvQoamlBtNNbqA4CxBH-zvUKlZhnSjLwTc16q4/edit#
  pointcut receiveAnswer(PromiseActorRef me, Object message, ActorRef sender):
  execution(public void akka.pattern.PromiseActorRef.$bang(Object, ActorRef)) &&
  args(message, sender) && this(me);

  Object around(PromiseActorRef me, Object message, ActorRef sender):
  receiveAnswer(me, message, sender) {
    if (inst.receiveAskAnswer(me, message, sender)) {
      Object ret = proceed(me, message, sender);
      inst.afterReceiveAskAnswer(me, message, sender);
      return ret;
    }
    return null;
  }

  // Sanity check: make sure we don't allocate two temp actors at the same
  // time with the same name.
  before(VirtualPathContainer me, String name, InternalActorRef ref):
  execution(* VirtualPathContainer.addChild(String, InternalActorRef)) &&
  this(me) && args(name, ref) {
    if (me.children.containsKey(name)) {
      System.err.println("WARNING: temp name already taken: " + name);
    }
  }

  /*
   For debugging redundant temp names:
  before(VirtualPathContainer me, String name, InternalActorRef ref):
  execution(* VirtualPathContainer.removeChild(String, InternalActorRef)) &&
  this(me) && args(name, ref) {
    System.out.println("removeChild: " + name);
  }

  before(VirtualPathContainer me, String name):
  execution(* VirtualPathContainer.removeChild(String)) &&
  this(me) && args(name) {
    System.out.println("removeChild: " + name);
  }
  */

  // Interposition on the code that assigns IDs to temporary actors. See this
  // doc for more details:
  // https://docs.google.com/document/d/1rAM8EEy3WnLRhhPROvHmBhAREv0rmihz0Gw0GgF1xC4/edit#
  pointcut tempPath(LocalActorRefProvider me):
  execution(public ActorPath LocalActorRefProvider.tempPath()) && this(me);

  Object around(LocalActorRefProvider me): tempPath(me) {
    return inst.assignTempPath(me.tempNode);
  }

  // newActor is invoked before preStart is called!
  after(ActorCell me):
  execution(* akka.actor.ActorCell.newActor()) &&
  this(me) {
    inst.new_actor(me.system(), me.props, me.self);
  }

  // after preStart has been called, i.e. actor is ready to receive messages.
  after(ActorCell me):
  execution(* akka.actor.ActorCell.create(..)) &&
  this(me) {
    inst.preStartCalled(me.self);
  }

  // Block until the actor has actually been created and preStart has been invoked!
  // (which is done asynchronously)
  after(ActorSystem me, Props props) returning(ActorRef actor):
  execution(ActorRef akka.actor.ActorSystem.actorOf(Props)) &&
  args(props) && this(me) {
    inst.blockUntilPreStartCalled(actor);
  }

  // Block until the actor has actually been created and preStart has been invoked!
  // (which is done asynchronously)
  after(ActorSystem me, Props props, String name) returning(ActorRef actor):
  execution(ActorRef akka.actor.ActorSystem.actorOf(Props, String)) &&
  args(props, name) && this(me) {
    inst.blockUntilPreStartCalled(actor);
  }

  // Block until the actor has actually been created and preStart has been invoked!
  // (which is done asynchronously)
  after(ActorContext me, Props props) returning(ActorRef actor):
  execution(ActorRef akka.actor.ActorContext.actorOf(Props)) &&
  args(props) && this(me) {
    inst.blockUntilPreStartCalled(actor);
  }

  // Block until the actor has actually been created and preStart has been invoked!
  // (which is done asynchronously)
  after(ActorContext me, Props props, String name) returning(ActorRef actor):
  execution(ActorRef akka.actor.ActorContext.actorOf(Props, String)) &&
  args(props, name) && this(me) {
    inst.blockUntilPreStartCalled(actor);
  }

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

  // 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) {
    if (!inst.actorKnown(receiver)) {
      return proceed(me,delay,receiver,msg,exc,sender);
    }
    class MyRunnable implements java.lang.Runnable {
      // Make it a no-op!
      public void run() {
      }
    }
    MyRunnable runnable = new MyRunnable();
    Cancellable c = new WrappedCancellable(
      me.scheduleOnce(delay, runnable, exc), receiver.path().name(), msg);
    inst.registerCancellable(c, false, receiver.path().name(), msg);
    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) {
    if (!inst.actorKnown(receiver)) {
      return proceed(me,delay,interval,receiver,msg,exc,sender);
    }
    class MyRunnable implements java.lang.Runnable {
      // Make it a no-op!
      public void run() {
      }
    }
    MyRunnable runnable = new MyRunnable();
    Cancellable c = new WrappedCancellable(
      me.schedule(delay, interval, runnable, exc), receiver.path().name(), msg);
    inst.registerCancellable(c, true, receiver.path().name(), msg);
    return c;
  }

  // Override akka.actor.Scheduler.scheduler(block)
  pointcut scheduleBlock(LightArrayRevolverScheduler me, FiniteDuration delay, FiniteDuration interval, scala.Function0<scala.runtime.BoxedUnit> block, ExecutionContext exc):
  execution(public * schedule(scala.concurrent.duration.FiniteDuration, scala.concurrent.duration.FiniteDuration, scala.Function0<scala.runtime.BoxedUnit>, scala.concurrent.ExecutionContext)) &&
  args(delay, interval, block, exc) && this(me);

  // Wrap the function in a special "Message" wrapper, that appears to
  // STS2 schedulers like a message, but which the Instrumenter understands as a
  // function to be invoked rather than a message to be sent.
  // TODO(cs): issue: no receiver.
  Object around(LightArrayRevolverScheduler me, FiniteDuration delay, FiniteDuration interval, scala.Function0<scala.runtime.BoxedUnit> block, ExecutionContext exc):
  scheduleBlock(me, delay, interval, block, exc) {
    if (!inst.actorSystemInitialized() || Instrumenter.akkaInternalCodeBlockSchedule()) {
      return proceed(me, delay, interval, block, exc);
    }

    class MyRunnable implements java.lang.Runnable {
      // Make it a no-op!
      public void run() {
      }
    }
    MyRunnable runnable = new MyRunnable();
    Cancellable c = new WrappedCancellable(me.schedule(delay, interval, runnable, exc), "ScheduleFunction", block);
    inst.registerCancellable(c, true, "ScheduleFunction", block);
    return c;
  }

  // Override akka.actor.Scheduler.scheduler(block)
  pointcut scheduleOnceBlock(LightArrayRevolverScheduler me, FiniteDuration delay, scala.Function0<scala.runtime.BoxedUnit> block, ExecutionContext exc):
  execution(public * scheduleOnce(scala.concurrent.duration.FiniteDuration, scala.Function0<scala.runtime.BoxedUnit>, scala.concurrent.ExecutionContext)) &&
  args(delay, block, exc) && this(me);

  // Wrap the function in a special "Message" wrapper, that appears to
  // STS2 schedulers like a message, but which the Instrumenter understands as a
  // function to be invoked rather than a message to be sent.
  // TODO(cs): issue: no receiver.
  Object around(LightArrayRevolverScheduler me, FiniteDuration delay, scala.Function0<scala.runtime.BoxedUnit> block, ExecutionContext exc):
  scheduleOnceBlock(me, delay, block, exc) {
    if (!inst.actorSystemInitialized() || Instrumenter.akkaInternalCodeBlockSchedule()) {
      return proceed(me, delay, block, exc);
    }

    class MyRunnable implements java.lang.Runnable {
      // Make it a no-op!
      public void run() {
      }
    }
    MyRunnable runnable = new MyRunnable();
    Cancellable c = new WrappedCancellable(me.scheduleOnce(delay, runnable, exc), "ScheduleFunction", block);
    inst.registerCancellable(c, false, "ScheduleFunction", block);
    return c;
  }
}


commit cff4ddd772af65522337ba3380d2e75b7b5d61e0
Author: Konstantin Boudnik <cos@yahoo-inc.com>
Date:   Tue May 4 18:47:13 2010 -0700

    HADOOP-6332 from https://issues.apache.org/jira/secure/attachment/12443668/6332-phase2.fix1.patch
      on top of https://issues.apache.org/jira/secure/attachment/12443539/6332-phase2.patch
      fixing the issue with FileSystem access from Herriot via RPC

git-svn-id: 7e7a2d564dde2945c3a26e08d1235fcc0cb7aba1@1077439 13f79535-47bb-0310-9956-ffa450edef68

/**
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you 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.apache.hadoop.test.system;

import java.io.File;
import java.io.IOException;
import java.security.PrivilegedExceptionAction;
import java.util.HashMap;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.util.Properties;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.hadoop.io.Writable;
import org.apache.hadoop.security.UserGroupInformation;
import org.apache.hadoop.util.Shell.ShellCommandExecutor;
import org.apache.hadoop.fs.FileStatus;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.conf.Configuration;

/**
 * Default DaemonProtocolAspect which is used to provide default implementation
 * for all the common daemon methods. If a daemon requires more specialized
 * version of method, it is responsibility of the DaemonClient to introduce the
 * same in woven classes.
 *
 */
public aspect DaemonProtocolAspect {

  private boolean DaemonProtocol.ready;

  @SuppressWarnings("unchecked")
  private HashMap<Object, List<ControlAction>> DaemonProtocol.actions =
    new HashMap<Object, List<ControlAction>>();
  private static final Log LOG = LogFactory.getLog(
      DaemonProtocolAspect.class.getName());
  /**
   * Set if the daemon process is ready or not, concrete daemon protocol should
   * implement pointcuts to determine when the daemon is ready and use the
   * setter to set the ready state.
   *
   * @param ready
   *          true if the Daemon is ready.
   */
  public void DaemonProtocol.setReady(boolean ready) {
    this.ready = ready;
  }

  /**
   * Checks if the daemon process is alive or not.
   *
   * @throws IOException
   *           if daemon is not alive.
   */
  public void DaemonProtocol.ping() throws IOException {
  }

  /**
   * Checks if the daemon process is ready to accepting RPC connections after it
   * finishes initialization.

   *
   * @return true if ready to accept connection.
   *
   * @throws IOException
   */
  public boolean DaemonProtocol.isReady() throws IOException {
    return ready;
  }

  /**
   * Returns the process related information regarding the daemon process.

   *
   * @return process information.
   * @throws IOException
   */
  public ProcessInfo DaemonProtocol.getProcessInfo() throws IOException {
    int activeThreadCount = Thread.activeCount();
    long currentTime = System.currentTimeMillis();
    long maxmem = Runtime.getRuntime().maxMemory();
    long freemem = Runtime.getRuntime().freeMemory();
    long totalmem = Runtime.getRuntime().totalMemory();
    Map<String, String> envMap = System.getenv();
    Properties sysProps = System.getProperties();
    Map<String, String> props = new HashMap<String, String>();
    for (Map.Entry entry : sysProps.entrySet()) {
      props.put((String) entry.getKey(), (String) entry.getValue());
    }
    ProcessInfo info = new ProcessInfoImpl(activeThreadCount, currentTime,
        freemem, maxmem, totalmem, envMap, props);
    return info;
  }

  public void DaemonProtocol.enable(List<Enum<?>> faults) throws IOException {
  }

  public void DaemonProtocol.disableAll() throws IOException {
  }

  public abstract Configuration DaemonProtocol.getDaemonConf()
    throws IOException;

  public FileStatus DaemonProtocol.getFileStatus(String path, boolean local)
    throws IOException {
    Path p = new Path(path);
    FileSystem fs = getFS(p, local);
    p.makeQualified(fs);
    FileStatus fileStatus = fs.getFileStatus(p);
    return cloneFileStatus(fileStatus);
  }

  public FileStatus[] DaemonProtocol.listStatus(String path, boolean local)
    throws IOException {
    Path p = new Path(path);
    FileSystem fs = getFS(p, local);
    FileStatus[] status = fs.listStatus(p);
    if (status != null) {
      FileStatus[] result = new FileStatus[status.length];
      int i = 0;
      for (FileStatus fileStatus : status) {
        result[i++] = cloneFileStatus(fileStatus);
      }
      return result;
    }
    return status;
  }

  /**
   * FileStatus object may not be serializable. Clone it into raw FileStatus
   * object.
   */
  private FileStatus DaemonProtocol.cloneFileStatus(FileStatus fileStatus) {
    return new FileStatus(fileStatus.getLen(),
        fileStatus.isDir(),
        fileStatus.getReplication(),
        fileStatus.getBlockSize(),
        fileStatus.getModificationTime(),
        fileStatus.getAccessTime(),
        fileStatus.getPermission(),
        fileStatus.getOwner(),
        fileStatus.getGroup(),
        fileStatus.getPath());
  }

  private FileSystem DaemonProtocol.getFS(final Path path, final boolean local)
      throws IOException {
    FileSystem ret = null;
    try {
      ret = UserGroupInformation.getLoginUser().doAs (
          new PrivilegedExceptionAction<FileSystem>() {
            public FileSystem run() throws IOException {
              FileSystem fs = null;
              if (local) {
                fs = FileSystem.getLocal(getDaemonConf());
              } else {
                fs = path.getFileSystem(getDaemonConf());
              }
              return fs;
            }
          });
    } catch (InterruptedException ie) {
    }
    return ret;
  }

  @SuppressWarnings("unchecked")
  public ControlAction[] DaemonProtocol.getActions(Writable key)
    throws IOException {
    synchronized (actions) {
      List<ControlAction> actionList = actions.get(key);
      if(actionList == null) {
        return new ControlAction[0];
      } else {
        return (ControlAction[]) actionList.toArray(new ControlAction[actionList
                                                                      .size()]);
      }
    }
  }

  @SuppressWarnings("unchecked")
  public void DaemonProtocol.sendAction(ControlAction action)
      throws IOException {
    synchronized (actions) {
      List<ControlAction> actionList = actions.get(action.getTarget());
      if(actionList == null) {
        actionList = new ArrayList<ControlAction>();
        actions.put(action.getTarget(), actionList);
      }
      actionList.add(action);
    }
  }

  @SuppressWarnings("unchecked")
  public boolean DaemonProtocol.isActionPending(ControlAction action)
    throws IOException{
    synchronized (actions) {
      List<ControlAction> actionList = actions.get(action.getTarget());
      if(actionList == null) {
        return false;
      } else {
        return actionList.contains(action);
      }
    }
  }

  @SuppressWarnings("unchecked")
  public void DaemonProtocol.removeAction(ControlAction action)
    throws IOException {
    synchronized (actions) {
      List<ControlAction> actionList = actions.get(action.getTarget());
      if(actionList == null) {
        return;
      } else {
        actionList.remove(action);
      }
    }
  }

  public void DaemonProtocol.clearActions() throws IOException {
    synchronized (actions) {
      actions.clear();
    }
  }

  public String DaemonProtocol.getFilePattern() {
    //We use the environment variable HADOOP_LOGFILE to get the
    //pattern to use in the search.
    String logDir = System.getenv("HADOOP_LOG_DIR");
    String daemonLogPattern = System.getenv("HADOOP_LOGFILE");
    if(daemonLogPattern == null && daemonLogPattern.isEmpty()) {
      return "*";
    }
    return  logDir+File.separator+daemonLogPattern+"*";
  }

  public int DaemonProtocol.getNumberOfMatchesInLogFile(String pattern,
      String[] list) throws IOException {
    StringBuffer filePattern = new StringBuffer(getFilePattern());
    if(list != null){
      for(int i =0; i < list.length; ++i)
      {
        filePattern.append(" | grep -v " + list[i] );
      }
    }
    String[] cmd =
        new String[] {
            "bash",
            "-c",
            "grep -c "
                + pattern + " " + filePattern
                + " | awk -F: '{s+=$2} END {print s}'" };
    ShellCommandExecutor shexec = new ShellCommandExecutor(cmd);
    shexec.execute();
    String output = shexec.getOutput();
    return Integer.parseInt(output.replaceAll("\n", "").trim());
  }

  private String DaemonProtocol.user = null;

  public String DaemonProtocol.getDaemonUser() {
    return user;
  }

  public void DaemonProtocol.setUser(String user) {
    this.user = user;
  }
}

/**
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you 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.apache.hadoop.test.system;

import java.io.File;
import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.util.Properties;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.hadoop.io.Writable;
import org.apache.hadoop.util.Shell.ShellCommandExecutor;
import org.apache.hadoop.fs.FileStatus;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.conf.Configuration;

/**
 * Default DaemonProtocolAspect which is used to provide default implementation
 * for all the common daemon methods. If a daemon requires more specialized
 * version of method, it is responsibility of the DaemonClient to introduce the
 * same in woven classes.
 *
 */
public aspect DaemonProtocolAspect {

  private boolean DaemonProtocol.ready;

  @SuppressWarnings("unchecked")
  private HashMap<Object, List<ControlAction>> DaemonProtocol.actions =
    new HashMap<Object, List<ControlAction>>();
  private static final Log LOG = LogFactory.getLog(
      DaemonProtocolAspect.class.getName());
  /**
   * Set if the daemon process is ready or not, concrete daemon protocol should
   * implement pointcuts to determine when the daemon is ready and use the
   * setter to set the ready state.
   *
   * @param ready
   *          true if the Daemon is ready.
   */
  public void DaemonProtocol.setReady(boolean ready) {
    this.ready = ready;
  }

  /**
   * Checks if the daemon process is alive or not.
   *
   * @throws IOException
   *           if daemon is not alive.
   */
  public void DaemonProtocol.ping() throws IOException {
  }

  /**
   * Checks if the daemon process is ready to accepting RPC connections after it
   * finishes initialization.

   *
   * @return true if ready to accept connection.
   *
   * @throws IOException
   */
  public boolean DaemonProtocol.isReady() throws IOException {
    return ready;
  }

  /**
   * Returns the process related information regarding the daemon process.

   *
   * @return process information.
   * @throws IOException
   */
  public ProcessInfo DaemonProtocol.getProcessInfo() throws IOException {
    int activeThreadCount = Thread.activeCount();
    long currentTime = System.currentTimeMillis();
    long maxmem = Runtime.getRuntime().maxMemory();
    long freemem = Runtime.getRuntime().freeMemory();
    long totalmem = Runtime.getRuntime().totalMemory();
    Map<String, String> envMap = System.getenv();
    Properties sysProps = System.getProperties();
    Map<String, String> props = new HashMap<String, String>();
    for (Map.Entry entry : sysProps.entrySet()) {
      props.put((String) entry.getKey(), (String) entry.getValue());
    }
    ProcessInfo info = new ProcessInfoImpl(activeThreadCount, currentTime,
        freemem, maxmem, totalmem, envMap, props);
    return info;
  }

  public void DaemonProtocol.enable(List<Enum<?>> faults) throws IOException {
  }

  public void DaemonProtocol.disableAll() throws IOException {
  }

  public abstract Configuration DaemonProtocol.getDaemonConf()
    throws IOException;

  public FileStatus DaemonProtocol.getFileStatus(String path, boolean local)
    throws IOException {
    Path p = new Path(path);
    FileSystem fs = getFS(p, local);
    p.makeQualified(fs);
    FileStatus fileStatus = fs.getFileStatus(p);
    return cloneFileStatus(fileStatus);
  }

  public FileStatus[] DaemonProtocol.listStatus(String path, boolean local)
    throws IOException {
    Path p = new Path(path);
    FileSystem fs = getFS(p, local);
    FileStatus[] status = fs.listStatus(p);
    if (status != null) {
      FileStatus[] result = new FileStatus[status.length];
      int i = 0;
      for (FileStatus fileStatus : status) {
        result[i++] = cloneFileStatus(fileStatus);
      }
      return result;
    }
    return status;
  }

  /**
   * FileStatus object may not be serializable. Clone it into raw FileStatus
   * object.
   */
  private FileStatus DaemonProtocol.cloneFileStatus(FileStatus fileStatus) {
    return new FileStatus(fileStatus.getLen(),
        fileStatus.isDir(),
        fileStatus.getReplication(),
        fileStatus.getBlockSize(),
        fileStatus.getModificationTime(),
        fileStatus.getAccessTime(),
        fileStatus.getPermission(),
        fileStatus.getOwner(),
        fileStatus.getGroup(),
        fileStatus.getPath());
  }

  private FileSystem DaemonProtocol.getFS(Path path, boolean local)
    throws IOException {
    FileSystem fs = null;
    if (local) {
      fs = FileSystem.getLocal(getDaemonConf());
    } else {
      fs = path.getFileSystem(getDaemonConf());
    }
    return fs;
  }

  @SuppressWarnings("unchecked")
  public ControlAction[] DaemonProtocol.getActions(Writable key)
    throws IOException {
    synchronized (actions) {
      List<ControlAction> actionList = actions.get(key);
      if(actionList == null) {
        return new ControlAction[0];
      } else {
        return (ControlAction[]) actionList.toArray(new ControlAction[actionList
                                                                      .size()]);
      }
    }
  }

  @SuppressWarnings("unchecked")
  public void DaemonProtocol.sendAction(ControlAction action)
      throws IOException {
    synchronized (actions) {
      List<ControlAction> actionList = actions.get(action.getTarget());
      if(actionList == null) {
        actionList = new ArrayList<ControlAction>();
        actions.put(action.getTarget(), actionList);
      }
      actionList.add(action);
    }
  }

  @SuppressWarnings("unchecked")
  public boolean DaemonProtocol.isActionPending(ControlAction action)
    throws IOException{
    synchronized (actions) {
      List<ControlAction> actionList = actions.get(action.getTarget());
      if(actionList == null) {
        return false;
      } else {
        return actionList.contains(action);
      }
    }
  }

  @SuppressWarnings("unchecked")
  public void DaemonProtocol.removeAction(ControlAction action)
    throws IOException {
    synchronized (actions) {
      List<ControlAction> actionList = actions.get(action.getTarget());
      if(actionList == null) {
        return;
      } else {
        actionList.remove(action);
      }
    }
  }

  public void DaemonProtocol.clearActions() throws IOException {
    synchronized (actions) {
      actions.clear();
    }
  }

  public String DaemonProtocol.getFilePattern() {
    //We use the environment variable HADOOP_LOGFILE to get the
    //pattern to use in the search.
    String logDir = System.getenv("HADOOP_LOG_DIR");
    String daemonLogPattern = System.getenv("HADOOP_LOGFILE");
    if(daemonLogPattern == null && daemonLogPattern.isEmpty()) {
      return "*";
    }
    return  logDir+File.separator+daemonLogPattern+"*";
  }

  public int DaemonProtocol.getNumberOfMatchesInLogFile(String pattern,
      String[] list) throws IOException {
    StringBuffer filePattern = new StringBuffer(getFilePattern());
    if(list != null){
      for(int i =0; i < list.length; ++i)
      {
        filePattern.append(" | grep -v " + list[i] );
      }
    }
    String[] cmd =
        new String[] {
            "bash",
            "-c",
            "grep -c "
                + pattern + " " + filePattern
                + " | awk -F: '{s+=$2} END {print s}'" };
    ShellCommandExecutor shexec = new ShellCommandExecutor(cmd);
    shexec.execute();
    String output = shexec.getOutput();
    return Integer.parseInt(output.replaceAll("\n", "").trim());
  }

  private String DaemonProtocol.user = null;

  public String DaemonProtocol.getDaemonUser() {
    return user;
  }

  public void DaemonProtocol.setUser(String user) {
    this.user = user;
  }
}