Merge "Add Network Logging Feature"
diff --git a/INFO.yaml b/INFO.yaml
index 26eea58..0b0f922 100644
--- a/INFO.yaml
+++ b/INFO.yaml
@@ -1,18 +1,24 @@
 ---
-project: 'policy/common'
-project_creation_date: '2017-02-17'
+project: 'policy-common'
+project_creation_date: '2017-02-14'
 lifecycle_state: 'Incubation'
+project_category: ''
 project_lead: &onap_releng_ptl
     name: 'Pamela Dragosh'
     email: 'pdragosh@research.att.com'
     id: 'pdragosh'
     company: 'ATT'
-    timezone: 'America/New York'
+    timezone: 'America/New_York'
 primary_contact: *onap_releng_ptl
 issue_tracking:
     type: 'jira'
     url: 'https://jira.onap.org/projects/POLICY'
     key: 'POLICY'
+mailing_list:
+    type: 'groups.io'
+    url: 'lists.onap.org'
+    tag: '<[sub-project_name]>'
+realtime_discussion: ''
 meetings:
     - type: 'zoom'
       agenda: 'https://wiki.onap.org/pages/viewpage.action?pageId=6589781'
@@ -21,6 +27,23 @@
       channel: 'n/a'
       repeats: 'weekly'
       time: '13:00 UTC'
+repositories:
+    - 'policy-apex-pdp'
+    - 'policy-api'
+    - 'policy-common'
+    - 'policy-core'
+    - 'policy-distribution'
+    - 'policy-docker'
+    - 'policy-drools-applications'
+    - 'policy-drools-pdp'
+    - 'policy-engine'
+    - 'policy-gui'
+    - 'policy-models'
+    - 'policy-oom'
+    - 'policy-pap'
+    - 'policy-parent'
+    - 'policy-pdp'
+    - 'policy-xacml-pdp'
 committers:
     - <<: *onap_releng_ptl
     - name: 'Jorge Hernandez'
diff --git a/gson/pom.xml b/gson/pom.xml
index d02ede1..7d7797a 100644
--- a/gson/pom.xml
+++ b/gson/pom.xml
@@ -47,6 +47,11 @@
             <artifactId>gson</artifactId>
         </dependency>
         <dependency>
+            <groupId>org.projectlombok</groupId>
+            <artifactId>lombok</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
             <groupId>org.assertj</groupId>
             <artifactId>assertj-core</artifactId>
             <scope>test</scope>
diff --git a/gson/src/main/java/org/onap/policy/common/gson/GsonMessageBodyHandler.java b/gson/src/main/java/org/onap/policy/common/gson/GsonMessageBodyHandler.java
index fc8e23e..9dad6db 100644
--- a/gson/src/main/java/org/onap/policy/common/gson/GsonMessageBodyHandler.java
+++ b/gson/src/main/java/org/onap/policy/common/gson/GsonMessageBodyHandler.java
@@ -21,6 +21,7 @@
 package org.onap.policy.common.gson;
 
 import com.google.gson.Gson;
+
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.InputStreamReader;
@@ -36,6 +37,9 @@
 import javax.ws.rs.ext.MessageBodyReader;
 import javax.ws.rs.ext.MessageBodyWriter;
 import javax.ws.rs.ext.Provider;
+
+import lombok.Getter;
+
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -52,7 +56,8 @@
     /**
      * Object to be used to serialize and de-serialize.
      */
