From 3d60afa5529ff02260a0ad335fdb1466bf20817d Mon Sep 17 00:00:00 2001 From: lutovich Date: Thu, 29 Oct 2015 18:17:52 +0100 Subject: [PATCH] Removed infrastructure for JDI tests Initial motivation for doing this change was that JDI test classes depended on proprietary com.sun packages which are not available on IBM JDK. At the same time there were only two tests that used this infrastructure. One of them was obsolete and other one was simply rewritten in a stochastic way. --- advanced/management/pom.xml | 57 -- .../org/neo4j/metatest/SubProcessTest.java | 39 +- .../test/AbstractSubProcessTestBase.java | 349 ---------- .../test/subprocess/BeforeDebuggedTest.java | 31 - .../org/neo4j/test/subprocess/BreakPoint.java | 269 -------- .../test/subprocess/BreakpointHandler.java | 33 - .../test/subprocess/BreakpointTrigger.java | 37 - .../neo4j/test/subprocess/DebugInterface.java | 210 ------ .../neo4j/test/subprocess/DebuggedThread.java | 147 ---- .../subprocess/DebuggerDeadlockCallback.java | 35 - .../test/subprocess/EnabledBreakpoints.java | 32 - .../test/subprocess/ForeignBreakpoints.java | 45 -- .../neo4j/test/subprocess/KillSubProcess.java | 42 -- .../org/neo4j/test/subprocess/SubProcess.java | 336 +--------- .../test/subprocess/SubProcessTestRunner.java | 632 ------------------ .../subprocess/SuspendedThreadsException.java | 61 -- .../java/org/neo4j/test/subprocess/Task.java | 33 - 17 files changed, 22 insertions(+), 2366 deletions(-) delete mode 100644 community/kernel/src/test/java/org/neo4j/test/AbstractSubProcessTestBase.java delete mode 100644 community/kernel/src/test/java/org/neo4j/test/subprocess/BeforeDebuggedTest.java delete mode 100644 community/kernel/src/test/java/org/neo4j/test/subprocess/BreakPoint.java delete mode 100644 community/kernel/src/test/java/org/neo4j/test/subprocess/BreakpointHandler.java delete mode 100644 community/kernel/src/test/java/org/neo4j/test/subprocess/BreakpointTrigger.java delete mode 100644 community/kernel/src/test/java/org/neo4j/test/subprocess/DebugInterface.java delete mode 100644 community/kernel/src/test/java/org/neo4j/test/subprocess/DebuggedThread.java delete mode 100644 community/kernel/src/test/java/org/neo4j/test/subprocess/DebuggerDeadlockCallback.java delete mode 100644 community/kernel/src/test/java/org/neo4j/test/subprocess/EnabledBreakpoints.java delete mode 100644 community/kernel/src/test/java/org/neo4j/test/subprocess/ForeignBreakpoints.java delete mode 100644 community/kernel/src/test/java/org/neo4j/test/subprocess/KillSubProcess.java delete mode 100644 community/kernel/src/test/java/org/neo4j/test/subprocess/SubProcessTestRunner.java delete mode 100644 community/kernel/src/test/java/org/neo4j/test/subprocess/SuspendedThreadsException.java delete mode 100644 community/kernel/src/test/java/org/neo4j/test/subprocess/Task.java diff --git a/advanced/management/pom.xml b/advanced/management/pom.xml index c413746d5fd5f..78174ebd30317 100644 --- a/advanced/management/pom.xml +++ b/advanced/management/pom.xml @@ -28,63 +28,6 @@ https://github.com/neo4j/neo4j/tree/master/advanced/management - - - Linux-dependency-jconsole.jar - - - ${java.home}/../lib/jconsole.jar - - - - - com.sun.tools - jconsole - ${sun.tools.version} - system - ${java.home}/../lib/jconsole.jar - true - - - - - Mac-OS-X-dependency-jconsole.jar - - - ${java.home}/lib/jconsole.jar - - - - - com.sun.tools - jconsole - ${sun.tools.version} - system - ${java.home}/lib/jconsole.jar - true - - - - - Windows-dependency-jconsole.jar - - - Windows - - - - - com.sun.tools - jconsole - ${sun.tools.version} - system - ${java.home}\\..\\lib\\jconsole.jar - true - - - - - GNU Affero General Public License, Version 3 diff --git a/community/kernel/src/test/java/org/neo4j/metatest/SubProcessTest.java b/community/kernel/src/test/java/org/neo4j/metatest/SubProcessTest.java index 1953df861bc74..a688dad55c76b 100644 --- a/community/kernel/src/test/java/org/neo4j/metatest/SubProcessTest.java +++ b/community/kernel/src/test/java/org/neo4j/metatest/SubProcessTest.java @@ -19,23 +19,21 @@ */ package org.neo4j.metatest; -import static java.util.concurrent.TimeUnit.SECONDS; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; +import org.junit.Ignore; +import org.junit.Test; import java.rmi.Remote; import java.rmi.RemoteException; import java.rmi.server.UnicastRemoteObject; import java.util.concurrent.Callable; import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicBoolean; -import org.junit.Ignore; -import org.junit.Test; -import org.neo4j.test.subprocess.BreakPoint; -import org.neo4j.test.subprocess.DebugInterface; import org.neo4j.test.subprocess.SubProcess; +import static java.util.concurrent.TimeUnit.SECONDS; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + public class SubProcessTest { private static final String MESSAGE = "message"; @@ -53,6 +51,7 @@ protected void startup( String parameter ) started = true; } + @Override public String call() throws Exception { while ( !started ) @@ -76,30 +75,6 @@ public void canInvokeSubprocessMethod() throws Exception } } - @Test - public void canDebugSubprocess() throws Exception - { - final AtomicBoolean called = new AtomicBoolean( false ); - Callable proc = new TestingProcess().start( MESSAGE,// - new BreakPoint( TestingProcess.class, "call" ) - { - @Override - protected void callback( DebugInterface debug ) - { - called.set( true ); - } - }.enable() ); - try - { - assertEquals( MESSAGE, proc.call() ); - assertTrue( "breakpoint callback never reached", called.get() ); - } - finally - { - SubProcess.stop( proc ); - } - } - @Ignore( "not reliable - the processes do exit though" ) @Test public void subprocessShouldExitWhenParentProcessExits() throws Exception diff --git a/community/kernel/src/test/java/org/neo4j/test/AbstractSubProcessTestBase.java b/community/kernel/src/test/java/org/neo4j/test/AbstractSubProcessTestBase.java deleted file mode 100644 index d2ea57a13359c..0000000000000 --- a/community/kernel/src/test/java/org/neo4j/test/AbstractSubProcessTestBase.java +++ /dev/null @@ -1,349 +0,0 @@ -/* - * Copyright (c) 2002-2015 "Neo Technology," - * Network Engine for Objects in Lund AB [http://neotechnology.com] - * - * This file is part of Neo4j. - * - * Neo4j is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package org.neo4j.test; - -import org.junit.After; -import org.junit.Before; -import org.junit.Rule; - -import java.io.File; -import java.io.IOException; -import java.io.Serializable; -import java.util.HashMap; -import java.util.Map; -import java.util.concurrent.atomic.AtomicReferenceFieldUpdater; - -import org.neo4j.graphdb.GraphDatabaseService; -import org.neo4j.graphdb.factory.GraphDatabaseFactory; -import org.neo4j.graphdb.factory.GraphDatabaseSettings; -import org.neo4j.helpers.Pair; -import org.neo4j.helpers.Settings; -import org.neo4j.kernel.GraphDatabaseAPI; -import org.neo4j.test.subprocess.BreakPoint; -import org.neo4j.test.subprocess.SubProcess; - -import static org.neo4j.helpers.collection.MapUtil.stringMap; - -public class AbstractSubProcessTestBase -{ - @Rule - public final TargetDirectory.TestDirectory target = TargetDirectory.testDirForTest( getClass() ); - protected final Pair[] instances; - - public AbstractSubProcessTestBase() - { - this( 1 ); - } - - @SuppressWarnings("unchecked") - protected AbstractSubProcessTestBase( int instances ) - { - this.instances = new Pair[instances]; - } - - protected final void runInThread( Task task ) - { - run( new ThreadTask( task ) ); - } - - protected final void run( Task task ) - { - for ( Pair instance : instances ) - { - if ( instance != null ) - { - instance.first().run( task ); - } - } - } - - protected final void restart() - { - for ( Pair instance : instances ) - { - if ( instance != null ) - { - instance.first().restart(); - } - } - } - - /** - * @param id the id of the instance to get breakpoints for - */ - protected BreakPoint[] breakpoints( int id ) - { - return null; - } - - protected interface Task extends Serializable - { - void run( GraphDatabaseAPI graphdb ); - } - - @Before - public final void startSubprocesses() throws IOException, InterruptedException - { - SubInstance prototype = new SubInstance(); - for ( int i = 0; i < instances.length; i++ ) - { - BreakPoint[] breakPoints = breakpoints( i ); - instances[i] = Pair.of( prototype.start( bootstrap( i ), breakPoints ), breakPoints ); - } - for ( Pair instance : instances ) - { - if ( instance != null ) - { - instance.first().awaitStarted(); - } - } - } - - protected Bootstrapper bootstrap( int id ) throws IOException - { - return bootstrap( id, new HashMap() ); - } - - protected Bootstrapper bootstrap( int id, Map dbConfiguration ) throws IOException - { - return new Bootstrapper( this, id, dbConfiguration ); - } - - @After - public final void stopSubprocesses() - { - synchronized ( instances ) - { - for ( int i = 0; i < instances.length; i++ ) - { - Pair instance = instances[i]; - if ( instance != null ) - { - SubProcess.stop( instance.first() ); - } - instances[i] = null; - } - } - } - - public interface Instance - { - void run( Task task ); - - void awaitStarted() throws InterruptedException; - - void restart(); - - // T getMBean( Class beanType ); - } - - @SuppressWarnings("serial") - public static class Bootstrapper implements Serializable - { - protected final String storeDir; - private final Map dbConfiguration; - - public Bootstrapper( AbstractSubProcessTestBase test, int instance ) - throws IOException - { - this( test, instance, new HashMap() ); - } - - public Bootstrapper( AbstractSubProcessTestBase test, int instance, - Map dbConfiguration ) throws IOException - { - this.dbConfiguration = addVitalConfig( dbConfiguration ); - this.storeDir = getStoreDir( test, instance ).getCanonicalPath(); - } - - private Map addVitalConfig( Map dbConfiguration ) - { - return stringMap( new HashMap<>( dbConfiguration ), - GraphDatabaseSettings.keep_logical_logs.name(), Settings.TRUE, - GraphDatabaseSettings.pagecache_memory.name(), "8m" ); - } - - protected GraphDatabaseService startup() - { - return new GraphDatabaseFactory().newEmbeddedDatabaseBuilder( storeDir ).setConfig( dbConfiguration ) - .newGraphDatabase(); - } - - protected void shutdown( GraphDatabaseService graphdb, boolean normal ) - { - graphdb.shutdown(); - } - } - - protected static File getStoreDir( AbstractSubProcessTestBase test, int instance ) - throws IOException - { - return test.target.directory( "graphdb." + instance ); - } - - protected static Bootstrapper killAwareBootstrapper( AbstractSubProcessTestBase test, int instance, - Map dbConfiguration ) - { - try - { - return new KillAwareBootstrapper( test, instance, dbConfiguration ); - } - catch ( IOException e ) - { - throw new RuntimeException( e ); - } - } - - public static class KillAwareBootstrapper extends Bootstrapper - { - public KillAwareBootstrapper( AbstractSubProcessTestBase test, int instance, - Map dbConfiguration ) throws IOException - { - super( test, instance, dbConfiguration ); - } - - @Override - protected void shutdown( GraphDatabaseService graphdb, boolean normal ) - { - if ( normal ) - { - super.shutdown( graphdb, normal ); - } - } - } - - @SuppressWarnings("serial") - private static class ThreadTask implements Task - { - private final Task task; - private final Exception stackTraceOfOrigin; - - ThreadTask( Task task ) - { - this.task = task; - this.stackTraceOfOrigin = new Exception("Stack trace of thread that created this ThreadTask"); - } - - @Override - public void run( final GraphDatabaseAPI graphdb ) - { - new Thread( new Runnable() - { - @Override - public void run() - { - try - { - task.run( graphdb ); - } - catch ( RuntimeException e ) - { - e.addSuppressed( stackTraceOfOrigin ); - throw e; - } - } - }, task.toString() ).start(); - } - } - - @SuppressWarnings({"hiding", "serial"}) - private static class SubInstance extends SubProcess implements Instance - { - private volatile GraphDatabaseAPI graphdb; - private static final AtomicReferenceFieldUpdater GRAPHDB = - AtomicReferenceFieldUpdater - .newUpdater( SubInstance.class, GraphDatabaseAPI.class, "graphdb" ); - private volatile Bootstrapper bootstrap; - private volatile Throwable failure; - - @Override - protected synchronized void startup( Bootstrapper bootstrap ) - { - this.bootstrap = bootstrap; - try - { - graphdb = (GraphDatabaseAPI) bootstrap.startup(); - } - catch ( Throwable failure ) - { - this.failure = failure; - } - } - - @Override - public void awaitStarted() throws InterruptedException - { - while ( graphdb == null ) - { - Throwable failure = this.failure; - if ( failure != null ) - { - throw new StartupFailureException( failure ); - } - Thread.sleep( 1 ); - } - } - - @Override - public void run( Task task ) - { - task.run( graphdb ); - } - - @Override - protected void shutdown( boolean normal ) - { - GraphDatabaseService graphdb; - Bootstrapper bootstrap = this.bootstrap; - graphdb = GRAPHDB.getAndSet( this, null ); - this.bootstrap = null; - if ( graphdb != null ) - { - bootstrap.shutdown( graphdb, normal ); - } - super.shutdown( normal ); - } - - @Override - public void restart() - { - GraphDatabaseService graphdb; - Bootstrapper bootstrap = this.bootstrap; - while ( (graphdb = GRAPHDB.getAndSet( this, null )) == null ) - { - if ( (bootstrap = this.bootstrap) == null ) - { - throw new IllegalStateException( "instance has been shut down" ); - } - } - graphdb.shutdown(); - this.graphdb = (GraphDatabaseAPI) bootstrap.startup(); - } - } - - @SuppressWarnings("serial") - private static class StartupFailureException extends RuntimeException - { - StartupFailureException( Throwable failure ) - { - super( failure ); - } - } -} diff --git a/community/kernel/src/test/java/org/neo4j/test/subprocess/BeforeDebuggedTest.java b/community/kernel/src/test/java/org/neo4j/test/subprocess/BeforeDebuggedTest.java deleted file mode 100644 index b69c73c4276f7..0000000000000 --- a/community/kernel/src/test/java/org/neo4j/test/subprocess/BeforeDebuggedTest.java +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright (c) 2002-2015 "Neo Technology," - * Network Engine for Objects in Lund AB [http://neotechnology.com] - * - * This file is part of Neo4j. - * - * Neo4j is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package org.neo4j.test.subprocess; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -@Target(ElementType.METHOD) -@Retention(RetentionPolicy.RUNTIME) -public @interface BeforeDebuggedTest -{ -} diff --git a/community/kernel/src/test/java/org/neo4j/test/subprocess/BreakPoint.java b/community/kernel/src/test/java/org/neo4j/test/subprocess/BreakPoint.java deleted file mode 100644 index fa2c8b8a756cf..0000000000000 --- a/community/kernel/src/test/java/org/neo4j/test/subprocess/BreakPoint.java +++ /dev/null @@ -1,269 +0,0 @@ -/* - * Copyright (c) 2002-2015 "Neo Technology," - * Network Engine for Objects in Lund AB [http://neotechnology.com] - * - * This file is part of Neo4j. - * - * Neo4j is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package org.neo4j.test.subprocess; - -import org.neo4j.function.Predicate; - -import java.io.PrintStream; -import java.util.Iterator; -import java.util.List; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.atomic.AtomicInteger; - -public abstract class BreakPoint implements DebuggerDeadlockCallback -{ - public enum Event - { - ENTRY, - EXIT - } - - public BreakPoint( Class type, String method, Class... args ) - { - this(Event.ENTRY,type,method,args); - } - - public BreakPoint( Event event, Class type, String method, Class... args ) - { - assert methodExists( type, method, args ); - this.event = event; - this.type = type.getName(); - this.method = method; - this.args = new String[args.length]; - for ( int i = 0; i < args.length; i++ ) - { - this.args[i] = args[i].getName(); - } - } - - private static boolean methodExists( Class type, String method, Class[] args ) - { - try - { - type.getDeclaredMethod( method, args ); - return true; - } - catch ( Exception e ) - { - throw new AssertionError( e ); - } - } - - @Override - public String toString() - { - StringBuilder result = new StringBuilder( "BreakPoint[" ); - result.append( type ).append( '#' ).append( method ).append( '(' ); - for ( int i = 0; i < args.length; i++ ) - { - if ( i > 0 ) result.append( ',' ); - result.append( args[i] ); - } - return result.append( ")]" ).toString(); - } - - public final int invocationCount() - { - return count.get(); - } - - public int invocationCount( int value ) - { - return count.getAndSet( value ); - } - - public final int resetInvocationCount() - { - return invocationCount( 0 ); - } - - final void invoke( DebugInterface debug ) throws KillSubProcess - { - count.incrementAndGet(); - callback( debug ); - } - - protected abstract void callback( DebugInterface debug ) throws KillSubProcess; - - @Override - public void deadlock( DebuggedThread thread ) - { - SubProcess.DebugDispatch.defaultCallback.deadlock( thread ); - } - - final Event event; - final String type; - private final String method; - private final String[] args; - private final AtomicInteger count = new AtomicInteger(); - volatile boolean enabled = false; - private @SuppressWarnings( "restriction" ) - com.sun.jdi.request.EventRequest request = null; - - @SuppressWarnings( "restriction" ) - public synchronized boolean isEnabled() - { - if ( request != null ) return request.isEnabled(); - return enabled; - } - - @SuppressWarnings( "restriction" ) - public synchronized BreakPoint enable() - { - this.enabled = true; - if ( request != null ) request.enable(); - return this; - } - - @SuppressWarnings( "restriction" ) - public synchronized BreakPoint disable() - { - this.enabled = false; - if ( request != null ) request.disable(); - return this; - } - - @SuppressWarnings( "restriction" ) - synchronized void setRequest( com.sun.jdi.request.EventRequest request ) - { - this.request = request; - this.request.setEnabled( enabled ); - } - - @SuppressWarnings( "restriction" ) - void setup( com.sun.jdi.ReferenceType type ) - { - com.sun.jdi.request.EventRequestManager erm = type.virtualMachine().eventRequestManager(); - for ( @SuppressWarnings( "hiding" ) com.sun.jdi.Method method : type.methodsByName( this.method ) ) - { - if ( matches( method.name(), method.argumentTypeNames() ) ) - { - switch (event) - { - case ENTRY: - setRequest( erm.createBreakpointRequest( method.location() ) ); - return; - case EXIT: - com.sun.jdi.request.MethodExitRequest request = erm.createMethodExitRequest(); - request.addClassFilter( type ); - setRequest( request ); - } - return; - } - } - } - - boolean matches( String name, List argNames ) - { - if ( !name.equals( method ) ) return false; - if ( argNames.size() != args.length ) return false; - Iterator names = argNames.iterator(); - for ( int i = 0; i < args.length; i++ ) - { - if ( !args[i].equals( names.next() ) ) return false; - } - return true; - } - - public static BreakPoint stacktrace( final PrintStream out, Class type, String method, Class... args ) - { - return new BreakPoint( type, method, args ) - { - @Override - protected void callback( DebugInterface debug ) - { - out.println( "Debugger stacktrace" ); - debug.printStackTrace( out ); - } - }; - } - - public static final Predicate ALL = new Predicate() - { - @Override - public boolean test( StackTraceElement[] item ) - { - return true; - } - }; - - private static Predicate reversed( final Predicate predicate ) - { - return new Predicate() - { - @Override - public boolean test( StackTraceElement[] item ) - { - return !predicate.test( item ); - } - }; - } - - public static Predicate stackTraceMustContainClass( final Class cls ) - { - return new Predicate() - { - @Override - public boolean test( StackTraceElement[] item ) - { - for ( StackTraceElement element : item ) - if ( element.getClassName().equals( cls.getName() ) ) - return true; - return false; - } - }; - } - - public static Predicate stackTraceMustNotContainClass( final Class cls ) - { - return reversed( stackTraceMustContainClass( cls ) ); - } - - public static BreakPoint thatCrashesTheProcess( final CountDownLatch crashNotification, - final int letNumberOfCallsPass, Class type, String method, Class... args ) - { - return thatCrashesTheProcess( Event.ENTRY, crashNotification, letNumberOfCallsPass, ALL, type, method, args ); - } - - public static BreakPoint thatCrashesTheProcess( Event event, final CountDownLatch crashNotification, - final int letNumberOfCallsPass, final Predicate stackTraceMustContain, Class type, - String method, Class... args ) - { - return new BreakPoint( event, type, method, args ) - { - private volatile int numberOfCalls; - - @Override - protected void callback( DebugInterface debug ) throws KillSubProcess - { - if ( ++numberOfCalls <= letNumberOfCallsPass ) - return; - - if ( !stackTraceMustContain.test( debug.thread().getStackTrace() ) ) - return; - - debug.thread().suspend( null ); - this.disable(); - crashNotification.countDown(); - throw KillSubProcess.withExitCode( -1 ); - } - }; - } -} diff --git a/community/kernel/src/test/java/org/neo4j/test/subprocess/BreakpointHandler.java b/community/kernel/src/test/java/org/neo4j/test/subprocess/BreakpointHandler.java deleted file mode 100644 index 015a14ac84494..0000000000000 --- a/community/kernel/src/test/java/org/neo4j/test/subprocess/BreakpointHandler.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright (c) 2002-2015 "Neo Technology," - * Network Engine for Objects in Lund AB [http://neotechnology.com] - * - * This file is part of Neo4j. - * - * Neo4j is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package org.neo4j.test.subprocess; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -@Target( { ElementType.METHOD, ElementType.PARAMETER } ) -@Retention( RetentionPolicy.RUNTIME ) -public @interface BreakpointHandler -{ - /** the name(s) of the breakpoint(s) to handle */ - String[] value(); -} diff --git a/community/kernel/src/test/java/org/neo4j/test/subprocess/BreakpointTrigger.java b/community/kernel/src/test/java/org/neo4j/test/subprocess/BreakpointTrigger.java deleted file mode 100644 index b974ff8675f8f..0000000000000 --- a/community/kernel/src/test/java/org/neo4j/test/subprocess/BreakpointTrigger.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright (c) 2002-2015 "Neo Technology," - * Network Engine for Objects in Lund AB [http://neotechnology.com] - * - * This file is part of Neo4j. - * - * Neo4j is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package org.neo4j.test.subprocess; - -import static org.neo4j.test.subprocess.BreakPoint.Event.ENTRY; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -@Target( ElementType.METHOD ) -@Retention( RetentionPolicy.RUNTIME ) -public @interface BreakpointTrigger -{ - /** the name of the break point */ - String value() default ""; - - BreakPoint.Event on() default ENTRY; -} diff --git a/community/kernel/src/test/java/org/neo4j/test/subprocess/DebugInterface.java b/community/kernel/src/test/java/org/neo4j/test/subprocess/DebugInterface.java deleted file mode 100644 index 255fa09f5c85d..0000000000000 --- a/community/kernel/src/test/java/org/neo4j/test/subprocess/DebugInterface.java +++ /dev/null @@ -1,210 +0,0 @@ -/* - * Copyright (c) 2002-2015 "Neo Technology," - * Network Engine for Objects in Lund AB [http://neotechnology.com] - * - * This file is part of Neo4j. - * - * Neo4j is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package org.neo4j.test.subprocess; - -import com.sun.jdi.AbsentInformationException; -import com.sun.jdi.ClassNotLoadedException; -import com.sun.jdi.IncompatibleThreadStateException; -import com.sun.jdi.InvalidTypeException; -import com.sun.jdi.LocalVariable; -import com.sun.jdi.StackFrame; -import com.sun.jdi.Value; -import com.sun.jdi.VirtualMachine; -import java.io.PrintStream; - -@SuppressWarnings( "restriction" ) -public class DebugInterface -{ - private final com.sun.jdi.event.LocatableEvent event; - private final SubProcess.DebugDispatch debug; - - DebugInterface( SubProcess.DebugDispatch debug, com.sun.jdi.event.LocatableEvent event ) - { - this.debug = debug; - this.event = event; - } - - public boolean matchCallingMethod( int offset, Class owner, String method ) - { - try - { - com.sun.jdi.Location location = event.thread().frame( offset ).location(); - if ( owner != null ) - { - if ( !owner.getName().equals( location.declaringType().name() ) ) return false; - } - if ( method != null ) - { - if ( !method.equals( location.method().name() ) ) return false; - } - return true; - } - catch ( com.sun.jdi.IncompatibleThreadStateException e ) - { - return false; - } - } - - public DebuggedThread thread() - { - return new DebuggedThread( debug, event.thread() ); - } - - public void printStackTrace( PrintStream out ) - { - thread().printStackTrace(out); - } - - public Object getLocalVariable( String name ) - { - try - { - StackFrame frame = event.thread().frame( 0 ); - return fromMirror( frame.getValue( frame.visibleVariableByName( name ) ) ); - } - catch ( IncompatibleThreadStateException e ) - { - throw new IllegalStateException( e ); - } - catch ( AbsentInformationException e ) - { - throw new IllegalStateException( e ); - } - } - - public void setLocalVariable( String name, Object value ) - { - try - { - StackFrame frame = event.thread().frame( 0 ); - LocalVariable local = frame.visibleVariableByName( name ); - frame.setValue( local, mirror( value ) ); - } - catch ( IncompatibleThreadStateException e ) - { - throw new IllegalStateException( e ); - } - catch ( AbsentInformationException e ) - { - throw new IllegalStateException( e ); - } - catch ( InvalidTypeException e ) - { - throw new IllegalStateException( e ); - } - catch ( ClassNotLoadedException e ) - { - throw new IllegalStateException( e ); - } - } - - @SuppressWarnings( "boxing" ) - private Value mirror( Object value ) - { - VirtualMachine vm = event.virtualMachine(); - if ( value == null ) - { - return vm.mirrorOfVoid(); - } - if ( value instanceof String ) - { - return vm.mirrorOf( (String) value ); - } - if ( value instanceof Integer ) - { - return vm.mirrorOf( (Integer) value ); - } - if ( value instanceof Long ) - { - return vm.mirrorOf( (Long) value ); - } - if ( value instanceof Double ) - { - return vm.mirrorOf( (Double) value ); - } - if ( value instanceof Boolean ) - { - return vm.mirrorOf( (Boolean) value ); - } - if ( value instanceof Byte ) - { - return vm.mirrorOf( (Byte) value ); - } - if ( value instanceof Character ) - { - return vm.mirrorOf( (Character) value ); - } - if ( value instanceof Short ) - { - return vm.mirrorOf( (Short) value ); - } - if ( value instanceof Float ) - { - return vm.mirrorOf( (Float) value ); - } - throw new IllegalArgumentException( "Cannot mirror: " + value ); - } - - @SuppressWarnings( "boxing" ) - private Object fromMirror( Value value ) - { - if ( value instanceof com.sun.jdi.VoidValue ) - { - return null; - } - if ( value instanceof com.sun.jdi.StringReference ) - { - return ( (com.sun.jdi.StringReference) value).value(); - } - if ( value instanceof com.sun.jdi.IntegerValue ) - { - return ( (com.sun.jdi.IntegerValue) value ).intValue(); - } - if ( value instanceof com.sun.jdi.LongValue ) - { - return ( (com.sun.jdi.LongValue) value ).longValue(); - } - if ( value instanceof com.sun.jdi.DoubleValue ) - { - return ( (com.sun.jdi.DoubleValue) value ).doubleValue(); - } - if ( value instanceof com.sun.jdi.BooleanValue ) - { - return ( (com.sun.jdi.BooleanValue) value ).booleanValue(); - } - if ( value instanceof com.sun.jdi.ByteValue ) - { - return ( (com.sun.jdi.ByteValue) value ).byteValue(); - } - if ( value instanceof com.sun.jdi.CharValue ) - { - return ( (com.sun.jdi.CharValue) value ).charValue(); - } - if ( value instanceof com.sun.jdi.ShortValue ) - { - return ( (com.sun.jdi.ShortValue) value ).shortValue(); - } - if ( value instanceof com.sun.jdi.FloatValue ) - { - return ( (com.sun.jdi.FloatValue) value ).floatValue(); - } - return value.toString(); - } -} \ No newline at end of file diff --git a/community/kernel/src/test/java/org/neo4j/test/subprocess/DebuggedThread.java b/community/kernel/src/test/java/org/neo4j/test/subprocess/DebuggedThread.java deleted file mode 100644 index 311b5f2fa6293..0000000000000 --- a/community/kernel/src/test/java/org/neo4j/test/subprocess/DebuggedThread.java +++ /dev/null @@ -1,147 +0,0 @@ -/* - * Copyright (c) 2002-2015 "Neo Technology," - * Network Engine for Objects in Lund AB [http://neotechnology.com] - * - * This file is part of Neo4j. - * - * Neo4j is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package org.neo4j.test.subprocess; - -import java.io.PrintStream; -import java.util.Iterator; -import java.util.LinkedList; -import java.util.List; - -import com.sun.jdi.ClassType; -import com.sun.jdi.Method; -import com.sun.jdi.ObjectReference; - -public class DebuggedThread -{ - private final com.sun.jdi.ThreadReference thread; - private final SubProcess.DebugDispatch debug; - - DebuggedThread( SubProcess.DebugDispatch debug, com.sun.jdi.ThreadReference thread ) - { - this.debug = debug; - this.thread = thread; - } - - @Override - public String toString() - { - return "DebuggedThread[" + debug.handler + " " + thread.name() + "]"; - } - - public DebuggedThread suspend( DebuggerDeadlockCallback callback ) - { - thread.suspend(); - debug.suspended( thread, callback ); - return this; - } - - // TODO Make it possible to define the type of exception to terminate the thread with - public DebuggedThread stop() - { - /* - * To kill a thread requires an Exception. But it is not a local thread so it has to be an exception - * object on the remote VM. So grab hold of a reference to the RuntimeException class, get its constructor, - * create an instance on the remote VM and use that to stop the thread. - */ - ClassType threadDeathClass = (ClassType) - thread.virtualMachine().classesByName( "java.lang.RuntimeException" ).get(0); - Method constructor = threadDeathClass.concreteMethodByName( "", "()V" ); - try - { - ObjectReference toKillWith = threadDeathClass.newInstance( thread, constructor, new LinkedList(), ClassType.INVOKE_SINGLE_THREADED ); - thread.stop( toKillWith ); - } - catch (Exception e) - { - /* - * Can be one of {InvalidType, ClassNotLoaded, IncompatibleThreadState, Invocation}Exception. We cannot - * recover on any of those, just rethrow it. - */ - throw new RuntimeException( e ); - } - return this; - } - - public DebuggedThread resume() - { - thread.resume(); - debug.resume( thread ); - return this; - } - - public String getLocal( int offset, String name ) - { - try - { - com.sun.jdi.StackFrame frame = thread.frames().get( offset ); - com.sun.jdi.LocalVariable variable = frame.visibleVariableByName( name ); - return frame.getValue( variable ).toString(); - } - catch ( Exception e ) - { - return null; - } - } - - public StackTraceElement[] getStackTrace() - { - try - { - List frames = thread.frames(); - StackTraceElement[] trace = new StackTraceElement[frames.size()]; - Iterator iter = frames.iterator(); - for ( int i = 0; iter.hasNext(); i++ ) - { - com.sun.jdi.Location loc = iter.next().location(); - com.sun.jdi.Method method = loc.method(); - String fileName; - try - { - fileName = loc.sourceName(); - } - catch ( com.sun.jdi.AbsentInformationException e ) - { - fileName = null; - } - trace[i] = new StackTraceElement( method.declaringType().name(), method.name(), fileName, - loc.lineNumber() ); - } - return trace; - } - catch ( com.sun.jdi.IncompatibleThreadStateException e ) - { - return new StackTraceElement[0]; - } - } - - public String name() - { - return thread.name(); - } - - public void printStackTrace( PrintStream out ) - { - out.println( name() ); - for ( StackTraceElement trace : getStackTrace() ) - { - out.println( "\tat " + trace ); - } - } -} \ No newline at end of file diff --git a/community/kernel/src/test/java/org/neo4j/test/subprocess/DebuggerDeadlockCallback.java b/community/kernel/src/test/java/org/neo4j/test/subprocess/DebuggerDeadlockCallback.java deleted file mode 100644 index a838947ea3fc9..0000000000000 --- a/community/kernel/src/test/java/org/neo4j/test/subprocess/DebuggerDeadlockCallback.java +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright (c) 2002-2015 "Neo Technology," - * Network Engine for Objects in Lund AB [http://neotechnology.com] - * - * This file is part of Neo4j. - * - * Neo4j is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package org.neo4j.test.subprocess; - -public interface DebuggerDeadlockCallback -{ - DebuggerDeadlockCallback RESUME_THREAD = new DebuggerDeadlockCallback() - { - @Override - public void deadlock( DebuggedThread thread ) - { - thread.resume(); - } - }; - - /** Will be called with the suspended thread that causes the deadlock */ - void deadlock( DebuggedThread thread ); -} diff --git a/community/kernel/src/test/java/org/neo4j/test/subprocess/EnabledBreakpoints.java b/community/kernel/src/test/java/org/neo4j/test/subprocess/EnabledBreakpoints.java deleted file mode 100644 index b3af095d573bb..0000000000000 --- a/community/kernel/src/test/java/org/neo4j/test/subprocess/EnabledBreakpoints.java +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright (c) 2002-2015 "Neo Technology," - * Network Engine for Objects in Lund AB [http://neotechnology.com] - * - * This file is part of Neo4j. - * - * Neo4j is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package org.neo4j.test.subprocess; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -@Target( ElementType.METHOD ) -@Retention( RetentionPolicy.RUNTIME ) -public @interface EnabledBreakpoints -{ - String[] value(); -} \ No newline at end of file diff --git a/community/kernel/src/test/java/org/neo4j/test/subprocess/ForeignBreakpoints.java b/community/kernel/src/test/java/org/neo4j/test/subprocess/ForeignBreakpoints.java deleted file mode 100644 index 0f18c322d74f2..0000000000000 --- a/community/kernel/src/test/java/org/neo4j/test/subprocess/ForeignBreakpoints.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright (c) 2002-2015 "Neo Technology," - * Network Engine for Objects in Lund AB [http://neotechnology.com] - * - * This file is part of Neo4j. - * - * Neo4j is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package org.neo4j.test.subprocess; - -import static org.neo4j.test.subprocess.BreakPoint.Event.ENTRY; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -@Target( ElementType.TYPE ) -@Retention( RetentionPolicy.RUNTIME ) -public @interface ForeignBreakpoints -{ - BreakpointDef[] value(); - - @interface BreakpointDef - { - String name() default ""; - - String type(); - - String method(); - - BreakPoint.Event on() default ENTRY; - } -} diff --git a/community/kernel/src/test/java/org/neo4j/test/subprocess/KillSubProcess.java b/community/kernel/src/test/java/org/neo4j/test/subprocess/KillSubProcess.java deleted file mode 100644 index eb060ea60f9bb..0000000000000 --- a/community/kernel/src/test/java/org/neo4j/test/subprocess/KillSubProcess.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright (c) 2002-2015 "Neo Technology," - * Network Engine for Objects in Lund AB [http://neotechnology.com] - * - * This file is part of Neo4j. - * - * Neo4j is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package org.neo4j.test.subprocess; - -@SuppressWarnings( "serial" ) -public final class KillSubProcess extends Exception -{ - public static KillSubProcess withExitCode( int exitCode ) - { - return new KillSubProcess( exitCode ); - } - - final int exitCode; - - private KillSubProcess( int exitCode ) - { - this.exitCode = exitCode; - } - - @Override - public synchronized Throwable fillInStackTrace() - { - return this; - } -} diff --git a/community/kernel/src/test/java/org/neo4j/test/subprocess/SubProcess.java b/community/kernel/src/test/java/org/neo4j/test/subprocess/SubProcess.java index 47c05466be799..6e6610704aa8e 100644 --- a/community/kernel/src/test/java/org/neo4j/test/subprocess/SubProcess.java +++ b/community/kernel/src/test/java/org/neo4j/test/subprocess/SubProcess.java @@ -44,10 +44,8 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; -import java.util.HashMap; import java.util.HashSet; import java.util.List; -import java.util.Map; import java.util.Set; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.CountDownLatch; @@ -132,13 +130,8 @@ public SubProcess() this( null, INHERIT_OUTPUT_DEFAULT_VALUE ); } - public T start( P parameter, BreakPoint... breakpoints ) + public T start( P parameter ) { - DebuggerConnector debugger = null; - if ( breakpoints != null && breakpoints.length != 0 ) - { - debugger = new DebuggerConnector( breakpoints ); - } DispatcherTrapImpl callback; try { @@ -150,37 +143,19 @@ public T start( P parameter, BreakPoint... breakpoints ) } Process process; String pid; - DebugDispatch debugDispatch = null; Dispatcher dispatcher; try { - synchronized ( debugger != null ? DebuggerConnector.class : new Object() ) + process = start( inheritOutput, "java", "-ea", "-Xmx1G", "-Djava.awt.headless=true", "-cp", + classPath( System.getProperty( "java.class.path" ) ), + SubProcess.class.getName(), serialize( callback ) ); + pid = getPid( process ); + // if IO was not inherited by current process we need to pipe error and input stream to corresponding + // target streams + if ( !inheritOutput ) { - if ( debugger != null ) - { - process = start( inheritOutput, "java", "-ea", "-Xmx1G", debugger.listen(), - "-Djava.awt.headless=true", "-cp", - classPath( System.getProperty( "java.class.path" ) ), SubProcess.class.getName(), - serialize( callback ) ); - } - else - { - process = start( inheritOutput, "java", "-ea", "-Xmx1G", "-Djava.awt.headless=true", "-cp", - classPath( System.getProperty( "java.class.path" ) ), - SubProcess.class.getName(), serialize( callback ) ); - } - pid = getPid( process ); - // if IO was not inherited by current process we need to pipe error and input stream to corresponding - // target streams - if (!inheritOutput ) - { - pipe( "[" + toString() + ":" + pid + "] ", process.getErrorStream(), errorStreamTarget() ); - pipe( "[" + toString() + ":" + pid + "] ", process.getInputStream(), inputStreamTarget() ); - } - if ( debugger != null ) - { - debugDispatch = debugger.connect( toString() + ":" + pid ); - } + pipe( "[" + toString() + ":" + pid + "] ", process.getErrorStream(), errorStreamTarget() ); + pipe( "[" + toString() + ":" + pid + "] ", process.getInputStream(), inputStreamTarget() ); } dispatcher = callback.get( process ); } @@ -199,12 +174,8 @@ public T start( P parameter, BreakPoint... breakpoints ) { throw new IllegalStateException( "failed to start sub process" ); } - Handler handler = new Handler( t, dispatcher, process, "<" + toString() + ":" + pid + ">", debugDispatch ); - if ( debugDispatch != null ) - { - debugDispatch.handler = handler; - } - return t.cast( Proxy.newProxyInstance( t.getClassLoader(), new Class[] { t }, live( handler ) ) ); + Handler handler = new Handler( t, dispatcher, process, "<" + toString() + ":" + pid + ">" ); + return t.cast( Proxy.newProxyInstance( t.getClassLoader(), new Class[]{t}, live( handler ) ) ); } protected PrintStream errorStreamTarget() @@ -256,281 +227,6 @@ private static Process start(boolean inheritOutput, String... args ) } } - @SuppressWarnings( "restriction" ) - private static class DebuggerConnector - { - private static final com.sun.jdi.connect.ListeningConnector connector; - static - { - com.sun.jdi.connect.ListeningConnector first = null; - for ( com.sun.jdi.connect.ListeningConnector conn : com.sun.jdi.Bootstrap.virtualMachineManager().listeningConnectors() ) - { - first = conn; - break; - } - connector = first; - } - private final Map> breakpoints = new HashMap<>(); - private final Map args; - - DebuggerConnector( BreakPoint[] breakpoints ) - { - this.args = connector.defaultArguments(); - for ( BreakPoint breakpoint : breakpoints ) - { - List list = this.breakpoints.get( breakpoint.type ); - if ( list == null ) - { - this.breakpoints.put( breakpoint.type, list = new ArrayList<>() ); - } - list.add( breakpoint ); - } - } - - String listen() - { - try - { - return String.format( "-agentlib:jdwp=transport=%s,address=%s", connector.transport().name(), - connector.startListening( args ) ); - } - catch ( Exception e ) - { - throw new UnsupportedOperationException( "Debugger not supported", e ); - } - } - - DebugDispatch connect( String string ) - { - final com.sun.jdi.VirtualMachine vm; - try - { - vm = connector.accept( args ); - connector.stopListening( args ); - } - catch ( Exception e ) - { - throw new RuntimeException( "Debugger connection failure", e ); - } - com.sun.jdi.request.EventRequestManager erm = vm.eventRequestManager(); - TYPES: for ( Map.Entry> entry : breakpoints.entrySet() ) - { - for ( com.sun.jdi.ReferenceType type : vm.classesByName( entry.getKey() ) ) - { - if ( type.name().equals( entry.getKey() ) ) - { - for ( BreakPoint breakpoint : entry.getValue() ) - { - breakpoint.setup( type ); - } - continue TYPES; - } - } - com.sun.jdi.request.ClassPrepareRequest prepare = erm.createClassPrepareRequest(); - prepare.addClassFilter( entry.getKey() ); - prepare.enable(); - } - if ( vm.canRequestMonitorEvents() ) - { - erm.createMonitorContendedEnterRequest().enable(); - } - DebugDispatch dispatch = new DebugDispatch( vm.eventQueue(), breakpoints ); - new Thread( dispatch, "Debugger: [" + string + "]" ).start(); - return dispatch; - } - } - - @SuppressWarnings( "restriction" ) - static class DebugDispatch implements Runnable - { - static DebugDispatch get( Object o ) - { - if ( Proxy.isProxyClass( o.getClass() ) ) - { - InvocationHandler handler = Proxy.getInvocationHandler( o ); - if ( handler instanceof Handler ) - { - return ( (Handler) handler ).debugDispatch; - } - } - throw new IllegalArgumentException( "Not a sub process: " + o ); - } - - volatile Handler handler; - private final com.sun.jdi.event.EventQueue queue; - private final Map> breakpoints; - private final Map suspended = new HashMap<>(); - static final DebuggerDeadlockCallback defaultCallback = new DebuggerDeadlockCallback() - { - @Override - public void deadlock( DebuggedThread thread ) - { - throw new DeadlockDetectedError(); - } - }; - - DebugDispatch( com.sun.jdi.event.EventQueue queue, Map> breakpoints ) - { - this.queue = queue; - this.breakpoints = breakpoints; - } - - @Override - public void run() - { - for ( ;; ) - { - final com.sun.jdi.event.EventSet events; - try - { - events = queue.remove(); - } - catch ( InterruptedException e ) - { - return; - } - Integer exitCode = null; - try - { - for ( com.sun.jdi.event.Event event : events ) - { - if ( event instanceof com.sun.jdi.event.MonitorContendedEnterEvent ) - { - com.sun.jdi.event.MonitorContendedEnterEvent monitor = (com.sun.jdi.event.MonitorContendedEnterEvent) event; - final com.sun.jdi.ThreadReference thread; - try - { - thread = monitor.monitor().owningThread(); - } - catch ( com.sun.jdi.IncompatibleThreadStateException e ) - { - e.printStackTrace(); - continue; - } - if ( thread != null && thread.isSuspended() ) - { - DebuggerDeadlockCallback callback = suspended.get( thread ); - try - { - if ( callback != null ) - { - callback.deadlock( new DebuggedThread( this, thread ) ); - } - } - catch ( DeadlockDetectedError deadlock ) - { - @SuppressWarnings( "hiding" ) Handler handler = this.handler; - if ( handler != null ) - { - handler.kill( false ); - } - } - } - } - else if ( event instanceof com.sun.jdi.event.LocatableEvent ) - { - callback( (com.sun.jdi.event.LocatableEvent) event ); - } - else if ( event instanceof com.sun.jdi.event.ClassPrepareEvent ) - { - setup( ( (com.sun.jdi.event.ClassPrepareEvent) event ).referenceType() ); - } - else if ( event instanceof com.sun.jdi.event.VMDisconnectEvent - || event instanceof com.sun.jdi.event.VMDeathEvent ) - { - return; - } - } - } - catch ( KillSubProcess kill ) - { - exitCode = kill.exitCode; - } - finally - { - if ( exitCode != null ) - { - events.virtualMachine().exit( exitCode ); - } - else - { - events.resume(); - } - } - } - } - - private void setup( com.sun.jdi.ReferenceType type ) - { - List list = breakpoints.get( type.name() ); - if ( list == null ) - { - return; - } - for ( BreakPoint breakpoint : list ) - { - breakpoint.setup( type ); - } - } - - private void callback( com.sun.jdi.event.LocatableEvent event ) throws KillSubProcess - { - List list = breakpoints.get( event.location().declaringType().name() ); - if ( list == null ) - { - return; - } - com.sun.jdi.Method method = event.location().method(); - for ( BreakPoint breakpoint : list ) - { - if ( breakpoint.matches( method.name(), method.argumentTypeNames() ) ) - { - if ( breakpoint.enabled ) - { - breakpoint.invoke( new DebugInterface( this, event ) ); - } - } - } - } - - void suspended( com.sun.jdi.ThreadReference thread, DebuggerDeadlockCallback callback ) - { - if ( callback == null ) - { - callback = defaultCallback; - } - suspended.put( thread, callback ); - } - - void resume( com.sun.jdi.ThreadReference thread ) - { - suspended.remove( thread ); - } - - DebuggedThread[] suspendedThreads() - { - if ( suspended.isEmpty() ) - { - return new DebuggedThread[0]; - } - List threads = new ArrayList<>(); - for ( com.sun.jdi.ThreadReference thread : suspended.keySet() ) - { - threads.add( new DebuggedThread( this, thread ) ); - } - return threads.toArray( new DebuggedThread[threads.size()] ); - } - } - - static class DeadlockDetectedError extends Error - { - @Override - public Throwable fillInStackTrace() - { - return this; - } - } - protected abstract void startup( P parameter ) throws Throwable; public final void shutdown() @@ -866,7 +562,7 @@ private interface Dispatcher extends Remote { void stop() throws RemoteException; - Object dispatch( String name, String[] types, Object[] args ) throws RemoteException, Throwable; + Object dispatch( String name, String[] types, Object[] args ) throws Throwable; } private static InvocationHandler live( Handler handler ) @@ -946,15 +642,13 @@ private static class Handler implements InvocationHandler private final Process process; private final Class type; private final String repr; - private final DebugDispatch debugDispatch; - Handler( Class type, Dispatcher dispatcher, Process process, String repr, DebugDispatch debugDispatch ) + Handler( Class type, Dispatcher dispatcher, Process process, String repr ) { this.type = type; this.dispatcher = dispatcher; this.process = process; this.repr = repr; - this.debugDispatch = debugDispatch; } @Override @@ -1061,7 +755,7 @@ private Object dispatch( Method method, Object[] args ) throws Throwable private static class DispatcherImpl extends UnicastRemoteObject implements Dispatcher { - private transient final SubProcess subprocess; + private final transient SubProcess subprocess; protected DispatcherImpl( SubProcess subprocess ) throws RemoteException { diff --git a/community/kernel/src/test/java/org/neo4j/test/subprocess/SubProcessTestRunner.java b/community/kernel/src/test/java/org/neo4j/test/subprocess/SubProcessTestRunner.java deleted file mode 100644 index ea65da2087337..0000000000000 --- a/community/kernel/src/test/java/org/neo4j/test/subprocess/SubProcessTestRunner.java +++ /dev/null @@ -1,632 +0,0 @@ -/* - * Copyright (c) 2002-2015 "Neo Technology," - * Network Engine for Objects in Lund AB [http://neotechnology.com] - * - * This file is part of Neo4j. - * - * Neo4j is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package org.neo4j.test.subprocess; - -import java.lang.annotation.Annotation; -import java.lang.reflect.Method; -import java.lang.reflect.ParameterizedType; -import java.lang.reflect.Type; -import java.rmi.Remote; -import java.rmi.RemoteException; -import java.rmi.server.UnicastRemoteObject; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; -import org.junit.internal.runners.model.EachTestNotifier; -import org.junit.internal.runners.statements.RunBefores; -import org.junit.runner.notification.Failure; -import org.junit.runner.notification.RunListener; -import org.junit.runner.notification.RunNotifier; -import org.junit.runners.BlockJUnit4ClassRunner; -import org.junit.runners.model.FrameworkMethod; -import org.junit.runners.model.InitializationError; -import org.junit.runners.model.MultipleFailureException; -import org.junit.runners.model.Statement; -import org.neo4j.helpers.Exceptions; -import org.neo4j.test.subprocess.ForeignBreakpoints.BreakpointDef; -import org.neo4j.test.subprocess.SubProcess.DebugDispatch; - -public class SubProcessTestRunner extends BlockJUnit4ClassRunner -{ - public SubProcessTestRunner( Class test ) throws InitializationError - { - super( test ); - } - - private final Map breakpoints = new HashMap(); - private volatile TestRunnerDispatcher dispatcher; - //private final SequencingManager seqManager = null; - private final Task.Executor taskExecutor = new Task.Executor() - { - @Override - public void submit( final Task task ) - { - new Thread() - { - @Override - public void run() - { - try - { - dispatcher.submit( task ); - } - catch ( Throwable e ) - { - e.printStackTrace(); - } - } - }.start(); - } - }; - - @Override - protected void collectInitializationErrors( java.util.List errors ) - { - super.collectInitializationErrors( errors ); - - for ( FrameworkMethod handler : getTestClass().getAnnotatedMethods( BreakpointHandler.class ) ) - { - handler.validatePublicVoid( /*static:*/true, errors ); - } - for ( FrameworkMethod beforeDebugged : getTestClass().getAnnotatedMethods( BeforeDebuggedTest.class ) ) - { - beforeDebugged.validatePublicVoidNoArg( /*static:*/true, errors ); - } - } - - private void startSubProcess( RunNotifier notifier ) throws Throwable - { - EachTestNotifier eachNotifier = new EachTestNotifier( notifier, getDescription() ); - NotifierImpl remoteNotifier = new NotifierImpl( eachNotifier ); - this.dispatcher = new TestProcess( getTestClass().getJavaClass().getName() ).start( remoteNotifier, - breakpoints() ); - } - - private void stopSubProcess() - { - try - { - SubProcess.stop( dispatcher ); - } - finally - { - dispatcher = null; - } - } - - @Override - protected Statement classBlock( final RunNotifier notifier ) - { - final Statement children = childrenInvoker( notifier ); - return new Statement() - { - @Override - public void evaluate() throws Throwable - { - startSubProcess( notifier ); - try - { - children.evaluate(); - } - finally - { - stopSubProcess(); - } - } - }; - } - - private BreakPoint[] breakpoints() throws Throwable - { - if ( breakpoints.isEmpty() ) - { - synchronized ( breakpoints ) - { - if ( breakpoints.isEmpty() ) - { - List failures = new ArrayList(); - final Object CLAIMED = new Object(); - Map bpDefs = new HashMap(); - ForeignBreakpoints foreign = getTestClass().getJavaClass().getAnnotation( ForeignBreakpoints.class ); - if ( foreign != null ) for ( BreakpointDef def : foreign.value() ) - { - String name = def.name(); - if ( name.isEmpty() ) name = def.method(); - if ( null != bpDefs.put( name, def ) ) - failures.add( new Exception( "Multiple definitions of the breakpoint \"" + name + "\"" ) ); - } - for ( FrameworkMethod method : getTestClass().getAnnotatedMethods( BreakpointTrigger.class ) ) - { - String name = method.getAnnotation( BreakpointTrigger.class ).value(); - if ( name.isEmpty() ) name = method.getName(); - if ( null != bpDefs.put( name, method ) ) - failures.add( new Exception( "Multiple definitions of the breakpoint \"" + name + "\"" ) ); - } - for ( FrameworkMethod handler : getTestClass().getAnnotatedMethods( BreakpointHandler.class ) ) - { - for ( String name : handler.getAnnotation( BreakpointHandler.class ).value() ) - { - Object bp = bpDefs.get( name ); - if ( bp == null ) - { - failures.add( new Exception( "No such breakpoint: \"" + name + "\", referenced from: " - + handler ) ); - } - else if ( bp == CLAIMED ) - { - failures.add( new Exception( "Multiple handlers for breakpoint: \"" + name - + "\", referenced from: " + handler ) ); - } - else if ( bp instanceof BreakpointDef ) - { - try - { - for ( BreakpointDispatcher dispatch : createForeignBreakpoints( (BreakpointDef) bp, - handler ) ) - { - breakpoints.put( name, dispatch ); - } - } - catch ( Exception exc ) - { - failures.add( exc ); - } - } - else if ( bp instanceof FrameworkMethod ) - { - breakpoints.put( name, new BreakpointDispatcher( - ( (FrameworkMethod) bp ).getAnnotation( BreakpointTrigger.class ).on(), - ( (FrameworkMethod) bp ).getMethod().getDeclaringClass(), - ( (FrameworkMethod) bp ).getMethod(), handler ) ); - } - else - { - failures.add( new Exception( "Internal error, unknown breakpoint def: " + bp ) ); - } - bpDefs.put( name, CLAIMED ); - } - } - if ( bpDefs.size() != breakpoints.size() ) for ( Object bp : bpDefs.values() ) - { - if ( bp != CLAIMED ) failures.add( new Exception( "Unhandled breakpoint: " + bp ) ); - } - if ( !failures.isEmpty() ) - { - if ( failures.size() == 1 ) throw failures.get( 0 ); - throw new MultipleFailureException( failures ); - } - } - } - } - return breakpoints.values().toArray( new BreakPoint[breakpoints.size()] ); - } - - Iterable createForeignBreakpoints( BreakpointDef def, FrameworkMethod handler ) throws Exception - { - Class type = Class.forName( def.type() ); - List result = new ArrayList( 1 ); - for ( Method method : type.getDeclaredMethods() ) if ( method.getName().equals( def.method() ) ) - { - result.add( new BreakpointDispatcher( def.on(), type, method, handler ) ); - } - if ( result.isEmpty() ) throw new Exception( "No such method: " + def ); - return result; - } - - private class BreakpointDispatcher extends BreakPoint - { - private final FrameworkMethod handler; - - BreakpointDispatcher( BreakPoint.Event event, Class type, Method method, FrameworkMethod handler ) - { - super( event, type, method.getName(), method.getParameterTypes() ); - this.handler = handler; - } - - @Override - protected void callback( DebugInterface debug ) throws KillSubProcess - { - Class[] types = handler.getMethod().getParameterTypes(); - Annotation[][] annotations = handler.getMethod().getParameterAnnotations(); - Object[] params = new Object[types.length]; - BUILD_PARAM_LIST: for ( int i = 0; i < types.length; i++ ) - { - if ( types[i] == DebugInterface.class ) - params[i] = debug; - else if ( types[i] == BreakPoint.class ) - { - for ( Annotation annotation : annotations[i] ) - { - if ( BreakpointHandler.class.isInstance( annotation ) ) - { - String[] value = ( (BreakpointHandler) annotation ).value(); - if ( value.length == 1 ) - { - params[i] = breakpoints.get( value[0] ); - continue BUILD_PARAM_LIST; - } - } - } - params[i] = this; - } - /* - else if ( types[i] == SequencingManager.class ) - params[i] = seqManager; - */ - else if ( types[i] == Task.Executor.class ) - params[i] = taskExecutor; - else - params[i] = null; - } - try - { - handler.invokeExplosively( null, params ); - } - catch ( Throwable exception ) - { - throw Exceptions.launderedException( KillSubProcess.class, exception ); - } - } - } - - @Override - protected Statement methodBlock( final FrameworkMethod method ) - { - Statement statement = new Statement() - { - @Override - public void evaluate() throws Throwable - { - enableBreakpoints( method.getAnnotation( EnabledBreakpoints.class ) ); - dispatcher.run( method.getName() ); - } - }; - List befores = getTestClass().getAnnotatedMethods( BeforeDebuggedTest.class ); - if ( !befores.isEmpty() ) statement = new RunBefores( statement, befores, null ); - return statement; - } - - private void enableBreakpoints( EnabledBreakpoints breakpoints ) - { - Set enabled = new HashSet(); - if ( breakpoints != null ) for ( String name : breakpoints.value() ) - { - enabled.add( name ); - } - for ( Map.Entry entry : this.breakpoints.entrySet() ) - { - BreakPoint bp = entry.getValue(); - ( enabled.remove( entry.getKey() ) ? bp.enable() : bp.disable() ).resetInvocationCount(); - } - if ( !enabled.isEmpty() ) throw new IllegalArgumentException( "Unknown breakpoints: " + enabled ); - } - - private void verifyBreakpointState() throws SuspendedThreadsException - { - DebugDispatch debugger = SubProcess.DebugDispatch.get( dispatcher ); - // if there are no breakpoints we will have no debugger - DebuggedThread[] threads = (debugger == null) ? new DebuggedThread[0] : debugger.suspendedThreads(); - if ( threads.length != 0 ) - { - String[] names = new String[threads.length]; - for ( int i = 0; i < threads.length; i++ ) - { - names[i] = threads[i].name(); - threads[i].resume(); - } - throw new SuspendedThreadsException( names ); - } - } - - interface TestRunnerDispatcher - { - void submit( Task task ) throws Throwable; - - void run( String methodName ) throws Throwable; - } - - private interface RemoteRunNotifier extends Remote - { - void failure( Throwable exception ) throws RemoteException; - - void checkPostConditions() throws RemoteException, Throwable; - } - - private class NotifierImpl extends UnicastRemoteObject implements RemoteRunNotifier - { - private static final long serialVersionUID = 1L; - private final EachTestNotifier notifier; - - NotifierImpl( EachTestNotifier notifier ) throws RemoteException - { - super(); - this.notifier = notifier; - } - - @Override - public void failure( Throwable exception ) throws RemoteException - { - notifier.addFailure( exception ); - } - - @Override - public void checkPostConditions() throws Throwable - { - verifyBreakpointState(); - } - } - - private static class RemoteRunListener extends RunListener - { - private final RemoteRunNotifier remote; - - public RemoteRunListener( RemoteRunNotifier remote ) - { - this.remote = remote; - } - - @Override - public void testFailure( Failure failure ) throws Exception - { - remote.failure( failure.getException() ); - } - } - - public static class RunTerminatedException extends RuntimeException - { - private static final long serialVersionUID = 1L; - - private RunTerminatedException() - { - // all instances are created here - } - } - - private static final Object TERMINATED = new Object(); - - private static class TestProcess extends SubProcess implements - TestRunnerDispatcher - { - private static final long serialVersionUID = 1L; - private final String className; - - public TestProcess(String className) - { - this.className = className; - } - private volatile Object runner; - - @Override - protected void startup( RemoteRunNotifier remote ) throws Throwable - { - try - { - RunNotifier notifier = new RunNotifier(); - notifier.addListener( new RemoteRunListener( remote ) ); - new RemoteTestRunner( this, remote, Class.forName( className ) ).run( notifier ); - } - catch ( Throwable failure ) - { - runner = failure; - throw failure; - } - } - - @Override - public String toString() - { - return className.substring( className.lastIndexOf( '.' ) + 1); - } - - @Override - protected void shutdown( boolean normal ) - { - try - { - Object runner = this.runner; - if ( runner instanceof RemoteTestRunner ) ( (RemoteTestRunner) runner ).terminate(); - } - finally - { - this.runner = new RunTerminatedException(); - } - super.shutdown( normal ); - } - - @Override - public void run( String methodName ) throws Throwable - { - runner().run( methodName ); - } - - @Override - public void submit( Task task ) throws Throwable - { - runner().run( task ); - } - - private RemoteTestRunner runner() throws Throwable - { - for ( Object runner = this.runner;; runner = this.runner ) - { - if ( runner instanceof RemoteTestRunner ) - { - return (RemoteTestRunner) runner; - } - else if ( runner instanceof Throwable ) - { - throw (Throwable) runner; - } - Thread.sleep( 1 ); // yield - } - } - } - - private static class RemoteTestRunner extends BlockJUnit4ClassRunner - { - private final TestProcess testProcess; - private final RemoteRunNotifier host; - private volatile Object testMethod; - private volatile Object test; - - RemoteTestRunner( TestProcess testProcess, RemoteRunNotifier host, Class test ) throws InitializationError - { - super( test ); - this.testProcess = testProcess; - this.host = host; - } - - void terminate() - { - testMethod = TERMINATED; - } - - @Override - protected Statement childrenInvoker( RunNotifier notifier ) - { - return new Statement() - { - @Override - public void evaluate() - { - testProcess.runner = RemoteTestRunner.this; - for ( Object test = testMethod;; test = testMethod ) - { - if ( test == null || test instanceof Throwable ) - { - try - { - Thread.sleep( 1 ); // yield - } - catch ( InterruptedException e ) - { - testMethod = e; - break; - } - } - else if ( test instanceof FrameworkMethod ) - { - try - { - methodBlock( (FrameworkMethod) test ).evaluate(); - testMethod = null; - } - catch ( Throwable e ) - { - testMethod = e; - } - } - else break; // received poison pill - } - } - }; - } - - @Override - protected Statement methodBlock( FrameworkMethod method ) - { - final Statement statement = super.methodBlock( method ); - return statement; - } - - @Override - protected Statement methodInvoker( FrameworkMethod method, Object test ) - { - this.test = test; - final Statement statement = super.methodInvoker( method, test ); - return new Statement() - { - @Override - public void evaluate() throws Throwable - { - statement.evaluate(); - host.checkPostConditions(); - } - }; - } - - private Map methods; - - void run( String methodName ) throws Throwable - { - if ( methods == null ) - { - Map map = new HashMap(); - for ( FrameworkMethod method : getChildren() ) - { - map.put( method.getName(), method ); - } - methods = map; - } - testMethod = methods.get( methodName ); - for ( Object result = testMethod;; result = testMethod ) - { - if ( result instanceof FrameworkMethod ) - { - Thread.sleep( 1 ); // yield - } - else if ( result instanceof Throwable ) - { - throw (Throwable) result; - } - else return; // success - } - } - - void run( Task task ) - { - task.run( inject( typeOf( task ) ) ); - } - - private T inject( Class type ) - { - if ( type == null ) /*could not find concrete parameter type*/return null; - Object test = this.test; - if ( type.isInstance( test ) ) return type.cast( test ); - return null; // TODO: what other things should we be able to inject into tasks? - } - - @SuppressWarnings( "unchecked" ) - private static Class typeOf( Task task ) - { - Class taskType = task.getClass(); - while ( taskType != Object.class ) - { - for ( Type type : taskType.getGenericInterfaces() ) - { - if ( type instanceof ParameterizedType ) - { - ParameterizedType paramType = (ParameterizedType) type; - if ( paramType.getRawType() == Task.class ) - { - Type param = paramType.getActualTypeArguments()[0]; - if ( param.getClass() == Class.class ) return (Class) param; - } - } - } - taskType = taskType.getSuperclass(); - } - return null; - } - } -} diff --git a/community/kernel/src/test/java/org/neo4j/test/subprocess/SuspendedThreadsException.java b/community/kernel/src/test/java/org/neo4j/test/subprocess/SuspendedThreadsException.java deleted file mode 100644 index b3669a554404b..0000000000000 --- a/community/kernel/src/test/java/org/neo4j/test/subprocess/SuspendedThreadsException.java +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright (c) 2002-2015 "Neo Technology," - * Network Engine for Objects in Lund AB [http://neotechnology.com] - * - * This file is part of Neo4j. - * - * Neo4j is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package org.neo4j.test.subprocess; - -import java.util.Arrays; -import java.util.Iterator; - -public class SuspendedThreadsException extends Exception implements Iterable -{ - private final String[] threadNames; - - SuspendedThreadsException( String... threadNames ) - { - super( message( threadNames ) ); - this.threadNames = threadNames.clone(); - } - - private static String message( String[] threadName ) - { - if ( threadName == null || threadName.length == 0 ) - throw new IllegalArgumentException( "No thread names given" ); - if ( threadName.length == 1 ) - { - return "The \"" + threadName[0] + "\" thread is still suspended."; - } - else - { - StringBuilder message = new StringBuilder( "The following threads are still suspended" ); - String sep = ": "; - for ( String name : threadName ) - { - message.append( sep ).append( '"' ).append( name ).append( '"' ); - sep = ", "; - } - return message.toString(); - } - } - - @Override - public Iterator iterator() - { - return Arrays.asList( threadNames ).iterator(); - } -} diff --git a/community/kernel/src/test/java/org/neo4j/test/subprocess/Task.java b/community/kernel/src/test/java/org/neo4j/test/subprocess/Task.java deleted file mode 100644 index d2c57ca20cdf1..0000000000000 --- a/community/kernel/src/test/java/org/neo4j/test/subprocess/Task.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright (c) 2002-2015 "Neo Technology," - * Network Engine for Objects in Lund AB [http://neotechnology.com] - * - * This file is part of Neo4j. - * - * Neo4j is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package org.neo4j.test.subprocess; - -import java.io.Serializable; - -public interface Task extends Serializable -{ - void run( T parameter ); - - interface Executor - { - /** submits a task for asynchronous execution */ - void submit( Task task ); - } -}