maven-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From tibordig...@apache.org
Subject maven-surefire git commit: [SUREFIRE-1342] Acknowledge normal exit of JVM and drain shared memory between processes [Forced Update!]
Date Tue, 28 Mar 2017 17:15:12 GMT
Repository: maven-surefire
Updated Branches:
  refs/heads/SUREFIRE-1342 9bc02fd86 -> da7ff6aa2 (forced update)


[SUREFIRE-1342] Acknowledge normal exit of JVM and drain shared memory between processes


Project: http://git-wip-us.apache.org/repos/asf/maven-surefire/repo
Commit: http://git-wip-us.apache.org/repos/asf/maven-surefire/commit/da7ff6aa
Tree: http://git-wip-us.apache.org/repos/asf/maven-surefire/tree/da7ff6aa
Diff: http://git-wip-us.apache.org/repos/asf/maven-surefire/diff/da7ff6aa

Branch: refs/heads/SUREFIRE-1342
Commit: da7ff6aa25a72094705c24981afbf8294ac4b1dc
Parents: d468680
Author: Tibor17 <tibor17@lycos.com>
Authored: Mon Mar 13 03:10:08 2017 +0100
Committer: Tibor17 <tibor17@lycos.com>
Committed: Tue Mar 28 19:09:41 2017 +0200

----------------------------------------------------------------------
 .../surefire/booterclient/ForkStarter.java      |   1 +
 .../lazytestprovider/AbstractCommandStream.java |   8 +-
 .../lazytestprovider/NotifiableTestStream.java  |   2 +
 .../lazytestprovider/TestLessInputStream.java   |  49 +++++++-
 .../TestProvidingInputStream.java               |  18 +--
 .../booterclient/output/ForkClient.java         |   1 +
 .../output/ThreadedStreamConsumer.java          |  40 +++---
 .../booterclient/MockNotifiableTestStream.java  |   5 +
 .../TestProvidingInputStreamTest.java           |   1 +
 .../apache/maven/surefire/booter/Command.java   |   1 +
 .../maven/surefire/booter/CommandReader.java    |  22 ++--
 .../surefire/booter/MasterProcessCommand.java   |   3 +-
 .../apache/maven/surefire/booter/Shutdown.java  |  15 +++
 .../surefire/util/internal/DumpFileUtils.java   | 126 ++++++++++++++++++-
 .../booter/MasterProcessCommandTest.java        |   7 ++
 .../maven/surefire/booter/ForkedBooter.java     | 124 ++++++++++++------
 .../jiras/Surefire141PluggableProvidersIT.java  |  33 +++--
 17 files changed, 362 insertions(+), 94 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/da7ff6aa/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/ForkStarter.java
