Merge "Use Alpine as base docker image in /policy/pap"
diff --git a/main/src/main/java/org/onap/policy/pap/main/comm/MultiPdpStatusListener.java b/main/src/main/java/org/onap/policy/pap/main/comm/MultiPdpStatusListener.java
index 38588d8..652756a 100644
--- a/main/src/main/java/org/onap/policy/pap/main/comm/MultiPdpStatusListener.java
+++ b/main/src/main/java/org/onap/policy/pap/main/comm/MultiPdpStatusListener.java
@@ -30,12 +30,15 @@
 import org.onap.policy.common.endpoints.event.comm.Topic.CommInfrastructure;
 import org.onap.policy.common.endpoints.listeners.TypedMessageListener;
 import org.onap.policy.pdp.common.models.PdpStatus;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 /**
  * Listener for PDP Status messages expected to be received from multiple PDPs. The
  * listener "completes" once a message has been seen from all of the PDPs.
  */
-public class MultiPdpStatusListener implements TypedMessageListener<PdpStatus> {
+public abstract class MultiPdpStatusListener implements TypedMessageListener<PdpStatus> {
+    private static final Logger logger = LoggerFactory.getLogger(MultiPdpStatusListener.class);
 
     /**
      * This is decremented once a message has been received from every PDP.
@@ -43,40 +46,40 @@
     private final CountDownLatch allSeen = new CountDownLatch(1);
 
     /**
-     * PDPs from which no message has been received yet.
+     * IDs for which no message has been received yet.
      */
-    private final Set<String> unseenPdpNames = ConcurrentHashMap.newKeySet();
+    private final Set<String> unseenIds = ConcurrentHashMap.newKeySet();
 
     /**
      * Constructs the object.
      *
-     * @param pdpName name of the PDP for which to wait
+     * @param id ID for which to wait
      */
-    public MultiPdpStatusListener(String pdpName) {
-        unseenPdpNames.add(pdpName);
+    public MultiPdpStatusListener(String id) {
+        unseenIds.add(id);
     }
 
     /**
      * Constructs the object.
      *
-     * @param pdpNames names of the PDP for which to wait
+     * @param ids IDs for which to wait
      */
-    public MultiPdpStatusListener(Collection<String> pdpNames) {
-        if (pdpNames.isEmpty()) {
+    public MultiPdpStatusListener(Collection<String> ids) {
+        if (ids.isEmpty()) {
             allSeen.countDown();
 
         } else {
-            unseenPdpNames.addAll(pdpNames);
+            unseenIds.addAll(ids);
         }
     }
 
     /**
-     * Gets the set of names for which messages have not yet been received.
+     * Gets the set of IDs for which messages have not yet been received.
      *
-     * @return the names of the PDPs that have not been seen yet
+     * @return the IDs that have not been seen yet
      */
-    public SortedSet<String> getUnseenPdpNames() {
-        return new TreeSet<>(unseenPdpNames);
+    public SortedSet<String> getUnseenIds() {
+        return new TreeSet<>(unseenIds);
     }
 
     /**
@@ -93,17 +96,37 @@
     }
 
     /**
-     * Indicates that a message was received for a PDP. Triggers completion of
-     * {@link #await(long, TimeUnit)} if all PDPs have received a message. Threads may
-     * override this method to process a message. However, they should still invoke this
-     * method so that PDPs can be properly tracked.
+     * After giving the event to the subclass via
+     * {@link #handleEvent(CommInfrastructure, String, PdpStatus)}, this triggers
+     * completion of {@link #await(long, TimeUnit)}, if all PDPs have received a message.
      */
     @Override
-    public void onTopicEvent(CommInfrastructure infra, String topic, PdpStatus message) {
-        unseenPdpNames.remove(message.getName());
+    public final void onTopicEvent(CommInfrastructure infra, String topic, PdpStatus message) {
+        String id = null;
+        try {
+            id = handleEvent(infra, topic, message);
+        } catch (RuntimeException e) {
+            logger.warn("handleEvent failed due to: {}", e.getMessage(), e);
+        }
 
-        if (unseenPdpNames.isEmpty()) {
+        if (id == null) {
+            return;
+        }
+
+        unseenIds.remove(id);
+
+        if (unseenIds.isEmpty()) {
             allSeen.countDown();
         }
     }
+
+    /**
+     * Indicates that a message was received for a PDP.
+     *
+     * @param infra infrastructure with which the message was received
+     * @param topic topic on which the message was received
+     * @param message message that was received
+     * @return the ID extracted from the message, or {@code null} if it cannot be extracted
+     */
+    protected abstract String handleEvent(CommInfrastructure infra, String topic, PdpStatus message);
 }
diff --git a/main/src/main/java/org/onap/policy/pap/main/rest/HealthCheckRestControllerV1.java b/main/src/main/java/org/onap/policy/pap/main/rest/HealthCheckRestControllerV1.java
new file mode 100644
index 0000000..bf36a1a
--- /dev/null
+++ b/main/src/main/java/org/onap/policy/pap/main/rest/HealthCheckRestControllerV1.java
@@ -0,0 +1,51 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2019 Nordix Foundation.
+ *  Modifications Copyright (C) 2019 AT&T Intellectual Property.
+ * ================================================================================
+ * 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.pap.main.rest;
+
+import io.swagger.annotations.ApiOperation;
+import io.swagger.annotations.ApiResponse;
+import io.swagger.annotations.ApiResponses;
+import io.swagger.annotations.Authorization;
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.core.Response;
+import org.onap.policy.common.endpoints.report.HealthCheckReport;
+
+/**
+ * Class to provide REST endpoints for PAP component health check.
+ *
+ * @author Ram Krishna Verma (ram.krishna.verma@est.tech)
+ */
+public class HealthCheckRestControllerV1 extends PapRestControllerV1 {
+
+    @GET
+    @Path("healthcheck")
+    @ApiOperation(value = "Perform healthcheck",
+                    notes = "Returns healthy status of the Policy Administration component",
+                    response = HealthCheckReport.class, authorizations = @Authorization(value = AUTHORIZATION_TYPE))
+    @ApiResponses(value = {@ApiResponse(code = AUTHENTICATION_ERROR_CODE, message = AUTHENTICATION_ERROR_MESSAGE),
+                    @ApiResponse(code = AUTHORIZATION_ERROR_CODE, message = AUTHORIZATION_ERROR_MESSAGE),
+                    @ApiResponse(code = SERVER_ERROR_CODE, message = SERVER_ERROR_MESSAGE)})
+    public Response healthcheck() {
+        return Response.status(Response.Status.OK).entity(new HealthCheckProvider().performHealthCheck()).build();
+    }
+}
diff --git a/main/src/main/java/org/onap/policy/pap/main/rest/PapRestController.java b/main/src/main/java/org/onap/policy/pap/main/rest/PapRestController.java
deleted file mode 100644
index 8d153f3..0000000
--- a/main/src/main/java/org/onap/policy/pap/main/rest/PapRestController.java
+++ /dev/null
@@ -1,86 +0,0 @@
-/*-
- * ============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.pap.main.rest;
-
-import io.swagger.annotations.Api;
-import io.swagger.annotations.ApiOperation;
-import io.swagger.annotations.ApiResponse;
-import io.swagger.annotations.ApiResponses;
-import io.swagger.annotations.Authorization;
-import io.swagger.annotations.BasicAuthDefinition;
-import io.swagger.annotations.Info;
-import io.swagger.annotations.SecurityDefinition;
-import io.swagger.annotations.SwaggerDefinition;
-import io.swagger.annotations.Tag;
-
-import javax.ws.rs.GET;
-import javax.ws.rs.Path;
-import javax.ws.rs.Produces;
-import javax.ws.rs.core.MediaType;
-import javax.ws.rs.core.Response;
-
-import org.onap.policy.common.endpoints.report.HealthCheckReport;
-
-/**
- * Class to provide REST endpoints for PAP component.
- *
- * @author Ram Krishna Verma (ram.krishna.verma@est.tech)
- */
-@Path("/policy/pap/v1")
-@Api(value = "Policy Administration (PAP) API")
-@Produces(MediaType.APPLICATION_JSON)
-@SwaggerDefinition(info = @Info(
-        description = "Policy Administration is responsible for the deployment life cycle of policies as well as "
-                + "interworking with the mechanisms required to orchestrate the nodes and containers on which "
-                + "policies run. It is also responsible for the administration of policies at run time;"
-                + " ensuring that policies are available to users, that policies are executing correctly,"
-                + " and that the state and status of policies is monitored",
-        version = "v1.0", title = "Policy Administration"), consumes = { MediaType.APPLICATION_JSON },
-        produces = { MediaType.APPLICATION_JSON },
-        schemes = { SwaggerDefinition.Scheme.HTTP, SwaggerDefinition.Scheme.HTTPS },
-        tags = { @Tag(name = "policy-administration", description = "Policy Administration Service Operations") },
-        securityDefinition = @SecurityDefinition(basicAuthDefinitions = { @BasicAuthDefinition(key = "basicAuth") }))
-public class PapRestController {
-
-    @GET
-    @Path("healthcheck")
-    @ApiOperation(value = "Perform healthcheck",
-            notes = "Returns healthy status of the Policy Administration component", response = HealthCheckReport.class,
-            authorizations = @Authorization(value = "basicAuth"))
-    @ApiResponses(value = { @ApiResponse(code = 401, message = "Authentication Error"),
-        @ApiResponse(code = 403, message = "Authorization Error"),
-        @ApiResponse(code = 500, message = "Internal Server Error") })
-    public Response healthcheck() {
-        return Response.status(Response.Status.OK).entity(new HealthCheckProvider().performHealthCheck()).build();
-    }
-
-    @GET
-    @Path("statistics")
-    @ApiOperation(value = "Fetch current statistics",
-            notes = "Returns current statistics of the Policy Administration component",
-            response = StatisticsReport.class, authorizations = @Authorization(value = "basicAuth"))
-    @ApiResponses(value = { @ApiResponse(code = 401, message = "Authentication Error"),
-        @ApiResponse(code = 403, message = "Authorization Error"),
-        @ApiResponse(code = 500, message = "Internal Server Error") })
-    public Response statistics() {
-        return Response.status(Response.Status.OK).entity(new StatisticsProvider().fetchCurrentStatistics()).build();
-    }
-}
diff --git a/main/src/main/java/org/onap/policy/pap/main/rest/PapRestControllerV1.java b/main/src/main/java/org/onap/policy/pap/main/rest/PapRestControllerV1.java
new file mode 100644
index 0000000..bd18223
--- /dev/null
+++ b/main/src/main/java/org/onap/policy/pap/main/rest/PapRestControllerV1.java
@@ -0,0 +1,67 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2019 Nordix Foundation.
+ *  Modifications Copyright (C) 2019 AT&T Intellectual Property.
+ * ================================================================================
+ * 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.pap.main.rest;
+
+import io.swagger.annotations.Api;
+import io.swagger.annotations.BasicAuthDefinition;
+import io.swagger.annotations.Info;
+import io.swagger.annotations.SecurityDefinition;
+import io.swagger.annotations.SwaggerDefinition;
+import io.swagger.annotations.Tag;
+import java.net.HttpURLConnection;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.MediaType;
+
+/**
+ * Version v1 common superclass to provide REST endpoints for PAP component.
+ *
+ * @author Ram Krishna Verma (ram.krishna.verma@est.tech)
+ */
+@Path("/policy/pap/v1")
+@Api(value = "Policy Administration (PAP) API")
+@Produces(MediaType.APPLICATION_JSON)
+@SwaggerDefinition(
+    info = @Info(description =
+                    "Policy Administration is responsible for the deployment life cycle of policies as well as "
+                    + "interworking with the mechanisms required to orchestrate the nodes and containers on which "
+                    + "policies run. It is also responsible for the administration of policies at run time;"
+                    + " ensuring that policies are available to users, that policies are executing correctly,"
+                    + " and that the state and status of policies is monitored", version = "v1.0",
+                    title = "Policy Administration"),
+    consumes = {MediaType.APPLICATION_JSON}, produces = {MediaType.APPLICATION_JSON},
+    schemes = {SwaggerDefinition.Scheme.HTTP, SwaggerDefinition.Scheme.HTTPS},
+    tags = {@Tag(name = "policy-administration", description = "Policy Administration Service Operations")},
+    securityDefinition = @SecurityDefinition(
+                    basicAuthDefinitions = {@BasicAuthDefinition(key = "basicAuth")}))
+public class PapRestControllerV1 {
+
+    public static final String AUTHORIZATION_TYPE = "basicAuth";
+
+    public static final int AUTHENTICATION_ERROR_CODE = HttpURLConnection.HTTP_UNAUTHORIZED;
+    public static final int AUTHORIZATION_ERROR_CODE = HttpURLConnection.HTTP_FORBIDDEN;
+    public static final int SERVER_ERROR_CODE = HttpURLConnection.HTTP_INTERNAL_ERROR;
+
+    public static final String AUTHENTICATION_ERROR_MESSAGE = "Authentication Error";
+    public static final String AUTHORIZATION_ERROR_MESSAGE = "Authorization Error";
+    public static final String SERVER_ERROR_MESSAGE = "Internal Server Error";
+}
diff --git a/main/src/main/java/org/onap/policy/pap/main/rest/PapRestServer.java b/main/src/main/java/org/onap/policy/pap/main/rest/PapRestServer.java
index c6a5e30..6064a14 100644
--- a/main/src/main/java/org/onap/policy/pap/main/rest/PapRestServer.java
+++ b/main/src/main/java/org/onap/policy/pap/main/rest/PapRestServer.java
@@ -91,7 +91,8 @@
         props.setProperty(svcpfx + PolicyEndPointProperties.PROPERTY_HTTP_PORT_SUFFIX,
                         Integer.toString(restServerParameters.getPort()));
         props.setProperty(svcpfx + PolicyEndPointProperties.PROPERTY_HTTP_REST_CLASSES_SUFFIX,
-                        PapRestController.class.getCanonicalName());
+                        String.join(",", HealthCheckRestControllerV1.class.getCanonicalName(),
+                                        StatisticsRestControllerV1.class.getCanonicalName()));
         props.setProperty(svcpfx + PolicyEndPointProperties.PROPERTY_MANAGED_SUFFIX, "false");
         props.setProperty(svcpfx + PolicyEndPointProperties.PROPERTY_HTTP_SWAGGER_SUFFIX, "true");
         props.setProperty(svcpfx + PolicyEndPointProperties.PROPERTY_HTTP_AUTH_USERNAME_SUFFIX,
diff --git a/main/src/main/java/org/onap/policy/pap/main/rest/StatisticsRestControllerV1.java b/main/src/main/java/org/onap/policy/pap/main/rest/StatisticsRestControllerV1.java
new file mode 100644
index 0000000..e864586
--- /dev/null
+++ b/main/src/main/java/org/onap/policy/pap/main/rest/StatisticsRestControllerV1.java
@@ -0,0 +1,50 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2019 Nordix Foundation.
+ *  Modifications Copyright (C) 2019 AT&T Intellectual Property.
+ * ================================================================================
+ * 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.pap.main.rest;
+
+import io.swagger.annotations.ApiOperation;
+import io.swagger.annotations.ApiResponse;
+import io.swagger.annotations.ApiResponses;
+import io.swagger.annotations.Authorization;
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.core.Response;
+
+/**
+ * Class to provide REST endpoints for PAP component statistics.
+ *
+ * @author Ram Krishna Verma (ram.krishna.verma@est.tech)
+ */
+public class StatisticsRestControllerV1 extends PapRestControllerV1 {
+
+    @GET
+    @Path("statistics")
+    @ApiOperation(value = "Fetch current statistics",
+                    notes = "Returns current statistics of the Policy Administration component",
+                    response = StatisticsReport.class, authorizations = @Authorization(value = AUTHORIZATION_TYPE))
+    @ApiResponses(value = {@ApiResponse(code = AUTHENTICATION_ERROR_CODE, message = AUTHENTICATION_ERROR_MESSAGE),
+                    @ApiResponse(code = AUTHORIZATION_ERROR_CODE, message = AUTHORIZATION_ERROR_MESSAGE),
+                    @ApiResponse(code = SERVER_ERROR_CODE, message = SERVER_ERROR_MESSAGE)})
+    public Response statistics() {
+        return Response.status(Response.Status.OK).entity(new StatisticsProvider().fetchCurrentStatistics()).build();
+    }
+}
diff --git a/main/src/test/java/org/onap/policy/pap/main/comm/MultiPdpStatusListenerTest.java b/main/src/test/java/org/onap/policy/pap/main/comm/MultiPdpStatusListenerTest.java
index 2dc1aa2..d0dba80 100644
--- a/main/src/test/java/org/onap/policy/pap/main/comm/MultiPdpStatusListenerTest.java
+++ b/main/src/test/java/org/onap/policy/pap/main/comm/MultiPdpStatusListenerTest.java
@@ -32,78 +32,79 @@
 import org.junit.Test;
 import org.onap.policy.common.endpoints.event.comm.Topic.CommInfrastructure;
 import org.onap.policy.pap.main.comm.MultiPdpStatusListener;
+import org.onap.policy.pdp.common.models.PdpResponseDetails;
 import org.onap.policy.pdp.common.models.PdpStatus;
 
 public class MultiPdpStatusListenerTest {
     private static final CommInfrastructure INFRA = CommInfrastructure.NOOP;
     private static final String TOPIC = "my-topic";
-    private static final String NAME1 = "pdp_1";
-    private static final String NAME2 = "pdp_2";
-    private static final List<String> NAME_LIST = Arrays.asList(NAME1, NAME2);
+    private static final String ID1 = "request-1";
+    private static final String ID2 = "request-2";
+    private static final List<String> ID_LIST = Arrays.asList(ID1, ID2);
 
     private MultiPdpStatusListener listener;
     private PdpStatus status;
 
     @Test
     public void testMultiPdpStatusListenerString() throws Exception {
-        listener = new MultiPdpStatusListener(NAME1);
-        assertEquals(Arrays.asList(NAME1).toString(), listener.getUnseenPdpNames().toString());
+        listener = new MyListener(ID1);
+        assertEquals(Arrays.asList(ID1).toString(), listener.getUnseenIds().toString());
 
-        // a name is in the queue - not done yet
+        // an ID is in the queue - not done yet
         assertFalse(doWait(0));
     }
 
     @Test
     public void testMultiPdpStatusListenerCollectionOfString() throws Exception {
-        List<String> lst = NAME_LIST;
+        List<String> lst = ID_LIST;
 
-        listener = new MultiPdpStatusListener(lst);
-        assertEquals(lst.toString(), listener.getUnseenPdpNames().toString());
+        listener = new MyListener(lst);
+        assertEquals(lst.toString(), listener.getUnseenIds().toString());
 
-        // a name is in the queue - not done yet
+        // an ID is in the queue - not done yet
         assertFalse(doWait(0));
 
         /*
          * Try with an empty list - should already be complete.
          */
-        listener = new MultiPdpStatusListener(new LinkedList<>());
-        assertTrue(listener.getUnseenPdpNames().isEmpty());
+        listener = new MyListener(new LinkedList<>());
+        assertTrue(listener.getUnseenIds().isEmpty());
         assertTrue(doWait(0));
     }
 
     @Test
-    public void testGetUnseenPdpNames() {
-        List<String> lst = NAME_LIST;
+    public void testGetUnseenIds() {
+        List<String> lst = ID_LIST;
 
-        listener = new MultiPdpStatusListener(lst);
-        assertEquals(lst.toString(), listener.getUnseenPdpNames().toString());
+        listener = new MyListener(lst);
+        assertEquals(lst.toString(), listener.getUnseenIds().toString());
 
         // receive message from one PDP
         status = new PdpStatus();
-        status.setName(NAME2);
+        status.setResponse(makeResponse(ID2));
         listener.onTopicEvent(INFRA, TOPIC, status);
-        assertEquals(Arrays.asList(NAME1).toString(), listener.getUnseenPdpNames().toString());
+        assertEquals(Arrays.asList(ID1).toString(), listener.getUnseenIds().toString());
 
         // receive message from the other PDP
         status = new PdpStatus();
-        status.setName(NAME1);
+        status.setResponse(makeResponse(ID1));
         listener.onTopicEvent(INFRA, TOPIC, status);
-        assertTrue(listener.getUnseenPdpNames().isEmpty());
+        assertTrue(listener.getUnseenIds().isEmpty());
     }
 
     @Test
     public void testAwait() throws Exception {
         // try with an empty list - should already be complete
-        listener = new MultiPdpStatusListener(new LinkedList<>());
+        listener = new MyListener(new LinkedList<>());
         assertTrue(doWait(0));
 
         // try it with something in the list
-        listener = new MultiPdpStatusListener(NAME_LIST);
+        listener = new MyListener(ID_LIST);
         assertFalse(doWait(0));
 
         // process a message from one PDP - wait should block the entire time
         status = new PdpStatus();
-        status.setName(NAME1);
+        status.setResponse(makeResponse(ID1));
         listener.onTopicEvent(INFRA, TOPIC, status);
         long tbeg = System.currentTimeMillis();
         assertFalse(doWait(50));
@@ -111,7 +112,7 @@
 
         // process a message from the other PDP - wait should NOT block
         status = new PdpStatus();
-        status.setName(NAME2);
+        status.setResponse(makeResponse(ID2));
         listener.onTopicEvent(INFRA, TOPIC, status);
         tbeg = System.currentTimeMillis();
         assertTrue(doWait(4000));
@@ -120,28 +121,52 @@
 
     @Test
     public void testOnTopicEvent() throws Exception {
-        listener = new MultiPdpStatusListener(NAME_LIST);
+        listener = new MyListener(ID_LIST);
 
         // not done yet
         assertFalse(doWait(0));
 
-        // process a message - still not done as have another name to go
+        // process a message - still not done as have another ID to go
         status = new PdpStatus();
-        status.setName(NAME1);
+        status.setResponse(makeResponse(ID1));
         listener.onTopicEvent(INFRA, TOPIC, status);
         assertFalse(doWait(0));
 
         // process a message from the same PDP - still not done
         status = new PdpStatus();
-        status.setName(NAME1);
+        status.setResponse(makeResponse(ID1));
         listener.onTopicEvent(INFRA, TOPIC, status);
         assertFalse(doWait(0));
 
         // process another message - now we're done
         status = new PdpStatus();
-        status.setName(NAME2);
+        status.setResponse(makeResponse(ID2));
         listener.onTopicEvent(INFRA, TOPIC, status);
         assertTrue(doWait(0));
+
+        // handleEvent throws an exception - doWait does not return true
+        listener = new MyListener(ID1) {
+            @Override
+            protected String handleEvent(CommInfrastructure infra, String topic, PdpStatus message) {
+                throw new RuntimeException("expected exception");
+            }
+        };
+        status = new PdpStatus();
+        status.setResponse(makeResponse(ID1));
+        listener.onTopicEvent(INFRA, TOPIC, status);
+        assertFalse(doWait(0));
+
+        // handleEvent returns null - doWait does not return true
+        listener = new MyListener(ID1) {
+            @Override
+            protected String handleEvent(CommInfrastructure infra, String topic, PdpStatus message) {
+                return null;
+            }
+        };
+        status = new PdpStatus();
+        status.setResponse(makeResponse(ID1));
+        listener.onTopicEvent(INFRA, TOPIC, status);
+        assertFalse(doWait(0));
     }
 
     /**
@@ -174,4 +199,33 @@
 
         return done.get();
     }
+
+    /**
+     * Makes a response for the given request ID.
+     *
+     * @param id ID of the request
+     * @return a new response
+     */
+    private PdpResponseDetails makeResponse(String id) {
+        PdpResponseDetails resp = new PdpResponseDetails();
+        resp.setResponseTo(id);
+
+        return resp;
+    }
+
+    private static class MyListener extends MultiPdpStatusListener {
+
+        public MyListener(String id) {
+            super(id);
+        }
+
+        public MyListener(List<String> lst) {
+            super(lst);
+        }
+
+        @Override
+        protected String handleEvent(CommInfrastructure infra, String topic, PdpStatus message) {
+            return (message.getResponse().getResponseTo());
+        }
+    }
 }