-    private Gson gson;
+    @Getter
+    private final Gson gson;
 
     /**
      * Constructs the object, using a plain Gson object.
diff --git a/lombok.config b/lombok.config
new file mode 100644
index 0000000..df71bb6
--- /dev/null
+++ b/lombok.config
@@ -0,0 +1,2 @@
+config.stopBubbling = true
+lombok.addLombokGeneratedAnnotation = true
diff --git a/pdp-common/src/main/java/org/onap/policy/pdp/common/models/Policy.java b/pdp-common/src/main/java/org/onap/policy/pdp/common/models/Policy.java
index 002a777..b756386 100644
--- a/pdp-common/src/main/java/org/onap/policy/pdp/common/models/Policy.java
+++ b/pdp-common/src/main/java/org/onap/policy/pdp/common/models/Policy.java
@@ -20,6 +20,7 @@
 
 package org.onap.policy.pdp.common.models;
 
+import lombok.EqualsAndHashCode;
 import lombok.Getter;
 import lombok.Setter;
 import lombok.ToString;
@@ -32,9 +33,11 @@
 @Getter
 @Setter
 @ToString
+@EqualsAndHashCode
 public class Policy {
 
     private String name;
+    private String policyVersion;
     private String policyType;
     private String policyTypeVersion;
     private String properties;
diff --git a/utils/src/main/java/org/onap/policy/common/utils/resources/TextFileUtils.java b/utils/src/main/java/org/onap/policy/common/utils/resources/TextFileUtils.java
new file mode 100644
index 0000000..5aeacf2
--- /dev/null
+++ b/utils/src/main/java/org/onap/policy/common/utils/resources/TextFileUtils.java
@@ -0,0 +1,110 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2019 Nordix Foundation.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.policy.common.utils.resources;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.nio.file.Files;
+
+/**
+ * The Class TextFileUtils is class that provides useful functions for handling text files. Functions to read and write
+ * text files to strings and strings are provided.
+ *
+ * @author Liam Fallon (liam.fallon@est.tech)
+ */
+public abstract class TextFileUtils {
+    private static final String UTF_8 = "UTF-8";
+    private static final int READER_CHAR_BUFFER_SIZE_4096 = 4096;
+
+    private TextFileUtils() {
+        // This class cannot be initialized
+    }
+
+    /**
+     * Method to return the contents of a text file as a string.
+     *
+     * @param textFilePath The path to the file as a string
+     * @return A string containing the contents of the file
+     * @throws IOException on errors reading text from the file
+     */
+    public static String getTextFileAsString(final String textFilePath) throws IOException {
+        final File textFile = new File(textFilePath);
+        return new String(Files.readAllBytes(textFile.toPath()), UTF_8);
+    }
+
+    /**
+     * Method to write contents of a string to a text file.
+     *
+     * @param outString The string to write
+     * @param textFilePath The path to the file as a string
+     * @throws IOException on errors reading text from the file
+     */
+    public static void putStringAsTextFile(final String outString, final String textFilePath) throws IOException {
+        final File textFile = new File(textFilePath);
+        putStringAsFile(outString, textFile);
+    }
+
+    /**
+     * Method to write contents of a string to a text file.
+     *
+     * @param outString The string to write
+     * @param textFile The file to write the string to
+     * @throws IOException on errors reading text from the file
+     */
+    public static void putStringAsFile(final String outString, final File textFile) throws IOException {
+        Files.write(textFile.toPath(), outString.getBytes(UTF_8));
+    }
+
+    /**
+     * Method to return the contents of a text steam as a string.
+     *
+     * @param textStream The stream
+     * @return A string containing the output of the stream as text
+     * @throws IOException on errors reading text from the file
+     */
+    public static String getStreamAsString(final InputStream textStream) throws IOException {
+        return getReaderAsString(new InputStreamReader(textStream, UTF_8));
+    }
+
+    /**
+     * Method to return the contents of a reader steam as a string. This closes the reader after use
+     *
+     * @param textReader The reader
+     * @return A string containing the output of the reader as text
+     * @throws IOException on errors reading text from the file
+     */
+    public static String getReaderAsString(final Reader textReader) throws IOException {
+        final StringBuilder builder = new StringBuilder();
+        int charsRead = -1;
+        final char[] chars = new char[READER_CHAR_BUFFER_SIZE_4096];
+        do {
+            charsRead = textReader.read(chars);
+            if (charsRead > 0) {
+                builder.append(chars, 0, charsRead);
+            }
+        }
+        while (charsRead > 0);
+        return builder.toString();
+    }
+}
diff --git a/utils/src/main/java/org/onap/policy/common/utils/security/CryptoUtils.java b/utils/src/main/java/org/onap/policy/common/utils/security/CryptoUtils.java
index ade8b46..ebe0483 100644
--- a/utils/src/main/java/org/onap/policy/common/utils/security/CryptoUtils.java
+++ b/utils/src/main/java/org/onap/policy/common/utils/security/CryptoUtils.java
@@ -64,36 +64,14 @@
     private static final String RANDOM_NUMBER_GENERATOR = "SHA1PRNG";
 
     /**
-     * This method is used as the main entry point when testing.
-     *
-     */
-    public static void main(String[] args) {
-        if (args.length == 3) {
-            if (args[0].equals("enc")) {
-                String encryptedValue = encrypt(args[1], args[2]);
-                logger.info("original value: " + args[1] + " encrypted value: " + encryptedValue);
-            } else if (args[0].equals("dec")) {
-                String decryptedValue = decrypt(args[1], args[2]);
-                logger.info("original value: " + args[1] + " decrypted value: " + decryptedValue);
-            } else {
-                logger.info("Unknown request: " + args[0]);
-            }
-        } else {
-            logger.info("Usage  : CryptoUtils enc/dec password   secretKey");
-            logger.info("Example: CryptoUtils enc     HelloWorld 1234");
-            logger.info("Example: CryptoUtils dec     enc:112233 1234");
-        }
-    }
-
-    public CryptoUtils(SecretKeySpec secretKeySpec) {
-        this.secretKeySpec = secretKeySpec;
-    }
-
-    /**
      * CryptoUtils - encryption tool constructor.
      * @param secretKey
      *  AES supports 128, 192 or 256-bit long key size, it can be plain text or generated with key generator
      */
