Stabilized admin-tests

- stop-removefiles-start principle replaced by asadmin commands (junit extension)
- discovery: detached jobs don't survive restarting domain, synchronous do.

Signed-off-by: David Matějček <dmatej@seznam.cz>
diff --git a/appserver/tests/admin/tests/src/test/java/org/glassfish/main/admin/test/ClusterITest.java b/appserver/tests/admin/tests/src/test/java/org/glassfish/main/admin/test/ClusterITest.java
index 9794619..aaaa56c 100644
--- a/appserver/tests/admin/tests/src/test/java/org/glassfish/main/admin/test/ClusterITest.java
+++ b/appserver/tests/admin/tests/src/test/java/org/glassfish/main/admin/test/ClusterITest.java
@@ -90,8 +90,8 @@
     @Test
     @Order(3)
     public void startInstancesTest() {
-        assertThat(ASADMIN.exec(30_000, false, "start-local-instance", INSTANCE_NAME_1), asadminOK());
-        assertThat(ASADMIN.exec(30_000, false, "start-local-instance", INSTANCE_NAME_2), asadminOK());
+        assertThat(ASADMIN.exec(30_000, "start-local-instance", INSTANCE_NAME_1), asadminOK());
+        assertThat(ASADMIN.exec(30_000, "start-local-instance", INSTANCE_NAME_2), asadminOK());
     }
 
     @Test
diff --git a/appserver/tests/admin/tests/src/test/java/org/glassfish/main/admin/test/progress/DetachAttachITest.java b/appserver/tests/admin/tests/src/test/java/org/glassfish/main/admin/test/progress/DetachAttachITest.java
index 0c7d3e5..58de9a9 100644
--- a/appserver/tests/admin/tests/src/test/java/org/glassfish/main/admin/test/progress/DetachAttachITest.java
+++ b/appserver/tests/admin/tests/src/test/java/org/glassfish/main/admin/test/progress/DetachAttachITest.java
@@ -19,28 +19,26 @@
 
 import java.util.ArrayList;
 import java.util.Collection;
-import java.util.Collections;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Set;
-import java.util.StringTokenizer;
 import java.util.concurrent.Callable;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
 import java.util.concurrent.Future;
-import java.util.concurrent.ThreadFactory;
 import java.util.logging.Level;
 import java.util.logging.Logger;
 
 import org.glassfish.main.admin.test.tool.asadmin.Asadmin;
 import org.glassfish.main.admin.test.tool.asadmin.AsadminResult;
+import org.glassfish.main.admin.test.tool.asadmin.DetachedTerseAsadminResult;
 import org.glassfish.main.admin.test.tool.asadmin.GlassFishTestEnvironment;
 import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
 
 import static org.glassfish.main.admin.test.tool.AsadminResultMatcher.asadminOK;
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.hamcrest.Matchers.greaterThan;
-import static org.hamcrest.Matchers.hasSize;
 import static org.hamcrest.Matchers.not;
 import static org.hamcrest.Matchers.stringContainsInOrder;
 import static org.junit.jupiter.api.Assertions.assertEquals;
@@ -51,10 +49,12 @@
 /**
  * @author martinmares
  */
