Merge "Allowed Jolokia to use HttpService"
authorMoiz Raja <moraja@cisco.com>
Tue, 9 Sep 2014 12:10:04 +0000 (12:10 +0000)
committerGerrit Code Review <gerrit@opendaylight.org>
Tue, 9 Sep 2014 12:10:04 +0000 (12:10 +0000)
opendaylight/md-sal/sal-inmemory-datastore/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/ResolveDataChangeState.java
opendaylight/netconf/config-persister-impl/src/main/java/org/opendaylight/controller/netconf/persist/impl/ConfigPersisterNotificationHandler.java
opendaylight/netconf/config-persister-impl/src/main/java/org/opendaylight/controller/netconf/persist/impl/PersisterAggregator.java
opendaylight/netconf/config-persister-impl/src/test/java/org/opendaylight/controller/netconf/persist/impl/ConfigPersisterNotificationHandlerTest.java [new file with mode: 0644]
opendaylight/netconf/config-persister-impl/src/test/java/org/opendaylight/controller/netconf/persist/impl/ConfigPersisterNotificationListenerTest.java [new file with mode: 0644]
opendaylight/netconf/config-persister-impl/src/test/java/org/opendaylight/controller/netconf/persist/impl/PersisterAggregatorTest.java

index d3c5a7cb70e8013734f64619b456e74ed18ee0e2..3db4115af67908bed3c4edfcfbb2d91cdae3baae 100644 (file)
@@ -47,16 +47,16 @@ final class ResolveDataChangeState {
     /**
      * Inherited from immediate parent
      */
-    private final Iterable<Builder> inheritedOne;
+    private final Collection<Builder> inheritedOne;
     private final YangInstanceIdentifier nodeId;
     private final Collection<Node> nodes;
 
-    private final Map<DataChangeListenerRegistration<?>, Builder> subBuilders = new HashMap<>();
-    private final Map<DataChangeListenerRegistration<?>, Builder> oneBuilders = new HashMap<>();
-    private final Map<DataChangeListenerRegistration<?>, Builder> baseBuilders = new HashMap<>();
+    private final Map<DataChangeListenerRegistration<?>, Builder> subBuilders;
+    private final Map<DataChangeListenerRegistration<?>, Builder> oneBuilders;
+    private final Map<DataChangeListenerRegistration<?>, Builder> baseBuilders;
 
     private ResolveDataChangeState(final YangInstanceIdentifier nodeId,
-            final Iterable<Builder> inheritedSub, final Iterable<Builder> inheritedOne,
+            final Iterable<Builder> inheritedSub, final Collection<Builder> inheritedOne,
             final Collection<Node> nodes) {
         this.nodeId = Preconditions.checkNotNull(nodeId);
         this.nodes = Preconditions.checkNotNull(nodes);
@@ -66,22 +66,36 @@ final class ResolveDataChangeState {
         /*
          * Collect the nodes which need to be propagated from us to the child.
          */
+        final Map<DataChangeListenerRegistration<?>, Builder> sub = new HashMap<>();
+        final Map<DataChangeListenerRegistration<?>, Builder> one = new HashMap<>();
+        final Map<DataChangeListenerRegistration<?>, Builder> base = new HashMap<>();
         for (Node n : nodes) {
             for (DataChangeListenerRegistration<?> l : n.getListeners()) {
                 final Builder b = DOMImmutableDataChangeEvent.builder(DataChangeScope.BASE);
                 switch (l.getScope()) {
                 case BASE:
-                    baseBuilders.put(l, b);
+                    base.put(l, b);
                     break;
                 case ONE:
-                    oneBuilders.put(l, b);
+                    one.put(l, b);
                     break;
                 case SUBTREE:
-                    subBuilders.put(l, b);
+                    sub.put(l, b);
                     break;
                 }
             }
         }
+
+        baseBuilders = maybeEmpty(base);
+        oneBuilders = maybeEmpty(one);
+        subBuilders = maybeEmpty(sub);
+    }
+
+    private static <K, V> Map<K, V> maybeEmpty(final Map<K, V> map) {
+        if (map.isEmpty()) {
+            return Collections.emptyMap();
+        }
+        return map;
     }
 
     /**
@@ -103,8 +117,38 @@ final class ResolveDataChangeState {
      * @return State handle
      */
     public ResolveDataChangeState child(final PathArgument childId) {
-        return new ResolveDataChangeState(nodeId.node(childId),
-            Iterables.concat(inheritedSub, subBuilders.values()),
+        /*
+         * We instantiate a concatenation only when needed:
+         *
+         * 1) If our collection is empty, we reuse the parent's. This is typically the case
+         *    for intermediate node, which should be the vast majority.
+         * 2) If the parent's iterable is a Collection and it is empty, reuse our collection.
+         *    This is the case for the first node which defines a subtree listener in a
+         *    particular subtree.
+         * 3) Concatenate the two collections. This happens when we already have some
+         *    subtree listeners and we encounter a node which adds a few more.
+         *
+         * This allows us to lower number of objects allocated and also
+         * speeds up Iterables.isEmpty() in needsProcessing().
+         *
+         * Note that the check for Collection in 2) relies on precisely this logic, which
+         * ensures that we simply cannot see an empty concatenation, but rather start off with
+         * an empty collection, then switch to a non-empty collection and finally switch to
+         * a concatenation. This saves us from instantiating iterators, which a trivial
+         * Iterables.isEmpty() would do as soon as we cross case 3).
+         */
+        final Iterable<Builder> sb;
+        if (!subBuilders.isEmpty()) {
+            if (inheritedSub instanceof Collection && ((Collection<?>) inheritedSub).isEmpty()) {
+                sb = subBuilders.values();
+            } else {
+                sb = Iterables.concat(inheritedSub, subBuilders.values());
+            }
+        } else {
+            sb = inheritedSub;
+        }
+
+        return new ResolveDataChangeState(nodeId.node(childId), sb,
             oneBuilders.values(), getListenerChildrenWildcarded(nodes, childId));
     }
 
@@ -127,16 +171,29 @@ final class ResolveDataChangeState {
         if (!nodes.isEmpty()) {
             return true;
         }
-        // Have SUBTREE listeners
-        if (!Iterables.isEmpty(inheritedSub)) {
-            return true;
-        }
         // Have ONE listeners
-        if (!Iterables.isEmpty(inheritedOne)) {
+        if (!inheritedOne.isEmpty()) {
             return true;
         }
 
-        return false;
+        /*
+         * Have SUBTREE listeners
+         *
+         * This is slightly magical replacement for !Iterables.isEmpty(inheritedSub).
+         * It relies on the logic in child(), which gives us the guarantee that when
+         * inheritedSub is not a Collection, it is guaranteed to be non-empty (which
+         * means we need to process). If it is a collection, we still need to check
+         * it for emptiness.
+         *
+         * Unlike Iterables.isEmpty() this code does not instantiate any temporary
+         * objects and is thus more efficient.
+         */
+        if (inheritedSub instanceof Collection) {
+            return !((Collection<?>) inheritedSub).isEmpty();
+        }
+
+        // Non-Collection => non-empty => have to process
+        return true;
     }
 
     /**
index 40a15be70616217d81529b003f899236865d8ac8..d72c26cb775dd76f34645d1d5bc533eb4bc082fe 100644 (file)
@@ -34,18 +34,20 @@ public class ConfigPersisterNotificationHandler implements Closeable {
 
     private static final Logger logger = LoggerFactory.getLogger(ConfigPersisterNotificationHandler.class);
     private final MBeanServerConnection mBeanServerConnection;
-    private final ConfigPersisterNotificationListener listener;
+    private final NotificationListener listener;
 
 
-    public ConfigPersisterNotificationHandler(MBeanServerConnection mBeanServerConnection,
-                                              Persister persisterAggregator) {
+    public ConfigPersisterNotificationHandler(final MBeanServerConnection mBeanServerConnection, final Persister persisterAggregator) {
+        this(mBeanServerConnection, new ConfigPersisterNotificationListener(persisterAggregator));
+    }
+
+    public ConfigPersisterNotificationHandler(final MBeanServerConnection mBeanServerConnection, final NotificationListener notificationListener) {
         this.mBeanServerConnection = mBeanServerConnection;
-        listener = new ConfigPersisterNotificationListener(persisterAggregator);
+        this.listener = notificationListener;
         registerAsJMXListener(mBeanServerConnection, listener);
-
     }
 
-    private static void registerAsJMXListener(MBeanServerConnection mBeanServerConnection, ConfigPersisterNotificationListener listener) {
+    private static void registerAsJMXListener(final MBeanServerConnection mBeanServerConnection, final NotificationListener listener) {
         logger.trace("Called registerAsJMXListener");
         try {
             mBeanServerConnection.addNotificationListener(DefaultCommitOperationMXBean.OBJECT_NAME, listener, null, null);
@@ -57,12 +59,12 @@ public class ConfigPersisterNotificationHandler implements Closeable {
     @Override
     public synchronized void close() {
         // unregister from JMX
-        ObjectName on = DefaultCommitOperationMXBean.OBJECT_NAME;
+        final ObjectName on = DefaultCommitOperationMXBean.OBJECT_NAME;
         try {
             if (mBeanServerConnection.isRegistered(on)) {
                 mBeanServerConnection.removeNotificationListener(on, listener);
             }
-        } catch (Exception e) {
+        } catch (final Exception e) {
             logger.warn("Unable to unregister {} as listener for {}", listener, on, e);
         }
     }
@@ -73,12 +75,12 @@ class ConfigPersisterNotificationListener implements NotificationListener {
 
     private final Persister persisterAggregator;
 
-    ConfigPersisterNotificationListener(Persister persisterAggregator) {
+    ConfigPersisterNotificationListener(final Persister persisterAggregator) {
         this.persisterAggregator = persisterAggregator;
     }
 
     @Override
-    public void handleNotification(Notification notification, Object handback) {
+    public void handleNotification(final Notification notification, final Object handback) {
         if (!(notification instanceof NetconfJMXNotification))
             return;
 
@@ -89,7 +91,7 @@ class ConfigPersisterNotificationListener implements NotificationListener {
         if (notification instanceof CommitJMXNotification) {
             try {
                 handleAfterCommitNotification((CommitJMXNotification) notification);
-            } catch (Exception e) {
+            } catch (final Exception e) {
                 // log exceptions from notification Handler here since
                 // notificationBroadcastSupport logs only DEBUG level
                 logger.warn("Failed to handle notification {}", notification, e);
@@ -105,7 +107,7 @@ class ConfigPersisterNotificationListener implements NotificationListener {
             persisterAggregator.persistConfig(new CapabilityStrippingConfigSnapshotHolder(notification.getConfigSnapshot(),
                     notification.getCapabilities()));
             logger.trace("Configuration persisted successfully");
-        } catch (IOException e) {
+        } catch (final IOException e) {
             throw new RuntimeException("Unable to persist configuration snapshot", e);
         }
     }
index a0e7974b942951c521305ae4726baa4a5a74d74a..7e68ac18757e194d3c4b405f69c6a7f2453c9044 100644 (file)
@@ -147,8 +147,8 @@ public final class PersisterAggregator implements Persister {
     public void persistConfig(ConfigSnapshotHolder holder) throws IOException {
         for (PersisterWithConfiguration persisterWithConfiguration: persisterWithConfigurations){
             if (!persisterWithConfiguration.readOnly){
-                logger.debug("Calling {}.persistConfig",persisterWithConfiguration.storage);
-                persisterWithConfiguration.storage.persistConfig(holder);
+                logger.debug("Calling {}.persistConfig", persisterWithConfiguration.getStorage());
+                persisterWithConfiguration.getStorage().persistConfig(holder);
             }
         }
     }
diff --git a/opendaylight/netconf/config-persister-impl/src/test/java/org/opendaylight/controller/netconf/persist/impl/ConfigPersisterNotificationHandlerTest.java b/opendaylight/netconf/config-persister-impl/src/test/java/org/opendaylight/controller/netconf/persist/impl/ConfigPersisterNotificationHandlerTest.java
new file mode 100644 (file)
index 0000000..f16083e
--- /dev/null
@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.netconf.persist.impl;
+
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyObject;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import javax.management.MBeanServerConnection;
+
+import javax.management.NotificationFilter;
+import javax.management.NotificationListener;
+import javax.management.ObjectName;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.opendaylight.controller.config.persist.api.Persister;
+
+public class ConfigPersisterNotificationHandlerTest {
+
+    @Mock
+    private MBeanServerConnection mBeanServer;
+    @Mock
+    private Persister notificationListener;
+
+    @Before
+    public void setUp() throws Exception {
+        MockitoAnnotations.initMocks(this);
+        doNothing().when(mBeanServer).addNotificationListener(any(ObjectName.class), any(NotificationListener.class),
+                any(NotificationFilter.class), anyObject());
+    }
+
+    @Test
+    public void testNotificationHandler() throws Exception {
+        doReturn(true).when(mBeanServer).isRegistered(any(ObjectName.class));
+        doThrow(Exception.class).when(mBeanServer).removeNotificationListener(any(ObjectName.class), any(NotificationListener.class));
+
+        final ConfigPersisterNotificationHandler testedHandler = new ConfigPersisterNotificationHandler(mBeanServer, notificationListener);
+        verify(mBeanServer).addNotificationListener(any(ObjectName.class), any(NotificationListener.class),
+                any(NotificationFilter.class), anyObject());
+
+        testedHandler.close();
+        verify(mBeanServer).removeNotificationListener(any(ObjectName.class), any(NotificationListener.class));
+    }
+
+    @Test
+    public void testNotificationHandlerCloseNotRegistered() throws Exception {
+        doReturn(false).when(mBeanServer).isRegistered(any(ObjectName.class));
+
+        final ConfigPersisterNotificationHandler testedHandler = new ConfigPersisterNotificationHandler(mBeanServer, notificationListener);
+
+        testedHandler.close();
+        verify(mBeanServer, times(0)).removeNotificationListener(any(ObjectName.class), any(NotificationListener.class));
+    }
+}
diff --git a/opendaylight/netconf/config-persister-impl/src/test/java/org/opendaylight/controller/netconf/persist/impl/ConfigPersisterNotificationListenerTest.java b/opendaylight/netconf/config-persister-impl/src/test/java/org/opendaylight/controller/netconf/persist/impl/ConfigPersisterNotificationListenerTest.java
new file mode 100644 (file)
index 0000000..bf031f1
--- /dev/null
@@ -0,0 +1,84 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.netconf.persist.impl;
+
+import java.util.Collections;
+
+import javax.management.Notification;
+
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Matchers;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+import org.opendaylight.controller.config.persist.api.ConfigSnapshotHolder;
+import org.opendaylight.controller.config.persist.api.Persister;
+import org.opendaylight.controller.netconf.api.jmx.CommitJMXNotification;
+import org.opendaylight.controller.netconf.api.jmx.NetconfJMXNotification;
+import org.opendaylight.controller.netconf.util.xml.XmlUtil;
+
+import com.google.common.collect.Lists;
+
+public class ConfigPersisterNotificationListenerTest {
+
+    @Mock
+    private Persister mockPersister;
+    private PersisterAggregator persisterAggregator;
+
+    @Mock
+    private NetconfJMXNotification unknownNetconfNotif;
+    @Mock
+    private CommitJMXNotification commitNetconfNotif;
+    @Mock
+    private Notification unknownNotif;
+
+    @Before
+    public void setUp() throws Exception {
+        MockitoAnnotations.initMocks(this);
+
+        Mockito.doNothing().when(mockPersister).persistConfig(Matchers.any(ConfigSnapshotHolder.class));
+        Mockito.doReturn("persister").when(mockPersister).toString();
+        final PersisterAggregator.PersisterWithConfiguration withCfg = new PersisterAggregator.PersisterWithConfiguration(mockPersister, false);
+        persisterAggregator = new PersisterAggregator(Lists.newArrayList(withCfg));
+
+        Mockito.doReturn("netconfUnknownNotification").when(unknownNetconfNotif).toString();
+        Mockito.doReturn("netconfCommitNotification").when(commitNetconfNotif).toString();
+
+        Mockito.doReturn(XmlUtil.readXmlToElement("<config-snapshot/>")).when(commitNetconfNotif).getConfigSnapshot();
+        Mockito.doReturn(Collections.emptySet()).when(commitNetconfNotif).getCapabilities();
+
+    }
+
+    @Test
+    public void testNotificationListenerUnknownNotification() throws Exception {
+        final ConfigPersisterNotificationListener testeListener = new ConfigPersisterNotificationListener(persisterAggregator);
+        testeListener.handleNotification(unknownNotif, null);
+        Mockito.verifyZeroInteractions(mockPersister);
+    }
+
+    @Test
+    public void testNotificationListenerUnknownNetconfNotification() throws Exception {
+        final ConfigPersisterNotificationListener testeListener = new ConfigPersisterNotificationListener(persisterAggregator);
+        try {
+            testeListener.handleNotification(unknownNetconfNotif, null);
+            Assert.fail("Unknown netconf notification should fail");
+        } catch (final IllegalStateException e) {
+            Mockito.verifyZeroInteractions(mockPersister);
+        }
+    }
+
+    @Test
+    public void testNotificationListenerCommitNetconfNotification() throws Exception {
+        final ConfigPersisterNotificationListener testeListener = new ConfigPersisterNotificationListener(persisterAggregator);
+        testeListener.handleNotification(commitNetconfNotif, null);
+        Mockito.verify(mockPersister).persistConfig(Matchers.any(ConfigSnapshotHolder.class));
+    }
+}
index acea75a7432d37dbede7c219ed961784b1f63b86..cd646aeb079100a72a230f9d62624504a72a5ad2 100644 (file)
@@ -8,6 +8,8 @@
 
 package org.opendaylight.controller.netconf.persist.impl;
 
+import com.google.common.collect.Lists;
+
 import org.junit.Test;
 import org.opendaylight.controller.config.persist.api.ConfigSnapshotHolder;
 import org.opendaylight.controller.config.persist.api.Persister;
@@ -87,6 +89,22 @@ public class PersisterAggregatorTest {
         assertEquals(1, DummyAdapter.props);
     }
 
+    @Test
+    public void testNoopAdapter() throws Exception {
+        final NoOpStorageAdapter noOpStorageAdapter = new NoOpStorageAdapter();
+        final PersisterAggregator persisterAggregator =
+                new PersisterAggregator(Lists.newArrayList(new PersisterWithConfiguration(noOpStorageAdapter, false)));
+
+        noOpStorageAdapter.instantiate(null);
+
+        persisterAggregator.persistConfig(null);
+        persisterAggregator.loadLastConfigs();
+        persisterAggregator.persistConfig(null);
+        persisterAggregator.loadLastConfigs();
+
+        noOpStorageAdapter.close();
+    }
+
     @Test
     public void testLoadFromPropertyFile() throws Exception {
         PersisterAggregator persisterAggregator = PersisterAggregator.createFromProperties(loadFile("test2.properties"));