----------------------------------------------------------------------
diff --git a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/ForkStarter.java
b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/ForkStarter.java
index 54d304a..b69d010 100644
--- a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/ForkStarter.java
+++ b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/ForkStarter.java
@@ -644,6 +644,7 @@ public class ForkStarter
         }
         finally
         {
+            currentForkClients.remove( forkClient );
             closer.close();
             if ( runResult == null )
             {

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/da7ff6aa/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/lazytestprovider/AbstractCommandStream.java
----------------------------------------------------------------------
diff --git a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/lazytestprovider/AbstractCommandStream.java
b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/lazytestprovider/AbstractCommandStream.java
index 4d6331c..3531ccf 100644
--- a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/lazytestprovider/AbstractCommandStream.java
+++ b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/lazytestprovider/AbstractCommandStream.java
@@ -41,10 +41,12 @@ public abstract class AbstractCommandStream
     protected abstract boolean isClosed();
 
     /**
-     * Unnecessarily opposite to {@link #isClosed()} however may respect
-     * {@link #getLastCommand() last command} and {@link #isClosed()}.
+     * Opposite to {@link #isClosed()}.
      */
-    protected abstract boolean canContinue();
+    protected boolean canContinue()
+    {
+        return !isClosed();
+    }
 
     /**
      * Possibly waiting for next command (see {@link #nextCommand()}) unless the stream is
atomically

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/da7ff6aa/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/lazytestprovider/NotifiableTestStream.java
----------------------------------------------------------------------
diff --git a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/lazytestprovider/NotifiableTestStream.java
b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/lazytestprovider/NotifiableTestStream.java
index 5c89173..b181de1 100644
--- a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/lazytestprovider/NotifiableTestStream.java
+++ b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/lazytestprovider/NotifiableTestStream.java
@@ -45,4 +45,6 @@ public interface NotifiableTestStream
     void shutdown( Shutdown shutdownType );
 
     void noop();
+
+    void acknowledgeByeEventReceived();
 }

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/da7ff6aa/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/lazytestprovider/TestLessInputStream.java
----------------------------------------------------------------------
diff --git a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/lazytestprovider/TestLessInputStream.java
b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/lazytestprovider/TestLessInputStream.java
index b6ae42c..d0ae47a 100644
--- a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/lazytestprovider/TestLessInputStream.java
+++ b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/lazytestprovider/TestLessInputStream.java
@@ -33,6 +33,7 @@ import java.util.concurrent.atomic.AtomicReference;
 import java.util.concurrent.locks.Lock;
 import java.util.concurrent.locks.ReentrantReadWriteLock;
 
+import static org.apache.maven.surefire.booter.Command.BYE_ACK;
 import static org.apache.maven.surefire.booter.Command.NOOP;
 import static org.apache.maven.surefire.booter.Command.SKIP_SINCE_NEXT_TEST;
 import static org.apache.maven.surefire.booter.Command.toShutdown;
@@ -93,15 +94,19 @@ public final class TestLessInputStream
     }
 
     @Override
-    protected boolean isClosed()
+    public void acknowledgeByeEventReceived()
     {
-        return closed.get();
+        if ( canContinue() )
+        {
+            immediateCommands.add( BYE_ACK );
+            barrier.release();
+        }
     }
 
     @Override
-    protected boolean canContinue()
+    protected boolean isClosed()
     {
-        return !isClosed();
+        return closed.get();
     }
 
     @Override
@@ -356,6 +361,24 @@ public final class TestLessInputStream
                     lock.unlock();
                 }
             }
+
+            @Override
+            public void acknowledgeByeEventReceived()
+            {
+                Lock lock = rwLock.readLock();
+                lock.lock();
+                try
+                {
+                    for ( TestLessInputStream aliveStream : TestLessInputStreamBuilder.this.aliveStreams
)
+                    {
+                        aliveStream.acknowledgeByeEventReceived();
+                    }
+                }
+                finally
+                {
+                    lock.unlock();
+                }
+            }
         }
 
         /**
@@ -419,6 +442,24 @@ public final class TestLessInputStream
                 }
             }
 
+            @Override
+            public void acknowledgeByeEventReceived()
+            {
+                Lock lock = rwLock.readLock();
+                lock.lock();
+                try
+                {
+                    if ( TestLessInputStreamBuilder.this.addTailNodeIfAbsent( BYE_ACK ) )
+                    {
+                        release();
+                    }
+                }
+                finally
+                {
+                    lock.unlock();
+                }
+            }
+
             private void release()
             {
                 for ( TestLessInputStream aliveStream : TestLessInputStreamBuilder.this.aliveStreams
)

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/da7ff6aa/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/lazytestprovider/TestProvidingInputStream.java
----------------------------------------------------------------------
diff --git a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/lazytestprovider/TestProvidingInputStream.java
b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/lazytestprovider/TestProvidingInputStream.java
index 766843d..69f73a2 100644
--- a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/lazytestprovider/TestProvidingInputStream.java
+++ b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/lazytestprovider/TestProvidingInputStream.java
@@ -28,7 +28,7 @@ import java.util.concurrent.ConcurrentLinkedQueue;
 import java.util.concurrent.Semaphore;
 import java.util.concurrent.atomic.AtomicBoolean;
 
-import static org.apache.maven.surefire.booter.MasterProcessCommand.TEST_SET_FINISHED;
+import static org.apache.maven.surefire.booter.Command.BYE_ACK;
 import static org.apache.maven.surefire.booter.Command.NOOP;
 import static org.apache.maven.surefire.booter.Command.SKIP_SINCE_NEXT_TEST;
 import static org.apache.maven.surefire.booter.Command.toRunClass;
@@ -110,6 +110,16 @@ public final class TestProvidingInputStream
     }
 
     @Override
+    public void acknowledgeByeEventReceived()
+    {
+        if ( canContinue() )
+        {
+            commands.add( BYE_ACK );
+            barrier.release();
+        }
+    }
+
+    @Override
     protected Command nextCommand()
     {
         Command cmd = commands.poll();
@@ -137,12 +147,6 @@ public final class TestProvidingInputStream
         return closed.get();
     }
 
-    @Override
-    protected boolean canContinue()
-    {
-        return getLastCommand() != TEST_SET_FINISHED && !isClosed();
-    }
-
     /**
      * Signal that a new test is to be provided.
      */

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/da7ff6aa/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/output/ForkClient.java
----------------------------------------------------------------------
diff --git a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/output/ForkClient.java
b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/output/ForkClient.java
index d6afcb2..faace0f 100644
--- a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/output/ForkClient.java
+++ b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/output/ForkClient.java
@@ -265,6 +265,7 @@ public class ForkClient
                     break;
                 case BOOTERCODE_BYE:
                     saidGoodBye = true;
+                    notifiableTestStream.acknowledgeByeEventReceived();
                     break;
                 case BOOTERCODE_STOP_ON_NEXT_TEST:
                     stopOnNextTest();

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/da7ff6aa/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/output/ThreadedStreamConsumer.java
----------------------------------------------------------------------
diff --git a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/output/ThreadedStreamConsumer.java
b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/output/ThreadedStreamConsumer.java
index c7d39ae..ff9c238 100644
--- a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/output/ThreadedStreamConsumer.java
+++ b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/output/ThreadedStreamConsumer.java
@@ -26,6 +26,7 @@ import java.io.Closeable;
 import java.io.IOException;
 import java.util.concurrent.ArrayBlockingQueue;
 import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.atomic.AtomicBoolean;
 
 import static java.lang.Thread.currentThread;
 
@@ -41,16 +42,14 @@ public final class ThreadedStreamConsumer
 
     private static final int ITEM_LIMIT_BEFORE_SLEEP = 10 * 1000;
 
-    private static final long CLOSE_TIMEOUT_MILLIS = 5 * 60 * 1000L;
-
     private final BlockingQueue<String> items = new ArrayBlockingQueue<String>(
ITEM_LIMIT_BEFORE_SLEEP );
 
+    private final AtomicBoolean stop = new AtomicBoolean();
+
     private final Thread thread;
 
     private final Pumper pumper;
 
-    private volatile boolean stop;
-
     final class Pumper
             implements Runnable
     {
@@ -76,14 +75,14 @@ public final class ThreadedStreamConsumer
          */
         public void run()
         {
-            while ( !ThreadedStreamConsumer.this.stop )
+            while ( !ThreadedStreamConsumer.this.stop.get() )
             {
                 try
                 {
                     String item = ThreadedStreamConsumer.this.items.take();
                     if ( shouldStopQueueing( item ) )
                     {
-                        break;
+                        return;
                     }
                     target.consumeLine( item );
                 }
@@ -114,7 +113,7 @@ public final class ThreadedStreamConsumer
 
     public void consumeLine( String s )
     {
-        if ( stop && !thread.isAlive() )
+        if ( stop.get() && !thread.isAlive() )
         {
             items.clear();
             return;
@@ -134,24 +133,17 @@ public final class ThreadedStreamConsumer
     public void close()
             throws IOException
     {
-        if ( stop )
-        {
-            return;
-        }
-
-        try
+        if ( stop.compareAndSet( false, true ) )
         {
-            items.put( END_ITEM );
-            thread.join( CLOSE_TIMEOUT_MILLIS );
-        }
-        catch ( InterruptedException e )
-        {
-            currentThread().interrupt();
-            throw new IOException( e );
-        }
-        finally
-        {
-            stop = true;
+            items.clear();
+            try
+            {
+                items.put( END_ITEM );
+            }
+            catch ( InterruptedException e )
+            {
+                currentThread().interrupt();
+            }
         }
 
         if ( pumper.hasErrors() )

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/da7ff6aa/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/MockNotifiableTestStream.java
----------------------------------------------------------------------
diff --git a/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/MockNotifiableTestStream.java
b/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/MockNotifiableTestStream.java
index 12a5f92..9e94e7f 100644
--- a/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/MockNotifiableTestStream.java
+++ b/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/MockNotifiableTestStream.java
@@ -46,4 +46,9 @@ final class MockNotifiableTestStream
     public void noop()
     {
     }
+
+    @Override
+    public void acknowledgeByeEventReceived()
+    {
+    }
 }
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/da7ff6aa/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/lazytestprovider/TestProvidingInputStreamTest.java
----------------------------------------------------------------------
diff --git a/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/lazytestprovider/TestProvidingInputStreamTest.java
b/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/lazytestprovider/TestProvidingInputStreamTest.java
index 6fc171e..b117a38 100644
--- a/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/lazytestprovider/TestProvidingInputStreamTest.java
+++ b/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/lazytestprovider/TestProvidingInputStreamTest.java
@@ -95,6 +95,7 @@ public class TestProvidingInputStreamTest
         assertThat( is.read(), is( 0 ) );
         assertThat( is.read(), is( 0 ) );
         assertThat( is.read(), is( 0 ) );
+        is.close();
         assertThat( is.read(), is( -1 ) );
     }
 

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/da7ff6aa/surefire-api/src/main/java/org/apache/maven/surefire/booter/Command.java
----------------------------------------------------------------------
diff --git a/surefire-api/src/main/java/org/apache/maven/surefire/booter/Command.java b/surefire-api/src/main/java/org/apache/maven/surefire/booter/Command.java
index 49ae52d..5024992 100644
--- a/surefire-api/src/main/java/org/apache/maven/surefire/booter/Command.java
+++ b/surefire-api/src/main/java/org/apache/maven/surefire/booter/Command.java
@@ -36,6 +36,7 @@ public final class Command
     public static final Command TEST_SET_FINISHED = new Command( MasterProcessCommand.TEST_SET_FINISHED
);
     public static final Command SKIP_SINCE_NEXT_TEST = new Command( MasterProcessCommand.SKIP_SINCE_NEXT_TEST
);
     public static final Command NOOP = new Command( MasterProcessCommand.NOOP );
+    public static final Command BYE_ACK = new Command( MasterProcessCommand.BYE_ACK );
 
     private final MasterProcessCommand command;
     private final String data;

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/da7ff6aa/surefire-api/src/main/java/org/apache/maven/surefire/booter/CommandReader.java
----------------------------------------------------------------------
diff --git a/surefire-api/src/main/java/org/apache/maven/surefire/booter/CommandReader.java
b/surefire-api/src/main/java/org/apache/maven/surefire/booter/CommandReader.java
index 57f1c2c..fb3ad05 100644
--- a/surefire-api/src/main/java/org/apache/maven/surefire/booter/CommandReader.java
+++ b/surefire-api/src/main/java/org/apache/maven/surefire/booter/CommandReader.java
@@ -42,6 +42,7 @@ import static java.lang.Thread.State.TERMINATED;
 import static java.lang.StrictMath.max;
 import static org.apache.maven.surefire.booter.Command.toShutdown;
 import static org.apache.maven.surefire.booter.ForkingRunListener.BOOTERCODE_NEXT_TEST;
+import static org.apache.maven.surefire.booter.MasterProcessCommand.BYE_ACK;
 import static org.apache.maven.surefire.booter.MasterProcessCommand.NOOP;
 import static org.apache.maven.surefire.booter.MasterProcessCommand.RUN_CLASS;
 import static org.apache.maven.surefire.booter.MasterProcessCommand.SHUTDOWN;
@@ -166,6 +167,11 @@ public final class CommandReader
         addListener( NOOP, listener );
     }
 
+    public void addByeAckListener( CommandListener listener )
+    {
+        addListener( BYE_ACK, listener );
+    }
+
     private void addListener( MasterProcessCommand cmd, CommandListener listener )
     {
         listeners.add( new BiProperty<MasterProcessCommand, CommandListener>( cmd,
listener ) );
@@ -470,17 +476,15 @@ public final class CommandReader
                 CommandReader.this.makeQueueFull();
                 CommandReader.this.wakeupIterator();
                 insertToListeners( toShutdown( shutdown ) );
-                switch ( shutdown )
+                if ( shutdown.isExit() )
                 {
-                    case EXIT:
-                        System.exit( 1 );
-                    case KILL:
-                        Runtime.getRuntime().halt( 1 );
-                    case DEFAULT:
-                    default:
-                        // should not happen; otherwise you missed enum case
-                        break;
+                    System.exit( 1 );
+                }
+                else if ( shutdown.isKill() )
+                {
+                    Runtime.getRuntime().halt( 1 );
                 }
+                // else is default: should not happen; otherwise you missed enum case
             }
         }
     }

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/da7ff6aa/surefire-api/src/main/java/org/apache/maven/surefire/booter/MasterProcessCommand.java
----------------------------------------------------------------------
diff --git a/surefire-api/src/main/java/org/apache/maven/surefire/booter/MasterProcessCommand.java
b/surefire-api/src/main/java/org/apache/maven/surefire/booter/MasterProcessCommand.java
index d5e314a..229c8af 100644
--- a/surefire-api/src/main/java/org/apache/maven/surefire/booter/MasterProcessCommand.java
+++ b/surefire-api/src/main/java/org/apache/maven/surefire/booter/MasterProcessCommand.java
@@ -46,7 +46,8 @@ public enum MasterProcessCommand
     SHUTDOWN( 3, String.class ),
 
     /** To tell a forked process that the master process is still alive. Repeated after 10
seconds. */
-    NOOP( 4, Void.class );
+    NOOP( 4, Void.class ),
+    BYE_ACK( 5, Void.class );
 
     private static final Charset ASCII = Charset.forName( "ASCII" );
 

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/da7ff6aa/surefire-api/src/main/java/org/apache/maven/surefire/booter/Shutdown.java
----------------------------------------------------------------------
diff --git a/surefire-api/src/main/java/org/apache/maven/surefire/booter/Shutdown.java b/surefire-api/src/main/java/org/apache/maven/surefire/booter/Shutdown.java
index 262cb29..77a09cf 100644
--- a/surefire-api/src/main/java/org/apache/maven/surefire/booter/Shutdown.java
+++ b/surefire-api/src/main/java/org/apache/maven/surefire/booter/Shutdown.java
@@ -44,6 +44,21 @@ public enum Shutdown
         return param;
     }
 
+    public boolean isKill()
+    {
+        return this == KILL;
+    }
+
+    public boolean isExit()
+    {
+        return this == EXIT;
+    }
+
+    public boolean isDefaultShutdown()
+    {
+        return this == DEFAULT;
+    }
+
     public static boolean isKnown( String param )
     {
         for ( Shutdown shutdown : values() )

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/da7ff6aa/surefire-api/src/main/java/org/apache/maven/surefire/util/internal/DumpFileUtils.java
----------------------------------------------------------------------
diff --git a/surefire-api/src/main/java/org/apache/maven/surefire/util/internal/DumpFileUtils.java
b/surefire-api/src/main/java/org/apache/maven/surefire/util/internal/DumpFileUtils.java
index 2c110f5..b561295 100644
--- a/surefire-api/src/main/java/org/apache/maven/surefire/util/internal/DumpFileUtils.java
+++ b/surefire-api/src/main/java/org/apache/maven/surefire/util/internal/DumpFileUtils.java
@@ -1 +1,125 @@
-package org.apache.maven.surefire.util.internal;

/*
 * 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.
 */

import org.apache.maven.surefire.report.ReporterConfiguration;

import java.io.File;
import
java.io.FileOutputStream;
import java.io.IOE
 xception;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.Writer;
import
java.text.SimpleDateFormat;
import java.util.Date;

/**
 * Dumps a text or exception in dump
file.
 * Each call logs a date when it was written to the dump file.
 *
 * @author <a href="mailto:tibordigana@apache.org">Tibor
Digana (tibor17)</a>
 * @since 2.19.2
 */
public final class DumpFileUtils
{
    private
DumpFileUtils()
    {
        throw new IllegalStateException( "no instantiable constructor"
);
    }

    /**
     * New dump file. Synchronized object appears in main memory and perfectly
visible in other threads.
     */
    public static synchronized File newDumpFile( String
dumpFileName, ReporterConfiguration configuration )
    {
        return new File( configuration.getReportsDirectory(),
dumpFileName );
    }

    public static void dumpException( Throwable t, File dumpFile )
   {
        dumpException( t, null, dumpFile );
    }

    public static void dumpException
 ( Throwable t, String msg, File dumpFile )
    {
        try
        {
            if ( t
!= null && dumpFile != null && ( dumpFile.exists() || dumpFile.createNewFile()
) )
            {
                Writer fw = createWriter( dumpFile );
                if
( msg != null )
                {
                    fw.append( msg )
                  
         .append( StringUtils.NL );
                }
                PrintWriter pw = new
PrintWriter( fw );
                t.printStackTrace( pw );
                pw.flush();
 
              fw.append( StringUtils.NL )
                        .append( StringUtils.NL
);
                fw.flush();
                fw.close();
            }
        }
      
 catch ( Exception e )
        {
            // do nothing
        }
    }

    public static
void dumpText( String msg, File dumpFile )
    {
        try
        {
            if ( msg
!= null && dumpFile != null
                         && ( dumpFile.exists()
|| mkdirs( dumpFile ) &&
  dumpFile.createNewFile() ) )
            {
                Writer fw = createWriter( dumpFile
)
                        .append( msg )
                        .append( StringUtils.NL )
                       .append( StringUtils.NL );
                fw.flush();
           
    fw.close();
            }
        }
        catch ( Exception e )
        {
         
  // do nothing
        }
    }

    public static String newFormattedDateFileName()
    {
       return new SimpleDateFormat( "yyyy-MM-dd'T'hh-mm-ss_SSS" ).format( new Date() );
 
  }

    private static Writer createWriter( File dumpFile ) throws IOException
    {
   
    return new OutputStreamWriter( new FileOutputStream( dumpFile, true ), "UTF-8" )
    
                  .append( "# Created on " )
                       .append( new SimpleDateFormat(
"yyyy-MM-dd'T'hh:mm:ss.SSS" ).format( new Date() ) )
                       .append( StringUtils.NL
);
    }

    private static boolean mkdirs( File dumpFile )
    {
  
       return dumpFile.getParentFile().mkdirs();
    }
}
\ No newline at end of file
+package org.apache.maven.surefire.util.internal;
+
+/*
+ * 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.
+ */
+
+import org.apache.maven.surefire.report.ReporterConfiguration;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStreamWriter;
+import java.io.PrintWriter;
+import java.io.Writer;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+
+/**
+ * Dumps a text or exception in dump file.
+ * Each call logs a date when it was written to the dump file.
+ *
+ * @author <a href="mailto:tibordigana@apache.org">Tibor Digana (tibor17)</a>
+ * @since 2.19.2
+ */
+public final class DumpFileUtils
+{
+    private DumpFileUtils()
+    {
+        throw new IllegalStateException( "no instantiable constructor" );
+    }
+
+    /**
+     * New dump file. Synchronized object appears in main memory and perfectly visible in
other threads.
+     */
+    public static synchronized File newDumpFile( String dumpFileName, ReporterConfiguration
configuration )
+    {
+        return new File( configuration.getReportsDirectory(), dumpFileName );
+    }
+
+    public static void dumpException( Throwable t, File dumpFile )
+    {
+        dumpException( t, null, dumpFile );
+    }
+
+    public static void dumpException( Throwable t, String msg, File dumpFile )
+    {
+        try
+        {
+            if ( t != null && dumpFile != null
+                         && ( dumpFile.exists() || mkdirs( dumpFile ) &&
dumpFile.createNewFile() ) )
+            {
+                Writer fw = createWriter( dumpFile );
+                if ( msg != null )
+                {
+                    fw.append( msg )
+                            .append( StringUtils.NL );
+                }
+                PrintWriter pw = new PrintWriter( fw );
+                t.printStackTrace( pw );
+                pw.flush();
+                fw.append( StringUtils.NL )
+                  .append( StringUtils.NL )
+                  .close();
+            }
+        }
+        catch ( Exception e )
+        {
+            // do nothing
+        }
+    }
+
+    public static void dumpText( String msg, File dumpFile )
+    {
+        try
+        {
+            if ( msg != null && dumpFile != null
+                         && ( dumpFile.exists() || mkdirs( dumpFile ) &&
dumpFile.createNewFile() ) )
+            {
+                createWriter( dumpFile )
+                        .append( msg )
+                        .append( StringUtils.NL )
+                        .append( StringUtils.NL )
+                        .close();
+            }
+        }
+        catch ( Exception e )
+        {
+            // do nothing
+        }
+    }
+
+    public static String newFormattedDateFileName()
+    {
+        return new SimpleDateFormat( "yyyy-MM-dd'T'HH-mm-ss_SSS" ).format( new Date() );
+    }
+
+    private static Writer createWriter( File dumpFile ) throws IOException
+    {
+        return new OutputStreamWriter( new FileOutputStream( dumpFile, true ), "UTF-8" )
+                       .append( "# Created on " )
+                       .append( new SimpleDateFormat( "yyyy-MM-dd'T'HH:mm:ss.SSS" ).format(
new Date() ) )
+                       .append( StringUtils.NL );
+    }
+
+    private static boolean mkdirs( File dumpFile )
+    {
+        File dir = dumpFile.getParentFile();
+        return dir.exists() || dir.mkdirs();
+    }
+}

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/da7ff6aa/surefire-api/src/test/java/org/apache/maven/surefire/booter/MasterProcessCommandTest.java
----------------------------------------------------------------------
diff --git a/surefire-api/src/test/java/org/apache/maven/surefire/booter/MasterProcessCommandTest.java
b/surefire-api/src/test/java/org/apache/maven/surefire/booter/MasterProcessCommandTest.java
index 8396d8b..6e66a18 100644
--- a/surefire-api/src/test/java/org/apache/maven/surefire/booter/MasterProcessCommandTest.java
+++ b/surefire-api/src/test/java/org/apache/maven/surefire/booter/MasterProcessCommandTest.java
@@ -117,6 +117,13 @@ public class MasterProcessCommandTest
                     decoded = command.toDataTypeAsString( encoded );
                     assertNull( decoded );
                     break;
+                case  BYE_ACK:
+                    assertEquals( Void.class, command.getDataType() );
+                    encoded = command.fromDataType( dummyData );
+                    assertThat( encoded.length, is( 0 ) );
+                    decoded = command.toDataTypeAsString( encoded );
+                    assertNull( decoded );
+                    break;
                 default:
                     fail();
             }

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/da7ff6aa/surefire-booter/src/main/java/org/apache/maven/surefire/booter/ForkedBooter.java
----------------------------------------------------------------------
diff --git a/surefire-booter/src/main/java/org/apache/maven/surefire/booter/ForkedBooter.java
b/surefire-booter/src/main/java/org/apache/maven/surefire/booter/ForkedBooter.java
index b76df2f..665e2f8 100644
--- a/surefire-booter/src/main/java/org/apache/maven/surefire/booter/ForkedBooter.java
+++ b/surefire-booter/src/main/java/org/apache/maven/surefire/booter/ForkedBooter.java
@@ -36,21 +36,22 @@ import java.lang.reflect.InvocationTargetException;
 import java.util.concurrent.ScheduledExecutorService;
 import java.util.concurrent.ScheduledFuture;
 import java.util.concurrent.ScheduledThreadPoolExecutor;
+import java.util.concurrent.Semaphore;
 import java.util.concurrent.ThreadFactory;
 import java.util.concurrent.atomic.AtomicBoolean;
 
+import static java.lang.Math.max;
 import static java.lang.System.err;
 import static java.lang.System.out;
 import static java.lang.System.setErr;
 import static java.lang.System.setOut;
 import static java.lang.Thread.currentThread;
+import static java.util.concurrent.TimeUnit.MILLISECONDS;
 import static java.util.concurrent.TimeUnit.SECONDS;
 import static org.apache.maven.surefire.booter.CommandReader.getReader;
 import static org.apache.maven.surefire.booter.ForkingRunListener.BOOTERCODE_BYE;
 import static org.apache.maven.surefire.booter.ForkingRunListener.BOOTERCODE_ERROR;
 import static org.apache.maven.surefire.booter.ForkingRunListener.encode;
-import static org.apache.maven.surefire.booter.Shutdown.EXIT;
-import static org.apache.maven.surefire.booter.Shutdown.KILL;
 import static org.apache.maven.surefire.booter.SystemPropertyManager.setSystemProperties;
 import static org.apache.maven.surefire.util.ReflectionUtils.instantiateOneArg;
 import static org.apache.maven.surefire.util.internal.DaemonThreadFactory.newDaemonThreadFactory;
@@ -70,8 +71,10 @@ public final class ForkedBooter
 {
     private static final long DEFAULT_SYSTEM_EXIT_TIMEOUT_IN_SECONDS = 30;
     private static final long PING_TIMEOUT_IN_SECONDS = 20;
-    private static final ScheduledExecutorService JVM_TERMINATOR = createJvmTerminator();
+    private static final long ONE_SECOND_IN_MILLIS = 1000;
+    private static final ScheduledExecutorService JVM_PING = createPingScheduler();
 
+    private static volatile ScheduledThreadPoolExecutor jvmTerminator;
     private static volatile long systemExitTimeoutInSeconds = DEFAULT_SYSTEM_EXIT_TIMEOUT_IN_SECONDS;
 
     /**
@@ -157,7 +160,7 @@ public final class ForkedBooter
             encodeAndWriteToOutput( ( (char) BOOTERCODE_BYE ) + ",0,BYE!\n", originalOut
);
             originalOut.flush();
             // noinspection CallToSystemExit
-            exit( 0, EXIT, reader, false );
+            exit( 0, reader );
         }
         catch ( Throwable t )
         {
@@ -166,7 +169,7 @@ public final class ForkedBooter
             // noinspection UseOfSystemOutOrSystemErr
             t.printStackTrace( err );
             // noinspection ProhibitedExceptionThrown,CallToSystemExit
-            exit( 1, EXIT, reader, false );
+            exit( 1 );
         }
         finally
         {
@@ -181,11 +184,11 @@ public final class ForkedBooter
 
     private static ScheduledFuture<?> listenToShutdownCommands( CommandReader reader
)
     {
-        reader.addShutdownListener( createExitHandler( reader ) );
+        reader.addShutdownListener( createExitHandler() );
         AtomicBoolean pingDone = new AtomicBoolean( true );
         reader.addNoopListener( createPingHandler( pingDone ) );
-        return JVM_TERMINATOR.scheduleAtFixedRate( createPingJob( pingDone, reader ),
-                                                   0, PING_TIMEOUT_IN_SECONDS, SECONDS );
+        Runnable pingJob = createPingJob( pingDone );
+        return JVM_PING.scheduleAtFixedRate( pingJob, 0, PING_TIMEOUT_IN_SECONDS, SECONDS
);
     }
 
     private static CommandListener createPingHandler( final AtomicBoolean pingDone )
@@ -199,18 +202,27 @@ public final class ForkedBooter
         };
     }
 
-    private static CommandListener createExitHandler( final CommandReader reader )
+    private static CommandListener createExitHandler()
     {
         return new CommandListener()
         {
             public void update( Command command )
             {
-                exit( 1, command.toShutdownData(), reader, true );
+                Shutdown shutdown = command.toShutdownData();
+                if ( shutdown.isKill() )
+                {
+                    kill();
+                }
+                else if ( shutdown.isExit() )
+                {
+                    exit( 1 );
+                }
+                // else refers to shutdown=testset, but not used now, keeping reader open
             }
         };
     }
 
-    private static Runnable createPingJob( final AtomicBoolean pingDone, final CommandReader
reader  )
+    private static Runnable createPingJob( final AtomicBoolean pingDone  )
     {
         return new Runnable()
         {
@@ -219,7 +231,7 @@ public final class ForkedBooter
                 boolean hasPing = pingDone.getAndSet( false );
                 if ( !hasPing )
                 {
-                    exit( 1, KILL, reader, true );
+                    kill();
                 }
             }
         };
@@ -231,23 +243,44 @@ public final class ForkedBooter
         out.write( encodeBytes, 0, encodeBytes.length );
     }
 
-    private static void exit( int returnCode, Shutdown shutdownType, CommandReader reader,
boolean stopReaderOnExit )
+    private static void kill()
     {
-        switch ( shutdownType )
+        Runtime.getRuntime().halt( 1 );
+    }
+
+    private static void exit( int returnCode )
+    {
+        launchLastDitchDaemonShutdownThread( returnCode );
+        System.exit( returnCode );
+    }
+
+    private static void exit( int returnCode, final CommandReader reader )
+    {
+        final Semaphore barrier = new Semaphore( 0 );
+        reader.addByeAckListener( new CommandListener()
+                                  {
+                                      @Override
+                                      public void update( Command command )
+                                      {
+                                          barrier.release();
+                                      }
+                                  }
+        );
+        launchLastDitchDaemonShutdownThread( returnCode );
+        final long timeoutMillis = max( systemExitTimeoutInSeconds * ONE_SECOND_IN_MILLIS,
ONE_SECOND_IN_MILLIS );
+        acquireOnePermit( barrier, timeoutMillis );
+        System.exit( returnCode );
+    }
+
+    private static boolean acquireOnePermit( Semaphore barrier, long timeoutMillis )
+    {
+        try
         {
-            case KILL:
-                Runtime.getRuntime().halt( returnCode );
-            case EXIT:
-                if ( stopReaderOnExit )
-                {
-                    reader.stop();
-                }
-                launchLastDitchDaemonShutdownThread( returnCode );
-                System.exit( returnCode );
-            case DEFAULT:
-                // refers to shutdown=testset, but not used now, keeping reader open
-            default:
-                break;
+            return barrier.tryAcquire( timeoutMillis, MILLISECONDS );
+        }
+        catch ( InterruptedException e )
+        {
+            return true;
         }
     }
 
@@ -269,11 +302,25 @@ public final class ForkedBooter
         return new ForkingReporterFactory( trimStackTrace, originalSystemOut );
     }
 
-    private static ScheduledExecutorService createJvmTerminator()
+    private static synchronized ScheduledThreadPoolExecutor getJvmTerminator()
+    {
+        if ( jvmTerminator == null )
+        {
+            ThreadFactory threadFactory =
+                    newDaemonThreadFactory( "last-ditch-daemon-shutdown-thread-" + systemExitTimeoutInSeconds
+ "s" );
+            jvmTerminator = new ScheduledThreadPoolExecutor( 1, threadFactory );
+            jvmTerminator.setMaximumPoolSize( 1 );
+            return jvmTerminator;
+        }
+        else
+        {
+            return jvmTerminator;
+        }
+    }
+
+    private static ScheduledExecutorService createPingScheduler()
     {
-        ThreadFactory threadFactory = newDaemonThreadFactory( "last-ditch-daemon-shutdown-thread-"
-                                                            + systemExitTimeoutInSeconds
-                                                            + "s" );
+        ThreadFactory threadFactory = newDaemonThreadFactory( "ping-" + PING_TIMEOUT_IN_SECONDS
+ "s" );
         ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor( 1, threadFactory
);
         executor.setMaximumPoolSize( 1 );
         executor.prestartCoreThread();
@@ -283,13 +330,14 @@ public final class ForkedBooter
     @SuppressWarnings( "checkstyle:emptyblock" )
     private static void launchLastDitchDaemonShutdownThread( final int returnCode )
     {
-        JVM_TERMINATOR.schedule( new Runnable()
-        {
-            public void run()
-            {
-                Runtime.getRuntime().halt( returnCode );
-            }
-        }, systemExitTimeoutInSeconds, SECONDS );
+        getJvmTerminator().schedule( new Runnable()
+                                        {
+                                            public void run()
+                                            {
+                                                Runtime.getRuntime().halt( returnCode );
+                                            }
+                                        }, systemExitTimeoutInSeconds, SECONDS
+        );
     }
 
     private static RunResult invokeProviderInSameClassLoader( Object testSet, Object factory,

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/da7ff6aa/surefire-integration-tests/src/test/java/org/apache/maven/surefire/its/jiras/Surefire141PluggableProvidersIT.java
----------------------------------------------------------------------
diff --git a/surefire-integration-tests/src/test/java/org/apache/maven/surefire/its/jiras/Surefire141PluggableProvidersIT.java
b/surefire-integration-tests/src/test/java/org/apache/maven/surefire/its/jiras/Surefire141PluggableProvidersIT.java
index 75aa743..b185217 100644
--- a/surefire-integration-tests/src/test/java/org/apache/maven/surefire/its/jiras/Surefire141PluggableProvidersIT.java
+++ b/surefire-integration-tests/src/test/java/org/apache/maven/surefire/its/jiras/Surefire141PluggableProvidersIT.java
@@ -52,6 +52,7 @@ public class Surefire141PluggableProvidersIT
         throws Exception
     {
         unpack( "surefire-141-pluggableproviders" )
+            .setForkJvm()
             .maven()
             .showExceptionMessages()
             .debugLogging()
@@ -68,13 +69,20 @@ public class Surefire141PluggableProvidersIT
         final String errorText = "Let's fail with a runtimeException";
 
         OutputValidator validator = unpack( "surefire-141-pluggableproviders" )
+            .setForkJvm()
             .sysProp( "invokeCrash", "runtimeException" )
             .maven()
             .withFailure()
-            .executeTest()
-            .verifyTextInLog( errorText );
+            .executeTest();
 
         assertErrorMessage( validator, errorText );
+
+        boolean hasErrorInLog = verifiedErrorInLog( validator, "There was an error in the
forked process" );
+        boolean verifiedInLog = verifiedErrorInLog( validator, errorText );
+        assertThat( hasErrorInLog && verifiedInLog )
+                .describedAs( "'" + errorText + "' could not be verified in log.txt nor *.dump
file. ("
+                                      + hasErrorInLog + ", " + verifiedInLog + ")" )
+                .isTrue();
     }
 
     @Test
@@ -84,13 +92,20 @@ public class Surefire141PluggableProvidersIT
         final String errorText = "Let's fail with a reporterexception";
 
         OutputValidator validator = unpack( "surefire-141-pluggableproviders" )
+            .setForkJvm()
             .sysProp( "invokeCrash", "reporterException" )
             .maven()
             .withFailure()
-            .executeTest()
-            .verifyTextInLog( errorText );
+            .executeTest();
 
         assertErrorMessage( validator, errorText );
+
+        boolean hasErrorInLog = verifiedErrorInLog( validator, "There was an error in the
forked process" );
+        boolean verifiedInLog = verifiedErrorInLog( validator, errorText );
+        assertThat( hasErrorInLog && verifiedInLog )
+                .describedAs( "'" + errorText + "' could not be verified in log.txt nor *.dump
file. ("
+                                      + hasErrorInLog + ", " + verifiedInLog + ")" )
+                .isTrue();
     }
 
     @Test
@@ -100,15 +115,19 @@ public class Surefire141PluggableProvidersIT
         final String errorText = "Let's fail with a runtimeException";
 
         OutputValidator validator = unpack( "surefire-141-pluggableproviders" )
+                                            .setForkJvm()
                                             .sysProp( "constructorCrash", "runtimeException"
)
                                             .maven()
                                             .withFailure()
                                             .executeTest();
 
+        assertErrorMessage( validator, errorText );
+
+        boolean hasErrorInLog = verifiedErrorInLog( validator, "There was an error in the
forked process" );
         boolean verifiedInLog = verifiedErrorInLog( validator, errorText );
-        boolean verifiedInDump = verifiedErrorInDump( validator, errorText );
-        assertThat( verifiedInLog || verifiedInDump )
-                .describedAs( "'" + errorText + "' could not be verified in log.txt nor *.dump
file." )
+        assertThat( hasErrorInLog && verifiedInLog )
+                .describedAs( "'" + errorText + "' could not be verified in log.txt nor *.dump
file. ("
+                                      + hasErrorInLog + ", " + verifiedInLog + ")" )
                 .isTrue();
     }
 


Mime
View raw message