+@ExtendWith(JobTestExtension.class)
 public class DetachAttachITest {
     private static final Logger LOG = Logger.getLogger(DetachAttachITest.class.getName());
     private static final Asadmin ASADMIN = GlassFishTestEnvironment.getAsadmin();
 
+
     @Test
     public void uptimePeriodically() throws Exception {
         Set<String> ids = new HashSet<>();
@@ -62,9 +62,9 @@
             LOG.log(Level.FINE, "detachAndAttachUptimePeriodically(): round " + i);
             final String id;
             {
-                AsadminResult result = ASADMIN.execDetached("--terse", "uptime");
+                DetachedTerseAsadminResult result = ASADMIN.execDetached("uptime");
                 assertThat(result, asadminOK());
-                id = parseJobIdFromEchoTerse(result.getStdOut());
+                id = result.getJobId();
                 assertTrue(ids.add(id));
             }
             Thread.sleep(1000L);
@@ -79,47 +79,36 @@
 
     @Test
     public void commandWithProgressStatus() throws Exception {
-        AsadminResult result = ASADMIN.execDetached("--terse", "progress-custom", "6x1");
-        assertThat(result, asadminOK());
-        String id = parseJobIdFromEchoTerse(result.getStdOut());
+        final DetachedTerseAsadminResult detached = ASADMIN.execDetached("progress-custom", "6x1");
+        assertThat(detached, asadminOK());
         Thread.sleep(2000L);
-        // Now attach running
-        result = ASADMIN.exec("attach", id);
-        assertThat(result, asadminOK());
-        assertThat(result.getStdOut(), stringContainsInOrder("progress-custom"));
-        List<ProgressMessage> prgs = ProgressMessage.grepProgressMessages(result.getStdOut());
+        final AsadminResult attachResult = ASADMIN.exec("attach", detached.getJobId());
+        assertThat(attachResult, asadminOK());
+        assertThat(attachResult.getStdOut(), stringContainsInOrder("progress-custom"));
+        List<ProgressMessage> prgs = ProgressMessage.grepProgressMessages(attachResult.getStdOut());
         assertFalse(prgs.isEmpty());
         assertThat(prgs.get(0).getValue(), greaterThan(0));
         assertEquals(100, prgs.get(prgs.size() - 1).getValue());
         // Now attach finished - must NOT exist - seen progress job is removed
-        assertThat(ASADMIN.exec("attach", id), not(asadminOK()));
+        assertThat(ASADMIN.exec("attach", detached.getJobId()), not(asadminOK()));
     }
 
 
     @Test
     public void detachOnesAttachMulti() throws Exception {
-        ExecutorService pool = Executors.newCachedThreadPool(new ThreadFactory() {
-            @Override
-            public Thread newThread(Runnable r) {
-                Thread result = new Thread(r);
-                result.setDaemon(true);
-                return result;
-            }
+        ExecutorService pool = Executors.newCachedThreadPool(r -> {
+            Thread result = new Thread(r);
+            result.setDaemon(true);
+            return result;
         });
-        AsadminResult result = ASADMIN.execDetached("--terse", "progress-custom", "8x1");
+        final DetachedTerseAsadminResult result = ASADMIN.execDetached("progress-custom", "8x1");
         assertThat(result, asadminOK());
-        final String id = parseJobIdFromEchoTerse(result.getStdOut());
-        assertNotNull(id, "id");
+        assertNotNull(result.getJobId(), "id");
         Thread.sleep(1500L);
         final int attachCount = 3;
         Collection<Callable<AsadminResult>> attaches = new ArrayList<>(attachCount);
         for (int i = 0; i < attachCount; i++) {
-            attaches.add(new Callable<AsadminResult>() {
-                @Override
-                public AsadminResult call() throws Exception {
-                    return ASADMIN.exec("attach", id);
-                }
-            });
+            attaches.add(() -> ASADMIN.exec("attach", result.getJobId()));
         }
         List<Future<AsadminResult>> results = pool.invokeAll(attaches);
         for (Future<AsadminResult> fRes : results) {
@@ -132,10 +121,4 @@
             assertEquals(100, prgs.get(prgs.size() - 1).getValue());
         }
     }
-
-    private String parseJobIdFromEchoTerse(String str) {
-        List<Object> stok = Collections.list(new StringTokenizer(str));
-        assertThat(stok, hasSize(1));
-        return (String) stok.get(0);
-    }
 }
diff --git a/appserver/tests/admin/tests/src/test/java/org/glassfish/main/admin/test/progress/JobManagerITest.java b/appserver/tests/admin/tests/src/test/java/org/glassfish/main/admin/test/progress/JobManagerITest.java
index 40ba44a..8e6bfc4 100644
--- a/appserver/tests/admin/tests/src/test/java/org/glassfish/main/admin/test/progress/JobManagerITest.java
+++ b/appserver/tests/admin/tests/src/test/java/org/glassfish/main/admin/test/progress/JobManagerITest.java
@@ -19,19 +19,17 @@
 
 import org.glassfish.main.admin.test.tool.asadmin.Asadmin;
 import org.glassfish.main.admin.test.tool.asadmin.AsadminResult;
-import org.junit.jupiter.api.BeforeEach;
+import org.glassfish.main.admin.test.tool.asadmin.DetachedTerseAsadminResult;
 import org.junit.jupiter.api.MethodOrderer.OrderAnnotation;
-
-import static org.glassfish.main.admin.test.tool.AsadminResultMatcher.asadminOK;
-import static org.glassfish.main.admin.test.tool.asadmin.GlassFishTestEnvironment.deleteJobsFile;
-import static org.glassfish.main.admin.test.tool.asadmin.GlassFishTestEnvironment.deleteOsgiDirectory;
-import static org.glassfish.main.admin.test.tool.asadmin.GlassFishTestEnvironment.getAsadmin;
-
 import org.junit.jupiter.api.Order;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.TestMethodOrder;
+import org.junit.jupiter.api.extension.ExtendWith;
 
+import static org.glassfish.main.admin.test.tool.AsadminResultMatcher.asadminOK;
+import static org.glassfish.main.admin.test.tool.asadmin.GlassFishTestEnvironment.getAsadmin;
 import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.matchesPattern;
 import static org.hamcrest.Matchers.stringContainsInOrder;
 
 /**
@@ -40,19 +38,13 @@
  * @author Bhakti Mehta
  * @author David Matejcek
  */
+@ExtendWith(JobTestExtension.class)
 @TestMethodOrder(OrderAnnotation.class)
 public class JobManagerITest {
 
     private static final String COMMAND_PROGRESS_SIMPLE = "progress-simple";
     private static final Asadmin ASADMIN = getAsadmin();
 
-    @BeforeEach
-    public void setUp() throws Exception {
-        assertThat(ASADMIN.exec("stop-domain"), asadminOK());
-        deleteJobsFile();
-        deleteOsgiDirectory();
-        assertThat(ASADMIN.exec("start-domain"), asadminOK());
-    }
 
     @Test
     @Order(1)
@@ -66,46 +58,37 @@
     @Test
     @Order(2)
     public void jobSurvivesRestart() throws Exception {
-        assertThat(ASADMIN.exec("--terse", "progress-simple"), asadminOK());
-        assertThat(ASADMIN.exec("list-jobs").getStdOut(), stringContainsInOrder(COMMAND_PROGRESS_SIMPLE, "COMPLETED"));
-        assertThat(ASADMIN.exec("list-jobs", "1").getStdOut(), stringContainsInOrder(COMMAND_PROGRESS_SIMPLE, "COMPLETED"));
-
+        assertThat(ASADMIN.exec(COMMAND_PROGRESS_SIMPLE), asadminOK());
         assertThat(ASADMIN.exec("stop-domain"), asadminOK());
         assertThat(ASADMIN.exec("start-domain"), asadminOK());
-        AsadminResult result = ASADMIN.exec("list-jobs", "1");
-        assertThat(result, asadminOK());
-        assertThat(result.getStdOut(), stringContainsInOrder(COMMAND_PROGRESS_SIMPLE, "COMPLETED"));
+        assertThat(ASADMIN.exec("list-jobs").getStdOut(), stringContainsInOrder(COMMAND_PROGRESS_SIMPLE, "COMPLETED"));
     }
 
 
     @Test
     @Order(3)
     public void detachAndAttach() throws Exception {
-        assertThat(ASADMIN.execDetached(COMMAND_PROGRESS_SIMPLE).getStdOut(), stringContainsInOrder("Job ID: "));
-        assertThat(ASADMIN.exec("list-jobs", "1").getStdOut(), stringContainsInOrder(COMMAND_PROGRESS_SIMPLE));
-        assertThat(ASADMIN.exec("attach", "1"), asadminOK());
+        DetachedTerseAsadminResult detached = ASADMIN.execDetached(COMMAND_PROGRESS_SIMPLE);
+        assertThat(detached.getJobId(), matchesPattern("[1-9][0-9]*"));
+        assertThat(ASADMIN.exec("list-jobs", detached.getJobId()).getStdOut(), stringContainsInOrder(COMMAND_PROGRESS_SIMPLE));
+        assertThat(ASADMIN.exec("attach", detached.getJobId()), asadminOK());
 
         // list-jobs and it should be purged since the user
         // starting is the same as the user who attached to it
-        assertThat(ASADMIN.exec("list-jobs").getOutput(), stringContainsInOrder("Nothing to list"));
+        assertThat(ASADMIN.exec("list-jobs", detached.getJobId()).getOutput(), stringContainsInOrder("Nothing to list"));
     }
 
 
     @Test
     @Order(4)
-    public void runConfigureManagedJobsTest() throws Exception {
-        assertThat(ASADMIN.exec("configure-managed-jobs", "--job-retention-period=60s", "--cleanup-initial-delay=1s", "--cleanup-poll-interval=1s"), asadminOK());
+    public void runSynchronously() throws Exception {
         assertThat(ASADMIN.exec(COMMAND_PROGRESS_SIMPLE), asadminOK());
-        assertThat(ASADMIN.exec("list-jobs", "1").getStdOut(), stringContainsInOrder(COMMAND_PROGRESS_SIMPLE));
+        assertThat(ASADMIN.exec("list-jobs").getStdOut(), stringContainsInOrder(COMMAND_PROGRESS_SIMPLE));
 
-        // FIXME: Random race condition on Linux caused by some bug in restart-domain; 4848 port is then blocked for start-domain in setUp();
-        assertThat(ASADMIN.exec("stop-domain"), asadminOK());
-        assertThat(ASADMIN.exec("start-domain"), asadminOK());
-        assertThat(ASADMIN.exec("list-jobs", "1").getStdOut(), stringContainsInOrder(COMMAND_PROGRESS_SIMPLE));
-
-        assertThat(ASADMIN.exec("configure-managed-jobs", "--job-retention-period=1s", "--cleanup-initial-delay=1s", "--cleanup-poll-interval=1s"), asadminOK());
-        Thread.sleep(2100L);
-        assertThat(ASADMIN.exec("list-jobs").getOutput(), stringContainsInOrder("Nothing to list"));
-        assertThat(ASADMIN.exec("configure-managed-jobs", "--job-retention-period=1h", "--cleanup-initial-delay=5m", "--cleanup-poll-interval=20m"), asadminOK());
+        assertThat(ASADMIN.exec("configure-managed-jobs", "--job-retention-period=1s", "--cleanup-initial-delay=1s",
+            "--cleanup-poll-interval=1s"), asadminOK());
+        Thread.sleep(1100L);
+        assertThat("Completed jobs should be removed", ASADMIN.exec("list-jobs").getOutput(),
+            stringContainsInOrder("Nothing to list"));
     }
 }
diff --git a/appserver/tests/admin/tests/src/test/java/org/glassfish/main/admin/test/progress/JobTestExtension.java b/appserver/tests/admin/tests/src/test/java/org/glassfish/main/admin/test/progress/JobTestExtension.java
new file mode 100644
index 0000000..b052df2
--- /dev/null
+++ b/appserver/tests/admin/tests/src/test/java/org/glassfish/main/admin/test/progress/JobTestExtension.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (c) 2022 Eclipse Foundation and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package org.glassfish.main.admin.test.progress;
+
+import org.glassfish.main.admin.test.tool.asadmin.Asadmin;
+import org.glassfish.main.admin.test.tool.asadmin.AsadminResult;
+import org.glassfish.main.admin.test.tool.asadmin.GlassFishTestEnvironment;
+import org.junit.jupiter.api.extension.AfterAllCallback;
+import org.junit.jupiter.api.extension.BeforeAllCallback;
+import org.junit.jupiter.api.extension.ExtensionContext;
+import org.junit.jupiter.api.extension.ExtensionContext.Namespace;
+import org.junit.jupiter.api.extension.ExtensionContext.Store;
+
+import static java.util.function.Function.identity;
+import static org.glassfish.main.admin.test.tool.AsadminResultMatcher.asadminOK;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.stringContainsInOrder;
+import static org.junit.jupiter.api.Assertions.fail;
+
+
+/**
+ * @author David Matejcek
+ */
+public class JobTestExtension implements BeforeAllCallback, AfterAllCallback {
+
+    private static final String ORIG_POLL_INTERVAL = "origPollInterval";
+    private static final String ORIG_INITIAL_DELAY = "origInitialDelay";
+    private static final String ORIG_RETENTION_PERIOD = "origRetentionPeriod";
+
+    private static final Asadmin ASADMIN = GlassFishTestEnvironment.getAsadmin();
+
+    @Override
+    public void beforeAll(ExtensionContext context) throws Exception {
+        Namespace namespaceClass = Namespace.create(context.getRequiredTestClass());
+        Store store = context.getStore(namespaceClass);
+        store.put(ORIG_RETENTION_PERIOD, ASADMIN.getValue("managed-job-config.job-retention-period", identity()).getValue());
+        store.put(ORIG_INITIAL_DELAY, ASADMIN.getValue("managed-job-config.initial-delay", identity()).getValue());
+        store.put(ORIG_POLL_INTERVAL, ASADMIN.getValue("managed-job-config.poll-interval", identity()).getValue());
+    }
+
+    @Override
+    public void afterAll(ExtensionContext context) throws Exception {
+        waitForCompletition();
+
+        // minimal possible values.
+        assertThat(ASADMIN.exec("configure-managed-jobs",
+            "--job-retention-period=1s",
+            "--cleanup-initial-delay=1s",
+            "--cleanup-poll-interval=1s"), asadminOK());
+
+        Thread.sleep(1100L);
+        // cleanup should be finished.
+        AsadminResult result = ASADMIN.exec("list-jobs");
+        assertThat(result.getOutput(), stringContainsInOrder("Nothing to list"));
+
+        Namespace namespaceClass = Namespace.create(context.getRequiredTestClass());
+        Store store = context.getStore(namespaceClass);
+        // reset original configuration
+        assertThat(ASADMIN.exec("configure-managed-jobs",
+            "--job-retention-period=" + store.get(ORIG_RETENTION_PERIOD),
+            "--cleanup-initial-delay=" + store.get(ORIG_INITIAL_DELAY),
+            "--cleanup-poll-interval=" + store.get(ORIG_POLL_INTERVAL)), asadminOK());
+    }
+
+
+    private void waitForCompletition() throws Exception {
+        final long start = System.currentTimeMillis();
+        while (true) {
+            AsadminResult result = ASADMIN.exec("list-jobs");
+            assertThat(result, asadminOK());
+            if (!result.getStdOut().contains("RUNNING")) {
+                return;
+            }
+            if (System.currentTimeMillis() > start + 10000L) {
+                fail("Timed out waiting for completition of all jobs.");
+            }
+            Thread.sleep(500);
+        }
+    }
+
+
+}
diff --git a/appserver/tests/admin/tests/src/test/java/org/glassfish/main/admin/test/progress/ProgressStatusBasicITest.java b/appserver/tests/admin/tests/src/test/java/org/glassfish/main/admin/test/progress/ProgressStatusBasicITest.java
index 6ba8b05..0902128 100644
--- a/appserver/tests/admin/tests/src/test/java/org/glassfish/main/admin/test/progress/ProgressStatusBasicITest.java
+++ b/appserver/tests/admin/tests/src/test/java/org/glassfish/main/admin/test/progress/ProgressStatusBasicITest.java
@@ -23,6 +23,7 @@
 import org.glassfish.main.admin.test.tool.asadmin.AsadminResult;
 import org.glassfish.main.admin.test.tool.asadmin.GlassFishTestEnvironment;
 import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
 
 import static org.glassfish.main.admin.test.progress.ProgressMessage.isIncreasing;
 import static org.glassfish.main.admin.test.tool.AsadminResultMatcher.asadminOK;
@@ -35,10 +36,12 @@
 /**
  * @author martinmares
  */
+@ExtendWith(JobTestExtension.class)
 public class ProgressStatusBasicITest {
 
     private static final Asadmin ASADMIN = GlassFishTestEnvironment.getAsadmin();
 
+
     @Test
     public void simple() {
         AsadminResult result = ASADMIN.exec("progress-simple");
diff --git a/appserver/tests/admin/tests/src/test/java/org/glassfish/main/admin/test/progress/ProgressStatusComplexITest.java b/appserver/tests/admin/tests/src/test/java/org/glassfish/main/admin/test/progress/ProgressStatusComplexITest.java
index 3b7ae11..ed060e7 100644
--- a/appserver/tests/admin/tests/src/test/java/org/glassfish/main/admin/test/progress/ProgressStatusComplexITest.java
+++ b/appserver/tests/admin/tests/src/test/java/org/glassfish/main/admin/test/progress/ProgressStatusComplexITest.java
@@ -23,6 +23,7 @@
 import org.glassfish.main.admin.test.tool.asadmin.AsadminResult;
 import org.glassfish.main.admin.test.tool.asadmin.GlassFishTestEnvironment;
 import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
 
 import static org.glassfish.main.admin.test.progress.ProgressMessage.grepProgressMessages;
 import static org.glassfish.main.admin.test.progress.ProgressMessage.isIncreasing;
@@ -41,13 +42,15 @@
 /**
  * @author martinmares
  */
+@ExtendWith(JobTestExtension.class)
 public class ProgressStatusComplexITest {
 
     private static final Asadmin ASADMIN = GlassFishTestEnvironment.getAsadmin();
 
+
     @Test
     public void executeCommandFromCommand() {
-        AsadminResult result = ASADMIN.exec(30_000, false, "progress-exec-other");
+        AsadminResult result = ASADMIN.exec(30_000, "progress-exec-other");
         assertThat(result, asadminOK());
         assertArrayEquals(new String[] {
             "Starting", "Preparing", "Parsing", "Working on main part",
diff --git a/appserver/tests/admin/tests/src/test/java/org/glassfish/main/admin/test/progress/ProgressStatusFailITest.java b/appserver/tests/admin/tests/src/test/java/org/glassfish/main/admin/test/progress/ProgressStatusFailITest.java
index 0ed4266..b6ad40c 100644
--- a/appserver/tests/admin/tests/src/test/java/org/glassfish/main/admin/test/progress/ProgressStatusFailITest.java
+++ b/appserver/tests/admin/tests/src/test/java/org/glassfish/main/admin/test/progress/ProgressStatusFailITest.java
@@ -23,6 +23,7 @@
 import org.glassfish.main.admin.test.tool.asadmin.AsadminResult;
 import org.glassfish.main.admin.test.tool.asadmin.GlassFishTestEnvironment;
 import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
 
 import static org.glassfish.main.admin.test.tool.AsadminResultMatcher.asadminOK;
 import static org.hamcrest.MatcherAssert.assertThat;
@@ -33,6 +34,7 @@
 /**
  * @author martinmares
  */
+@ExtendWith(JobTestExtension.class)
 public class ProgressStatusFailITest {
 
     private static final Asadmin ASADMIN = GlassFishTestEnvironment.getAsadmin();
@@ -41,14 +43,14 @@
     public void failDuringExecution() {
         AsadminResult result = ASADMIN.exec("progress-fail-in-half");
         assertThat(result, not(asadminOK()));
-        List<ProgressMessage> prgs = ProgressMessage.grepProgressMessages(result.getStdOut());
-        assertFalse(prgs.isEmpty());
-        assertEquals(50, prgs.get(prgs.size() - 1).getValue());
+        List<ProgressMessage> messages = ProgressMessage.grepProgressMessages(result.getStdOut());
+        assertFalse(messages.isEmpty());
+        assertEquals(50, messages.get(messages.size() - 1).getValue());
     }
 
     @Test
     public void timeout() {
-        AsadminResult result = ASADMIN.exec(6_000, false, "progress-custom", "3x1", "1x8", "2x1");
+        AsadminResult result = ASADMIN.exec(6_000, "progress-custom", "3x1", "1x8", "2x1");
         assertThat(result, not(asadminOK()));
         List<ProgressMessage> prgs = ProgressMessage.grepProgressMessages(result.getStdOut());
         assertFalse(prgs.isEmpty());
diff --git a/appserver/tests/admin/tests/src/test/java/org/glassfish/main/admin/test/progress/ProgressStatusSpecialITest.java b/appserver/tests/admin/tests/src/test/java/org/glassfish/main/admin/test/progress/ProgressStatusSpecialITest.java
index e5a8bef..576e949 100644
--- a/appserver/tests/admin/tests/src/test/java/org/glassfish/main/admin/test/progress/ProgressStatusSpecialITest.java
+++ b/appserver/tests/admin/tests/src/test/java/org/glassfish/main/admin/test/progress/ProgressStatusSpecialITest.java
@@ -24,6 +24,7 @@
 import org.glassfish.main.admin.test.tool.asadmin.AsadminResult;
 import org.glassfish.main.admin.test.tool.asadmin.GlassFishTestEnvironment;
 import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
 
 import static org.glassfish.main.admin.test.progress.ProgressMessage.isIncreasing;
 import static org.glassfish.main.admin.test.tool.AsadminResultMatcher.asadminOK;
@@ -36,10 +37,12 @@
 /**
  * @author martinmares
  */
+@ExtendWith(JobTestExtension.class)
 public class ProgressStatusSpecialITest {
 
     private static final Asadmin ASADMIN = GlassFishTestEnvironment.getAsadmin();
 
+
     @Test
     public void stepBackCommand() {
         AsadminResult result = ASADMIN.exec("progress-step-back");
diff --git a/appserver/tests/admin/tests/src/test/java/org/glassfish/main/admin/test/rest/JobsResourceITest.java b/appserver/tests/admin/tests/src/test/java/org/glassfish/main/admin/test/rest/JobsResourceITest.java
index b20bb43..cd7c57c 100644
--- a/appserver/tests/admin/tests/src/test/java/org/glassfish/main/admin/test/rest/JobsResourceITest.java
+++ b/appserver/tests/admin/tests/src/test/java/org/glassfish/main/admin/test/rest/JobsResourceITest.java
@@ -80,11 +80,11 @@
         JSONObject json = response.readEntity(JSONObject.class);
         JSONArray resources = json.getJSONArray("resources");
         assertNotNull(resources);
-        assertThat(resources.length(), equalTo(1));
+        assertThat(resources.toString(), resources.length(), equalTo(1));
 
         JSONArray items = json.getJSONArray("items");
         assertNotNull(items);
-        assertThat(items.length(), equalTo(1));
+        assertThat(items.toString(), items.length(), equalTo(1));
 
         // unlike most resources that also return a parent link,
         // the jobs resource only returns child links.
@@ -112,7 +112,7 @@
 
         resources = json.getJSONArray("resources");
         assertNotNull(resources);
-        assertThat(resources.length(), equalTo(1));
+        assertThat(resources.toString(), resources.length(), equalTo(1));
 
         resource = resources.getJSONObject(0);
         assertEquals("parent", resource.getString("rel"));
diff --git a/appserver/tests/admin/tests/src/test/java/org/glassfish/main/admin/test/tool/asadmin/Asadmin.java b/appserver/tests/admin/tests/src/test/java/org/glassfish/main/admin/test/tool/asadmin/Asadmin.java
index e40e999..b18ca6d 100644
--- a/appserver/tests/admin/tests/src/test/java/org/glassfish/main/admin/test/tool/asadmin/Asadmin.java
+++ b/appserver/tests/admin/tests/src/test/java/org/glassfish/main/admin/test/tool/asadmin/Asadmin.java
@@ -22,11 +22,17 @@
 
 import java.io.File;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.List;
+import java.util.Objects;
+import java.util.function.Function;
 import java.util.logging.Level;
 import java.util.logging.Logger;
+import java.util.stream.Collectors;
 
 import static java.util.Arrays.asList;
+import static org.glassfish.main.admin.test.tool.AsadminResultMatcher.asadminOK;
+import static org.hamcrest.MatcherAssert.assertThat;
 
 /**
  * Tool for executing asadmin/asadmin.bat commands.
@@ -37,7 +43,14 @@
 public class Asadmin {
     private static final Logger LOG = Logger.getLogger(Asadmin.class.getName());
 
-    private static final int DEFAULT_TIMEOUT_MSEC = 10 * 1000;
+    private static final int DEFAULT_TIMEOUT_MSEC = 30 * 1000;
+    private static final Function<String, KeyAndValue<String>> KEYVAL_SPLITTER = s -> {
+        int equalSignPos = s.indexOf('=');
+        if (equalSignPos <= 0 || equalSignPos == s.length() - 1) {
+            return null;
+        }
+        return new KeyAndValue<>(s.substring(0, equalSignPos), s.substring(equalSignPos + 1, s.length()));
+    };
 
     private final File asadmin;
     private final String adminUser;
@@ -66,6 +79,27 @@
     }
 
 
+    public <T> KeyAndValue<T> getValue(final String key, final Function<String, T> transformer) {
+        List<KeyAndValue<T>> result = get(key, transformer);
+        if (result.isEmpty()) {
+            return null;
+        }
+        if (result.size() > 1) {
+            throw new IllegalArgumentException("The key is not concrete enough to get a single value: " + key);
+        }
+        return result.get(0);
+    }
+
+
+    public <T> List<KeyAndValue<T>> get(final String key, final Function<String, T> transformer) {
+        AsadminResult result = exec("get", key);
+        assertThat(result, asadminOK());
+        return Arrays.stream(result.getStdOut().split(System.lineSeparator())).map(KEYVAL_SPLITTER)
+            .filter(Objects::nonNull).map(kv -> new KeyAndValue<>(kv.getKey(), transformer.apply(kv.getValue())))
+            .collect(Collectors.toList());
+    }
+
+
     /**
      * Executes the command with arguments asynchronously with {@value #DEFAULT_TIMEOUT_MSEC} ms timeout.
      * The command can be attached by the attach command.
@@ -74,8 +108,8 @@
      * @param args
      * @return {@link AsadminResult} never null.
      */
-    public AsadminResult execDetached(final String... args) {
-        return exec(DEFAULT_TIMEOUT_MSEC, true, args);
+    public DetachedTerseAsadminResult execDetached(final String... args) {
+        return (DetachedTerseAsadminResult) exec(DEFAULT_TIMEOUT_MSEC, true, args);
     }
 
     /**
@@ -87,40 +121,51 @@
     public AsadminResult exec(final String... args) {
         return exec(DEFAULT_TIMEOUT_MSEC, false, args);
     }
-
+    /**
+     * Executes the command with arguments synchronously with given timeout in millis.
+     *
+     * @param timeout
+     * @param args
+     * @return {@link AsadminResult} never null.
+     */
+    public AsadminResult exec(final int timeout, final String... args) {
+        return exec(timeout, false, args);
+    }
 
     /**
      * Executes the command with arguments.
      *
      * @param timeout timeout in millis
-     * @param detached - detached command is executed asynchronously, can be attached later by the attach command.
+     * @param detachedAndTerse - detached command is executed asynchronously, can be attached later by the attach command.
      * @param args - command and arguments.
      * @return {@link AsadminResult} never null.
      */
-    public AsadminResult exec(final int timeout, final boolean detached, final String... args) {
+    private AsadminResult exec(final int timeout, final boolean detachedAndTerse, final String... args) {
         final List<String> parameters = asList(args);
-        LOG.log(Level.INFO, "exec(timeout={0}, detached={1}, args={2})", new Object[] {timeout, detached, parameters});
+        LOG.log(Level.INFO, "exec(timeout={0}, detached={1}, args={2})",
+            new Object[] {timeout, detachedAndTerse, parameters});
         final List<String> command = new ArrayList<>();
         command.add(asadmin.getAbsolutePath());
         command.add("--user");
         command.add(adminUser);
         command.add("--passwordfile");
         command.add(adminPasswordFile.getAbsolutePath());
-        if (detached) {
+        if (detachedAndTerse) {
+            command.add("--terse");
             command.add("--detach");
         }
         command.addAll(parameters);
 
-        final ProcessManager pm = new ProcessManager(command);
-        pm.setTimeoutMsec(timeout);
-        pm.setEcho(false);
+        final ProcessManager processManager = new ProcessManager(command);
+        processManager.setTimeoutMsec(timeout);
+        processManager.setEcho(false);
 
         int exitCode;
         String asadminErrorMessage = "";
         try {
-            exitCode = pm.execute();
+            exitCode = processManager.execute();
         } catch (final ProcessManagerTimeoutException e) {
-            asadminErrorMessage = "ProcessManagerTimeoutException: command timed stdOut after " + timeout + " ms.\n";
+            asadminErrorMessage = "ProcessManagerTimeoutException: command timed out after " + timeout + " ms.\n";
             exitCode = 1;
         } catch (final ProcessManagerException e) {
             LOG.log(Level.SEVERE, "The execution failed.", e);
@@ -128,16 +173,14 @@
             exitCode = 1;
         }
 
-        final String stdErr = pm.getStderr() + '\n' + asadminErrorMessage;
-        final AsadminResult ret = new AsadminResult(args[0], exitCode, pm.getStdout(), stdErr);
-        writeToStdOut(ret.getOutput());
-        return ret;
-    }
-
-
-    private static void writeToStdOut(final String text) {
-        if (!text.isEmpty()) {
-            System.out.print(text);
+        final String stdErr = processManager.getStderr() + '\n' + asadminErrorMessage;
+        final AsadminResult result;
+        if (detachedAndTerse) {
+            result = new DetachedTerseAsadminResult(args[0], exitCode, processManager.getStdout(), stdErr);
+        } else {
+            result = new AsadminResult(args[0], exitCode, processManager.getStdout(), stdErr);
         }
+        System.out.print(result.getStdOut());
+        return result;
     }
 }
diff --git a/appserver/tests/admin/tests/src/test/java/org/glassfish/main/admin/test/tool/asadmin/AsadminResult.java b/appserver/tests/admin/tests/src/test/java/org/glassfish/main/admin/test/tool/asadmin/AsadminResult.java
index e11433b..e7e09a0 100644
--- a/appserver/tests/admin/tests/src/test/java/org/glassfish/main/admin/test/tool/asadmin/AsadminResult.java
+++ b/appserver/tests/admin/tests/src/test/java/org/glassfish/main/admin/test/tool/asadmin/AsadminResult.java
@@ -31,13 +31,13 @@
     /**
      * Creates a value object instance.
      *
-     * @param cmd - command name
+     * @param commandName
      * @param exitCode
      * @param stdOut
      * @param stdErr
      */
-    public AsadminResult(final String cmd, final int exitCode, final String stdOut, final String stdErr) {
-        this.error = exitCode != 0 || containsError(stdOut, String.format("Command %s failed.", cmd));
+    public AsadminResult(final String commandName, final int exitCode, final String stdOut, final String stdErr) {
+        this.error = exitCode != 0 || containsError(stdOut, String.format("Command %s failed.", commandName));
         this.stdOut = stdOut;
         this.stdErr = stdErr;
         this.output = this.stdOut + this.stdErr;
diff --git a/appserver/tests/admin/tests/src/test/java/org/glassfish/main/admin/test/tool/asadmin/DetachedTerseAsadminResult.java b/appserver/tests/admin/tests/src/test/java/org/glassfish/main/admin/test/tool/asadmin/DetachedTerseAsadminResult.java
new file mode 100644
index 0000000..3f3daf1
--- /dev/null
+++ b/appserver/tests/admin/tests/src/test/java/org/glassfish/main/admin/test/tool/asadmin/DetachedTerseAsadminResult.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2022 Eclipse Foundation and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package org.glassfish.main.admin.test.tool.asadmin;
+
+import java.util.Arrays;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.arrayWithSize;
+
+/**
+ * @author David Matejcek
+ */
+public class DetachedTerseAsadminResult extends AsadminResult {
+
+    private final String jobId;
+
+    public DetachedTerseAsadminResult(String commandName, int exitCode, String stdOut, String stdErr) {
+        super(commandName, exitCode, stdOut, stdErr);
+        this.jobId = parseJobId(stdOut);
+    }
+
+
+    public String getJobId() {
+        return jobId;
+    }
+
+
+    private static String parseJobId(String stdOut) {
+        String[] lines = stdOut.split(System.lineSeparator());
+        assertThat(Arrays.toString(lines), lines, arrayWithSize(1));
+        return Integer.valueOf(lines[0]).toString();
+    }
+}
diff --git a/appserver/tests/admin/tests/src/test/java/org/glassfish/main/admin/test/tool/asadmin/GlassFishTestEnvironment.java b/appserver/tests/admin/tests/src/test/java/org/glassfish/main/admin/test/tool/asadmin/GlassFishTestEnvironment.java
index b41b7b3..33aa6c1 100644
--- a/appserver/tests/admin/tests/src/test/java/org/glassfish/main/admin/test/tool/asadmin/GlassFishTestEnvironment.java
+++ b/appserver/tests/admin/tests/src/test/java/org/glassfish/main/admin/test/tool/asadmin/GlassFishTestEnvironment.java
@@ -28,7 +28,6 @@
 import java.nio.file.Files;
 import java.nio.file.Path;
 import java.nio.file.Paths;
-import java.util.Comparator;
 import java.util.HashMap;
 import java.util.Locale;
 import java.util.logging.Level;
@@ -66,10 +65,10 @@
         LOG.log(Level.INFO, "Expected GlassFish directory: {0}", GF_ROOT);
         changePassword();
         Thread hook = new Thread(() -> {
-            getAsadmin().exec(10_000, false, "stop-domain", "--kill", "--force");
+            getAsadmin().exec(10_000, "stop-domain", "--kill", "--force");
         });
         Runtime.getRuntime().addShutdownHook(hook);
-        assertThat(getAsadmin().exec(30_000, false, "start-domain"), AsadminResultMatcher.asadminOK());
+        assertThat(getAsadmin().exec(30_000, "start-domain"), AsadminResultMatcher.asadminOK());
     }
 
     /**
@@ -130,28 +129,6 @@
 
 
     /**
-     * osgi-cache workaround
-     */
-    public static void deleteOsgiDirectory() {
-        Path osgiCacheDir = GF_ROOT.toPath().resolve(Paths.get("domains", "domain1", "osgi-cache"));
-        try {
-            Files.list(osgiCacheDir).forEach(GlassFishTestEnvironment::deleteSubpaths);
-        } catch (IOException e) {
-            throw new IllegalStateException(e);
-        }
-    }
-
-
-    public static void deleteSubpaths(final Path path) {
-        try {
-            Files.walk(path).sorted(Comparator.reverseOrder()).map(Path::toFile).forEach(File::delete);
-        } catch (Exception e) {
-            throw new IllegalStateException("Cannot delete path recursively: " + path, e);
-        }
-    }
-
-
-    /**
      * Useful for a heuristic inside Eclipse and other environments.
      *
      * @return Absolute path to the glassfish directory.
@@ -203,7 +180,7 @@
 
     private static void changePassword() {
         final Asadmin asadmin = new Asadmin(ASADMIN, ADMIN_USER, PASSWORD_FILE_FOR_UPDATE);
-        final AsadminResult result = asadmin.exec(5_000, false, "change-admin-password");
+        final AsadminResult result = asadmin.exec(5_000, "change-admin-password");
         if (result.isError()) {
             // probably changed by previous execution without maven clean
             System.out.println("Admin password NOT changed.");
diff --git a/appserver/tests/admin/tests/src/test/java/org/glassfish/main/admin/test/tool/asadmin/KeyAndValue.java b/appserver/tests/admin/tests/src/test/java/org/glassfish/main/admin/test/tool/asadmin/KeyAndValue.java
new file mode 100644
index 0000000..a5ee39c
--- /dev/null
+++ b/appserver/tests/admin/tests/src/test/java/org/glassfish/main/admin/test/tool/asadmin/KeyAndValue.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (c) 2022 Eclipse Foundation and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package org.glassfish.main.admin.test.tool.asadmin;
+
+import java.util.Comparator;
+import java.util.Objects;
+
+import static java.util.Comparator.comparing;
+
+/**
+ * @param <T> value type
+ *
+ * @author David Matejcek
+ */
+public class KeyAndValue<T> implements Comparable<KeyAndValue<?>> {
+
+
+    // note: toString should be fast.
+    private static final Comparator<Object> NULL_SAFE_COMPARATOR = comparing(o -> o == null ? "" : o.toString(),
+        String::compareTo);
+
+    private final String key;
+    private final T value;
+
+    public KeyAndValue(final String key, final T value) {
+        this.key = key;
+        this.value = value;
+    }
+
+
+    public String getKey() {
+        return key;
+    }
+
+
+    public T getValue() {
+        return value;
+    }
+
+
+    @Override
+    public String toString() {
+        return getKey() + '=' + getValue();
+    }
+
+
+    @Override
+    public boolean equals(Object object) {
+        if (object instanceof KeyAndValue<?>) {
+            KeyAndValue<?> another = (KeyAndValue<?>) object;
+            return Objects.equals(key, another.key) && Objects.equals(value, another.value);
+        }
+        return false;
+    }
+
+
+    @Override
+    public int hashCode() {
+        return key.hashCode();
+    }
+
+
+    @Override
+    public int compareTo(KeyAndValue<?> o) {
+        int keysComparisonResult = key.compareTo(o.key);
+        if (keysComparisonResult != 0) {
+            return keysComparisonResult;
+        }
+        return Objects.compare(value, o.value, NULL_SAFE_COMPARATOR);
+    }
+}