Bug 2517: Catch RuntimeExceptions thrown from the DCL in DataChangeListener
[controller.git] / opendaylight / md-sal / sal-distributed-datastore / src / main / java / org / opendaylight / controller / cluster / datastore / DataChangeListener.java
index b435eda7a381560320825d44458580e3c165a403..6f14af304f403e8340ea904bbbf22a0a1d40673d 100644 (file)
@@ -10,6 +10,8 @@ package org.opendaylight.controller.cluster.datastore;
 
 import akka.actor.Props;
 import akka.japi.Creator;
+import com.google.common.base.Preconditions;
+import org.opendaylight.controller.cluster.common.actor.AbstractUntypedActor;
 import org.opendaylight.controller.cluster.datastore.messages.DataChanged;
 import org.opendaylight.controller.cluster.datastore.messages.DataChangedReply;
 import org.opendaylight.controller.cluster.datastore.messages.EnableNotification;
@@ -17,23 +19,23 @@ import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeEvent;
 import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeListener;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
-import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 public class DataChangeListener extends AbstractUntypedActor {
+    private static final Logger LOG = LoggerFactory.getLogger(DataChangeListener.class);
+
     private final AsyncDataChangeListener<YangInstanceIdentifier, NormalizedNode<?, ?>> listener;
-    private final SchemaContext schemaContext;
-    private final YangInstanceIdentifier pathId;
     private boolean notificationsEnabled = false;
 
-    public DataChangeListener(SchemaContext schemaContext,
-                              AsyncDataChangeListener<YangInstanceIdentifier, NormalizedNode<?, ?>> listener, YangInstanceIdentifier pathId) {
-        this.listener = listener;
-        this.schemaContext = schemaContext;
-        this.pathId  = pathId;
+    public DataChangeListener(AsyncDataChangeListener<YangInstanceIdentifier,
+                                                      NormalizedNode<?, ?>> listener) {
+        this.listener = Preconditions.checkNotNull(listener, "listener should not be null");
     }
 
-    @Override public void handleReceive(Object message) throws Exception {
-        if(message.getClass().equals(DataChanged.SERIALIZABLE_CLASS)){
+    @Override
+    public void handleReceive(Object message) throws Exception {
+        if(message instanceof DataChanged){
             dataChanged(message);
         } else if(message instanceof EnableNotification){
             enableNotification((EnableNotification) message);
@@ -42,33 +44,55 @@ public class DataChangeListener extends AbstractUntypedActor {
 
     private void enableNotification(EnableNotification message) {
         notificationsEnabled = message.isEnabled();
+        LOG.debug("{} notifications for listener {}", (notificationsEnabled ? "Enabled" : "Disabled"),
+                listener);
     }
 
-    public void dataChanged(Object message) {
+    private void dataChanged(Object message) {
 
         // Do nothing if notifications are not enabled
-        if(!notificationsEnabled){
+        if(!notificationsEnabled) {
+            LOG.debug("Notifications not enabled for listener {} - dropping change notification",
+                    listener);
             return;
         }
 
-        DataChanged reply = DataChanged.fromSerialize(schemaContext,message, pathId);
-        AsyncDataChangeEvent<YangInstanceIdentifier, NormalizedNode<?, ?>>
-            change = reply.getChange();
-        this.listener.onDataChanged(change);
+        DataChanged reply = (DataChanged) message;
+        AsyncDataChangeEvent<YangInstanceIdentifier, NormalizedNode<?, ?>> change = reply.getChange();
+
+        LOG.debug("Sending change notification {} to listener {}", change, listener);
 
-        if(getSender() != null){
-            getSender().tell(new DataChangedReply().toSerializable(), getSelf());
+        try {
+            this.listener.onDataChanged(change);
+        } catch (RuntimeException e) {
+            LOG.error( String.format( "Error notifying listener %s", this.listener ), e );
+        }
+
+        // It seems the sender is never null but it doesn't hurt to check. If the caller passes in
+        // a null sender (ActorRef.noSender()), akka translates that to the deadLetters actor.
+        if(getSender() != null && !getContext().system().deadLetters().equals(getSender())) {
+            getSender().tell(new DataChangedReply(), getSelf());
         }
     }
 
-    public static Props props(final SchemaContext schemaContext, final AsyncDataChangeListener<YangInstanceIdentifier, NormalizedNode<?, ?>> listener, final YangInstanceIdentifier pathId) {
-        return Props.create(new Creator<DataChangeListener>() {
-            @Override
-            public DataChangeListener create() throws Exception {
-                return new DataChangeListener(schemaContext,listener,pathId );
-            }
+    public static Props props(final AsyncDataChangeListener<YangInstanceIdentifier,
+                                                            NormalizedNode<?, ?>> listener) {
+        return Props.create(new DataChangeListenerCreator(listener));
+    }
 
-        });
+    private static class DataChangeListenerCreator implements Creator<DataChangeListener> {
+        private static final long serialVersionUID = 1L;
 
+        final AsyncDataChangeListener<YangInstanceIdentifier, NormalizedNode<?, ?>> listener;
+
+        DataChangeListenerCreator(
+                AsyncDataChangeListener<YangInstanceIdentifier, NormalizedNode<?, ?>> listener) {
+            this.listener = listener;
+        }
+
+        @Override
+        public DataChangeListener create() throws Exception {
+            return new DataChangeListener(listener);
+        }
     }
 }