+    public CryptoUtils(SecretKeySpec secretKeySpec) {
+        this.secretKeySpec = secretKeySpec;
+    }
+
     public CryptoUtils(String secretKey) {
         this.secretKeySpec = readSecretKeySpec(secretKey);
     }
@@ -256,4 +234,26 @@
     public static Boolean isEncrypted(String value) {
         return (value != null && value.startsWith("enc:"));
     }
+
+    /**
+     * This method is used as the main entry point when testing.
+     *
+     */
+    public static void main(String[] args) {
+        if (args.length == 3) {
+            if ("enc".equals(args[0])) {
+                String encryptedValue = encrypt(args[1], args[2]);
+                logger.info("original value: " + args[1] + " encrypted value: " + encryptedValue);
+            } else if ("dec".equals(args[0])) {
+                String decryptedValue = decrypt(args[1], args[2]);
+                logger.info("original value: " + args[1] + " decrypted value: " + decryptedValue);
+            } else {
+                logger.info("Unknown request: " + args[0]);
+            }
+        } else {
+            logger.info("Usage  : CryptoUtils enc/dec password   secretKey");
+            logger.info("Example: CryptoUtils enc     HelloWorld 1234");
+            logger.info("Example: CryptoUtils dec     enc:112233 1234");
+        }
+    }
 }
\ No newline at end of file
diff --git a/utils/src/main/java/org/onap/policy/common/utils/services/ServiceManager.java b/utils/src/main/java/org/onap/policy/common/utils/services/ServiceManager.java
index 8bf89d5..13cd6de 100644
--- a/utils/src/main/java/org/onap/policy/common/utils/services/ServiceManager.java
+++ b/utils/src/main/java/org/onap/policy/common/utils/services/ServiceManager.java
@@ -31,10 +31,15 @@
  * Manages a series of services. The services are started in order, and stopped in reverse
  * order.
  */
