<dependencyManagement>
<dependencies>
- <dependency>
- <groupId>org.opendaylight.mdsal</groupId>
- <artifactId>features-mdsal</artifactId>
- <classifier>features</classifier>
- <version>${project.version}</version>
- <type>xml</type>
- </dependency>
-
- <!-- Common APIs for Binding and DOM -->
- <dependency>
- <groupId>org.opendaylight.mdsal</groupId>
- <artifactId>mdsal-common-api</artifactId>
- <version>${project.version}</version>
- </dependency>
-
- <dependency>
- <groupId>org.opendaylight.mdsal</groupId>
- <artifactId>mdsal-dom-api</artifactId>
- <version>${project.version}</version>
- </dependency>
- <dependency>
- <groupId>org.opendaylight.mdsal</groupId>
- <artifactId>mdsal-dom-spi</artifactId>
- <version>${project.version}</version>
- </dependency>
- <dependency>
- <groupId>org.opendaylight.mdsal</groupId>
- <artifactId>mdsal-dom-broker</artifactId>
- <version>${project.version}</version>
- </dependency>
- <dependency>
- <groupId>org.opendaylight.mdsal</groupId>
- <artifactId>mdsal-dom-inmemory-datastore</artifactId>
- <version>${project.version}</version>
- </dependency>
+ <dependency>
+ <groupId>org.opendaylight.mdsal</groupId>
+ <artifactId>features-mdsal</artifactId>
+ <classifier>features</classifier>
+ <version>${project.version}</version>
+ <type>xml</type>
+ </dependency>
+
+ <!-- Common APIs for Binding and DOM -->
+ <dependency>
+ <groupId>org.opendaylight.mdsal</groupId>
+ <artifactId>mdsal-common-api</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+
+ <dependency>
+ <groupId>org.opendaylight.mdsal</groupId>
+ <artifactId>mdsal-dom-api</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.mdsal</groupId>
+ <artifactId>mdsal-dom-spi</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.mdsal</groupId>
+ <artifactId>mdsal-dom-broker</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.mdsal</groupId>
+ <artifactId>mdsal-dom-inmemory-datastore</artifactId>
+ <version>${project.version}</version>
+ </dependency>
<!-- Binding MD-SAL & Java Binding -->
<dependency>
<artifactId>mdsal-eos-dom-api</artifactId>
<version>${project.version}</version>
</dependency>
+ <dependency>
+ <groupId>org.opendaylight.mdsal</groupId>
+ <artifactId>mdsal-eos-dom-simple</artifactId>
+ <version>${project.version}</version>
+ </dependency>
<dependency>
<groupId>org.opendaylight.mdsal</groupId>
<artifactId>mdsal-eos-binding-api</artifactId>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (c) 2016 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
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <groupId>org.opendaylight.odlparent</groupId>
+ <artifactId>bundle-parent</artifactId>
+ <version>1.8.0-SNAPSHOT</version>
+ <relativePath/>
+ </parent>
+
+ <groupId>org.opendaylight.mdsal</groupId>
+ <artifactId>mdsal-eos-dom-simple</artifactId>
+ <version>2.2.0-SNAPSHOT</version>
+ <packaging>bundle</packaging>
+
+ <dependencyManagement>
+ <dependencies>
+ <dependency>
+ <groupId>org.opendaylight.mdsal</groupId>
+ <artifactId>mdsal-artifacts</artifactId>
+ <version>2.2.0-SNAPSHOT</version>
+ <type>pom</type>
+ <scope>import</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>yangtools-artifacts</artifactId>
+ <version>1.1.0-SNAPSHOT</version>
+ <type>pom</type>
+ <scope>import</scope>
+ </dependency>
+ </dependencies>
+ </dependencyManagement>
+
+ <dependencies>
+ <dependency>
+ <groupId>com.google.guava</groupId>
+ <artifactId>guava</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.mdsal</groupId>
+ <artifactId>mdsal-eos-common-api</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.mdsal</groupId>
+ <artifactId>mdsal-eos-common-spi</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.mdsal</groupId>
+ <artifactId>mdsal-eos-dom-api</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>yang-common</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>org.kohsuke.metainf-services</groupId>
+ <artifactId>metainf-services</artifactId>
+ <optional>true</optional>
+ </dependency>
+
+ <dependency>
+ <groupId>org.mockito</groupId>
+ <artifactId>mockito-core</artifactId>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-checkstyle-plugin</artifactId>
+ <configuration>
+ <propertyExpansion>checkstyle.violationSeverity=error</propertyExpansion>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+
+ <scm>
+ <connection>scm:git:http://git.opendaylight.org/gerrit/controller.git</connection>
+ <developerConnection>scm:git:ssh://git.opendaylight.org:29418/controller.git</developerConnection>
+ <tag>HEAD</tag>
+ <url>https://wiki.opendaylight.org/view/OpenDaylight_Controller:MD-SAL</url>
+ </scm>
+
+ <!--
+ Maven Site Configuration
+
+ The following configuration is necessary for maven-site-plugin to
+ correctly identify the correct deployment path for OpenDaylight Maven
+ sites.
+ -->
+ <url>${odl.site.url}/${project.groupId}/${stream}/${project.artifactId}/</url>
+
+ <distributionManagement>
+ <site>
+ <id>opendaylight-site</id>
+ <url>${nexus.site.url}/${project.artifactId}/</url>
+ </site>
+ </distributionManagement>
+
+</project>
--- /dev/null
+/*
+ * Copyright (c) 2017 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.mdsal.eos.dom.simple;
+
+import static org.opendaylight.mdsal.eos.common.api.EntityOwnershipChangeState.LOCAL_OWNERSHIP_GRANTED;
+import static org.opendaylight.mdsal.eos.common.api.EntityOwnershipChangeState.LOCAL_OWNERSHIP_LOST_NO_OWNER;
+
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.MoreObjects;
+import com.google.common.base.MoreObjects.ToStringHelper;
+import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
+import com.google.common.collect.ArrayListMultimap;
+import com.google.common.collect.HashBasedTable;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Multimap;
+import com.google.common.collect.Table;
+import java.util.Collection;
+import java.util.UUID;
+import javax.annotation.concurrent.GuardedBy;
+import org.kohsuke.MetaInfServices;
+import org.opendaylight.mdsal.eos.common.api.CandidateAlreadyRegisteredException;
+import org.opendaylight.mdsal.eos.common.api.EntityOwnershipChangeState;
+import org.opendaylight.mdsal.eos.common.api.EntityOwnershipState;
+import org.opendaylight.mdsal.eos.dom.api.DOMEntity;
+import org.opendaylight.mdsal.eos.dom.api.DOMEntityOwnershipCandidateRegistration;
+import org.opendaylight.mdsal.eos.dom.api.DOMEntityOwnershipChange;
+import org.opendaylight.mdsal.eos.dom.api.DOMEntityOwnershipListener;
+import org.opendaylight.mdsal.eos.dom.api.DOMEntityOwnershipListenerRegistration;
+import org.opendaylight.mdsal.eos.dom.api.DOMEntityOwnershipService;
+import org.opendaylight.yangtools.concepts.AbstractObjectRegistration;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Simple {@link DOMEntityOwnershipService} operating as an isolated island. It has no awareness of the world outside
+ * of itself.
+ *
+ * @author Robert Varga
+ */
+@MetaInfServices
+public final class SimpleDOMEntityOwnershipService implements DOMEntityOwnershipService {
+ private static final Logger LOG = LoggerFactory.getLogger(SimpleDOMEntityOwnershipService.class);
+
+ @GuardedBy("entities")
+ private final Table<String, YangInstanceIdentifier, DOMEntity> entities = HashBasedTable.create();
+
+ @GuardedBy("listeners")
+ private final Multimap<String, DOMEntityOwnershipListener> listeners = ArrayListMultimap.create(0, 1);
+
+ private final UUID uuid;
+
+ @VisibleForTesting
+ SimpleDOMEntityOwnershipService(final UUID uuid) {
+ this.uuid = Preconditions.checkNotNull(uuid);
+ }
+
+ public SimpleDOMEntityOwnershipService() {
+ this(UUID.randomUUID());
+ }
+
+ @Override
+ public DOMEntityOwnershipCandidateRegistration registerCandidate(final DOMEntity entity)
+ throws CandidateAlreadyRegisteredException {
+ synchronized (entities) {
+ final DOMEntity prev = entities.get(entity.getType(), entity.getIdentifier());
+ if (prev != null) {
+ throw new CandidateAlreadyRegisteredException(prev);
+ }
+
+ entities.put(entity.getType(), entity.getIdentifier(), entity);
+ LOG.debug("{}: registered candidate {}", uuid, entity);
+ }
+
+ notifyListeners(entity, LOCAL_OWNERSHIP_GRANTED);
+ return new EntityRegistration(entity);
+ }
+
+ @Override
+ public DOMEntityOwnershipListenerRegistration registerListener(final String entityType,
+ final DOMEntityOwnershipListener listener) {
+
+ final Collection<DOMEntity> owned;
+ synchronized (entities) {
+ owned = ImmutableList.copyOf(entities.row(entityType).values());
+ LOG.trace("{}: acquired candidates {} for new listener {}", uuid, owned, listener);
+ }
+
+ synchronized (listeners) {
+ listeners.put(entityType, listener);
+ }
+
+ for (DOMEntity entity : owned) {
+ notifyListener(listener, new DOMEntityOwnershipChange(entity, LOCAL_OWNERSHIP_GRANTED));
+ }
+ LOG.debug("{}: registered listener {}", uuid, listener);
+ return new ListenerRegistration(entityType, listener);
+ }
+
+ @Override
+ public Optional<EntityOwnershipState> getOwnershipState(final DOMEntity forEntity) {
+ return isCandidateRegistered(forEntity) ? Optional.of(EntityOwnershipState.IS_OWNER) : Optional.absent();
+ }
+
+ @Override
+ public boolean isCandidateRegistered(final DOMEntity forEntity) {
+ synchronized (entities) {
+ return entities.contains(forEntity.getType(), forEntity.getIdentifier());
+ }
+ }
+
+ private void removeEntity(final DOMEntity entity) {
+ synchronized (entities) {
+ entities.remove(entity.getType(), entity.getIdentifier());
+ LOG.debug("{}: unregistered candidate {}", uuid, entity);
+ }
+
+ notifyListeners(entity, LOCAL_OWNERSHIP_LOST_NO_OWNER);
+ }
+
+ @SuppressWarnings("checkstyle:illegalCatch")
+ private void notifyListener(final DOMEntityOwnershipListener listener, final DOMEntityOwnershipChange change) {
+ try {
+ LOG.trace("{} notifying listener {} change {}", uuid, listener, change);
+ listener.ownershipChanged(change);
+ } catch (RuntimeException e) {
+ LOG.warn("{}: Listener {} change {} failed", uuid, listener, change, e);
+ }
+ }
+
+ private void notifyListeners(final DOMEntity entity, final EntityOwnershipChangeState state) {
+ final DOMEntityOwnershipChange change = new DOMEntityOwnershipChange(entity, state);
+
+ final Collection<DOMEntityOwnershipListener> snap;
+
+ synchronized (listeners) {
+ snap = ImmutableList.copyOf(listeners.get(entity.getType()));
+ }
+
+ for (DOMEntityOwnershipListener listener : snap) {
+ notifyListener(listener, change);
+ }
+ }
+
+ void unregisterListener(final ListenerRegistration reg) {
+ synchronized (listeners) {
+ listeners.remove(reg.getEntityType(), reg.getInstance());
+ LOG.debug("{}: unregistered listener {}", uuid, reg.getInstance());
+ }
+ }
+
+ @Override
+ public String toString() {
+ final ToStringHelper h = MoreObjects.toStringHelper(SimpleDOMEntityOwnershipService.class).add("uuid", uuid);
+
+ synchronized (entities) {
+ h.add("entities", entities);
+ }
+ synchronized (listeners) {
+ h.add("listeners", listeners);
+ }
+
+ return h.toString();
+ }
+
+ private final class EntityRegistration extends AbstractObjectRegistration<DOMEntity> implements
+ DOMEntityOwnershipCandidateRegistration {
+ EntityRegistration(final DOMEntity entity) {
+ super(entity);
+ }
+
+ @Override
+ protected void removeRegistration() {
+ removeEntity(getInstance());
+ }
+ }
+
+ private final class ListenerRegistration extends AbstractObjectRegistration<DOMEntityOwnershipListener>
+ implements DOMEntityOwnershipListenerRegistration {
+ private final String entityType;
+
+ ListenerRegistration(final String entityType, final DOMEntityOwnershipListener listener) {
+ super(listener);
+ this.entityType = Preconditions.checkNotNull(entityType);
+ }
+
+ @Override
+ public String getEntityType() {
+ return entityType;
+ }
+
+ @Override
+ protected void removeRegistration() {
+ unregisterListener(this);
+ }
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2017 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.mdsal.eos.dom.simple;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyZeroInteractions;
+
+import com.google.common.base.Optional;
+import java.util.UUID;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+import org.opendaylight.mdsal.eos.common.api.CandidateAlreadyRegisteredException;
+import org.opendaylight.mdsal.eos.common.api.EntityOwnershipChangeState;
+import org.opendaylight.mdsal.eos.common.api.EntityOwnershipState;
+import org.opendaylight.mdsal.eos.dom.api.DOMEntity;
+import org.opendaylight.mdsal.eos.dom.api.DOMEntityOwnershipCandidateRegistration;
+import org.opendaylight.mdsal.eos.dom.api.DOMEntityOwnershipChange;
+import org.opendaylight.mdsal.eos.dom.api.DOMEntityOwnershipListener;
+import org.opendaylight.mdsal.eos.dom.api.DOMEntityOwnershipListenerRegistration;
+import org.opendaylight.mdsal.eos.dom.api.DOMEntityOwnershipService;
+
+public class SimpleDOMEntityOwnershipServiceTest {
+ private static final String FOO_TYPE = "foo";
+ private static final String BAR_TYPE = "bar";
+
+ private static final DOMEntity FOO_FOO_ENTITY = new DOMEntity(FOO_TYPE, "foo");
+ private static final DOMEntity FOO_BAR_ENTITY = new DOMEntity(FOO_TYPE, "bar");
+
+ private DOMEntityOwnershipService service;
+
+ @Before
+ public void setUp() {
+ service = new SimpleDOMEntityOwnershipService();
+ }
+
+ @Test
+ public void testNonExistingEntity() {
+ assertFalse(service.isCandidateRegistered(FOO_FOO_ENTITY));
+ final Optional<EntityOwnershipState> state = service.getOwnershipState(FOO_FOO_ENTITY);
+ assertNotNull(state);
+ assertFalse(state.isPresent());
+ }
+
+ @Test
+ public void testExistingEntity() throws CandidateAlreadyRegisteredException {
+ final DOMEntityOwnershipCandidateRegistration reg = service.registerCandidate(FOO_FOO_ENTITY);
+ assertNotNull(reg);
+
+ assertTrue(service.isCandidateRegistered(FOO_FOO_ENTITY));
+ assertFalse(service.isCandidateRegistered(FOO_BAR_ENTITY));
+
+ final Optional<EntityOwnershipState> state = service.getOwnershipState(FOO_FOO_ENTITY);
+ assertNotNull(state);
+ assertTrue(state.isPresent());
+ assertEquals(EntityOwnershipState.IS_OWNER, state.get());
+
+ reg.close();
+ assertFalse(service.isCandidateRegistered(FOO_FOO_ENTITY));
+ }
+
+ @Test(expected = CandidateAlreadyRegisteredException.class)
+ public void testDuplicateRegistration() throws CandidateAlreadyRegisteredException {
+ final DOMEntityOwnershipCandidateRegistration reg = service.registerCandidate(FOO_FOO_ENTITY);
+ assertNotNull(reg);
+
+ // Should throw
+ service.registerCandidate(FOO_FOO_ENTITY);
+ }
+
+ @Test
+ public void testListener() throws CandidateAlreadyRegisteredException {
+ final DOMEntityOwnershipCandidateRegistration entityReg = service.registerCandidate(FOO_FOO_ENTITY);
+ assertNotNull(entityReg);
+
+ // Mismatched type, not triggered
+ final DOMEntityOwnershipListener barListener = mock(DOMEntityOwnershipListener.class);
+ doNothing().when(barListener).ownershipChanged(any(DOMEntityOwnershipChange.class));
+ final DOMEntityOwnershipListenerRegistration barReg = service.registerListener(BAR_TYPE, barListener);
+ verifyZeroInteractions(barListener);
+
+ // Matching type should be triggered
+ final DOMEntityOwnershipListener fooListener = mock(DOMEntityOwnershipListener.class);
+ doNothing().when(fooListener).ownershipChanged(any(DOMEntityOwnershipChange.class));
+ final DOMEntityOwnershipListenerRegistration fooReg = service.registerListener(FOO_TYPE, fooListener);
+ final ArgumentCaptor<DOMEntityOwnershipChange> fooCaptor = ArgumentCaptor.forClass(
+ DOMEntityOwnershipChange.class);
+ verify(fooListener).ownershipChanged(fooCaptor.capture());
+
+ DOMEntityOwnershipChange fooChange = fooCaptor.getValue();
+ assertEquals(FOO_FOO_ENTITY, fooChange.getEntity());
+ assertEquals(EntityOwnershipChangeState.LOCAL_OWNERSHIP_GRANTED, fooChange.getState());
+
+ reset(fooListener);
+ doNothing().when(fooListener).ownershipChanged(any(DOMEntityOwnershipChange.class));
+ entityReg.close();
+ verifyZeroInteractions(barListener);
+ verify(fooListener).ownershipChanged(fooCaptor.capture());
+ fooChange = fooCaptor.getValue();
+ assertEquals(FOO_FOO_ENTITY, fooChange.getEntity());
+ assertEquals(EntityOwnershipChangeState.LOCAL_OWNERSHIP_LOST_NO_OWNER, fooChange.getState());
+
+ fooReg.close();
+ barReg.close();
+ }
+
+ @Test
+ public void testToString() throws CandidateAlreadyRegisteredException {
+ final UUID uuid = UUID.randomUUID();
+ final String expected = String.format("SimpleDOMEntityOwnershipService{uuid=%s, entities={}, listeners={}}",
+ uuid);
+ assertEquals(expected, new SimpleDOMEntityOwnershipService(uuid).toString());
+ }
+}
<module>mdsal-eos-common-api</module>
<module>mdsal-eos-common-spi</module>
<module>mdsal-eos-dom-api</module>
+ <module>mdsal-eos-dom-simple</module>
<module>mdsal-eos-binding-api</module>
<module>mdsal-eos-binding-adapter</module>
</modules>