-public class ServiceManager {
+public class ServiceManager implements Startable {
     private static final Logger logger = LoggerFactory.getLogger(ServiceManager.class);
 
     /**
+     * Manager name.
+     */
+    private final String name;
+
+    /**
      * Services to be started/stopped.
      */
     private final Deque<Service> items = new LinkedList<>();
@@ -45,6 +50,25 @@
     private boolean running;
 
     /**
+     * Constructs the object, with a default name.
+     */
+    public ServiceManager() {
+        this("service manager");
+    }
+
+    /**
+     * Constructs the object.
+     * @param name the manager's name, used for logging purposes
+     */
+    public ServiceManager(String name) {
+        this.name = name;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    /**
      * Adds a pair of service actions to the manager.
      *
      * @param stepName name to be logged when the service is started/stopped
@@ -54,7 +78,7 @@
      */
     public synchronized ServiceManager addAction(String stepName, RunnableWithEx starter, RunnableWithEx stopper) {
         if (running) {
-            throw new IllegalStateException("services are already running; cannot add " + stepName);
+            throw new IllegalStateException(name + " is already running; cannot add " + stepName);
         }
 
         items.add(new Service(stepName, starter, stopper));
@@ -71,44 +95,47 @@
      */
     public synchronized ServiceManager addService(String stepName, Startable service) {
         if (running) {
-            throw new IllegalStateException("services are already running; cannot add " + stepName);
+            throw new IllegalStateException(name + " is already running; cannot add " + stepName);
         }
 
         items.add(new Service(stepName, () -> service.start(), () -> service.stop()));
         return this;
     }
 
-    /**
-     * Starts each service, in order. If a service throws an exception, then the
-     * previously started services are stopped, in reverse order.
-     *
-     * @throws ServiceManagerException if a service fails to start
-     */
-    public synchronized void start() throws ServiceManagerException {
+    @Override
+    public synchronized boolean isAlive() {
+        return running;
+    }
+
+    @Override
+    public synchronized boolean start() {
         if (running) {
-            throw new IllegalStateException("services are already running");
+            throw new IllegalStateException(name + " is already running");
         }
 
+        logger.info("{} starting", name);
+
         // tracks the services that have been started so far
         Deque<Service> started = new LinkedList<>();
         Exception ex = null;
 
         for (Service item : items) {
             try {
-                logger.info("starting {}", item.stepName);
+                logger.info("{} starting {}", name, item.stepName);
                 item.starter.run();
                 started.add(item);
 
             } catch (Exception e) {
-                logger.error("failed to start {}; rewinding steps", item.stepName);
+                logger.error("{} failed to start {}; rewinding steps", name, item.stepName);
                 ex = e;
                 break;
             }
         }
 
         if (ex == null) {
+            logger.info("{} started", name);
             running = true;
-            return;
+            return true;
         }
 
         // one of the services failed to start - rewind those we've previously started
@@ -116,26 +143,27 @@
             rewind(started);
 
         } catch (ServiceManagerException e) {
-            logger.error("rewind failed", e);
+            logger.error("{} rewind failed", name, e);
         }
 
         throw new ServiceManagerException(ex);
     }
 
-    /**
-     * Stops the services, in reverse order from which they were started. Stops all of the
-     * services, even if one of the "stop" functions throws an exception. Assumes that
-     * {@link #start()} has completed successfully.
-     *
-     * @throws ServiceManagerException if a service fails to stop
-     */
-    public synchronized void stop() throws ServiceManagerException {
+    @Override
+    public synchronized boolean stop() {
         if (!running) {
-            throw new IllegalStateException("services are not running");
+            throw new IllegalStateException(name + " is not running");
         }
 
         running = false;
         rewind(items);
+
+        return true;
+    }
+
+    @Override
+    public void shutdown() {
+        stop();
     }
 
     /**
@@ -148,21 +176,25 @@
     private void rewind(Deque<Service> running) throws ServiceManagerException {
         Exception ex = null;
 
+        logger.info("{} stopping", name);
+
         // stop everything, in reverse order
         Iterator<Service> it = running.descendingIterator();
         while (it.hasNext()) {
             Service item = it.next();
             try {
-                logger.info("stopping {}", item.stepName);
+                logger.info("{} stopping {}", name, item.stepName);
                 item.stopper.run();
             } catch (Exception e) {
-                logger.error("failed to stop {}", item.stepName);
+                logger.error("{} failed to stop {}", name, item.stepName);
                 ex = e;
 
                 // do NOT break or re-throw, as we must stop ALL remaining items
             }
         }
 
+        logger.info("{} stopped", name);
+
         if (ex != null) {
             throw new ServiceManagerException(ex);
         }
@@ -185,6 +217,6 @@
 
     @FunctionalInterface
     public static interface RunnableWithEx {
-        public void run() throws Exception;
+        void run() throws Exception;
     }
 }
diff --git a/utils/src/main/java/org/onap/policy/common/utils/services/ServiceManagerException.java b/utils/src/main/java/org/onap/policy/common/utils/services/ServiceManagerException.java
index 3daa441..ac37b6b 100644
--- a/utils/src/main/java/org/onap/policy/common/utils/services/ServiceManagerException.java
+++ b/utils/src/main/java/org/onap/policy/common/utils/services/ServiceManagerException.java
@@ -23,7 +23,7 @@
 /**
  * Exceptions thrown by the ServiceManager.
  */
-public class ServiceManagerException extends Exception {
+public class ServiceManagerException extends RuntimeException {
     private static final long serialVersionUID = 1L;
 
     public ServiceManagerException() {
diff --git a/utils/src/test/java/org/onap/policy/common/utils/resources/TextFileUtilsTest.java b/utils/src/test/java/org/onap/policy/common/utils/resources/TextFileUtilsTest.java
new file mode 100644
index 0000000..7f246ab
--- /dev/null
+++ b/utils/src/test/java/org/onap/policy/common/utils/resources/TextFileUtilsTest.java
@@ -0,0 +1,53 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2019 Nordix Foundation.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.policy.common.utils.resources;
+
+import static org.junit.Assert.assertEquals;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+
+import org.junit.Test;
+
+/**
+ * Test text file utilities.
+ * @author Liam Fallon (liam.fallon@est.tech)
+ */
+public class TextFileUtilsTest {
+
+    private static final String FILE_CONTENT = "This is the contents of a text file";
+
+    @Test
+    public void test() throws IOException {
+        final File tempTextFile = File.createTempFile("Test", "txt");
+
+        TextFileUtils.putStringAsTextFile(FILE_CONTENT, tempTextFile.getAbsolutePath());
+
+        final String textFileString0 = TextFileUtils.getTextFileAsString(tempTextFile.getAbsolutePath());
+        assertEquals(FILE_CONTENT, textFileString0);
+
+        final FileInputStream fis = new FileInputStream(tempTextFile);
+        final String textFileString1 = TextFileUtils.getStreamAsString(fis);
+        assertEquals(textFileString0, textFileString1);
+
+    }
+}
diff --git a/utils/src/test/java/org/onap/policy/common/utils/services/ServiceManagerTest.java b/utils/src/test/java/org/onap/policy/common/utils/services/ServiceManagerTest.java
index 49c0599..b7774a5 100644
--- a/utils/src/test/java/org/onap/policy/common/utils/services/ServiceManagerTest.java
+++ b/utils/src/test/java/org/onap/policy/common/utils/services/ServiceManagerTest.java
@@ -23,6 +23,8 @@
 import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
 import static org.assertj.core.api.Assertions.assertThatThrownBy;
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.verify;
@@ -36,7 +38,8 @@
 import org.onap.policy.common.utils.services.ServiceManager.RunnableWithEx;
 
 public class ServiceManagerTest {
-    private static final String ALREADY_RUNNING = "services are already running";
+    private static final String MY_NAME = "my-name";
+    private static final String ALREADY_RUNNING = MY_NAME + " is already running";
     private static final String EXPECTED_EXCEPTION = "expected exception";
 
     private ServiceManager svcmgr;
@@ -46,7 +49,17 @@
      */
     @Before
     public void setUp() {
-        svcmgr = new ServiceManager();
+        svcmgr = new ServiceManager(MY_NAME);
+    }
+
+    @Test
+    public void testServiceName() {
+        assertEquals("service manager", new ServiceManager().getName());
+    }
+
+    @Test
+    public void testGetName() {
+        assertEquals(MY_NAME, svcmgr.getName());
     }
 
     @Test
@@ -106,16 +119,20 @@
         Startable start1 = mock(Startable.class);
         svcmgr.addService("test start", start1);
 
-        svcmgr.start();
+        assertTrue(svcmgr.start());
+
+        assertTrue(svcmgr.isAlive());
         verify(start1).start();
         verify(start1, never()).stop();
 
         // cannot re-start
-        assertThatIllegalStateException().isThrownBy(() -> svcmgr.start())
-                        .withMessage(ALREADY_RUNNING);
+        assertThatIllegalStateException().isThrownBy(() -> svcmgr.start()).withMessage(ALREADY_RUNNING);
 
         // verify that it didn't try to start the service again
         verify(start1).start();
+
+        // still running
+        assertTrue(svcmgr.isAlive());
     }
 
     @Test
@@ -140,6 +157,8 @@
 
         assertThatThrownBy(() -> svcmgr.start()).isInstanceOf(ServiceManagerException.class).hasCause(exception);
 
+        assertFalse(svcmgr.isAlive());
+
         verify(start1).start();
         verify(start2).start();
         verify(start3).start();
@@ -177,6 +196,8 @@
         svcmgr.addService("fifth test start rewind", start5);
 
         assertThatThrownBy(() -> svcmgr.start()).isInstanceOf(ServiceManagerException.class).hasCause(exception);
+
+        assertFalse(svcmgr.isAlive());
     }
 
     @Test
@@ -185,8 +206,7 @@
         svcmgr.addService("first stop", start1);
 
         // cannot stop until started
-        assertThatIllegalStateException().isThrownBy(() -> svcmgr.stop())
-                        .withMessage("services are not running");
+        assertThatIllegalStateException().isThrownBy(() -> svcmgr.stop()).withMessage(MY_NAME + " is not running");
 
         // verify that it didn't try to stop the service
         verify(start1, never()).stop();
@@ -194,7 +214,9 @@
         // start it
         svcmgr.start();
 
-        svcmgr.stop();
+        assertTrue(svcmgr.stop());
+
+        assertFalse(svcmgr.isAlive());
         verify(start1).stop();
     }
 
@@ -218,6 +240,28 @@
         verify(stop1).run();
         verify(start2).start();
         verify(start2).stop();
+
+        assertFalse(svcmgr.isAlive());
+    }
+
+    @Test
+    public void testShutdown() throws Exception {
+        Startable start1 = mock(Startable.class);
+        svcmgr.addService("first stop", start1);
+
+        // cannot stop until started
+        assertThatIllegalStateException().isThrownBy(() -> svcmgr.shutdown()).withMessage(MY_NAME + " is not running");
+
+        // verify that it didn't try to stop the service
+        verify(start1, never()).stop();
+
+        // start it
+        svcmgr.start();
+
+        svcmgr.shutdown();
+
+        assertFalse(svcmgr.isAlive());
+        verify(start1).stop();
     }
 
     @Test
@@ -242,6 +286,8 @@
 
         assertThatThrownBy(() -> svcmgr.stop()).isInstanceOf(ServiceManagerException.class).hasCause(exception);
 
+        assertFalse(svcmgr.isAlive());
+
         // all of them should have been stopped, in reverse order
         assertEquals(Arrays.asList("rewind5", "rewind4", "rewind3", "rewind2", "rewind1").toString(), lst.toString());
     }