import org.slf4j.LoggerFactory;
/**
- * Implementation of {@link ClusterSingletonServiceGroup} on top of the Entity Ownership Service. Since EOS is atomic
+ * Implementation of {@link ServiceGroup} on top of the Entity Ownership Service. Since EOS is atomic
* in its operation and singleton services incur startup and most notably cleanup, we need to do something smart here.
*
* <p>
* The implementation takes advantage of the fact that EOS provides stable ownership, i.e. owners are not moved as
* a result on new candidates appearing. We use two entities:
- * - service entity, to which all nodes register
- * - cleanup entity, which only the service entity owner registers to
+ * <ol>
+ * <li>service entity, to which all nodes register</li>
+ * <li>cleanup entity, which only the service entity owner registers to</li>
+ * </ol>
*
* <p>
* Once the cleanup entity ownership is acquired, services are started. As long as the cleanup entity is registered,
* it should remain the owner. In case a new service owner emerges, the old owner will start the cleanup process,
* eventually releasing the cleanup entity. The new owner registers for the cleanup entity -- but will not see it
* granted until the old owner finishes the cleanup.
- *
- * @param <P> the instance identifier path type
- * @param <E> the GenericEntity type
- * @param <C> the GenericEntityOwnershipChange type
- * @param <G> the GenericEntityOwnershipListener type
- * @param <S> the GenericEntityOwnershipService type
*/
-// FIXME: rename to ActiveServiceGroup
-final class ClusterSingletonServiceGroupImpl extends ClusterSingletonServiceGroup {
+final class ActiveServiceGroup extends ServiceGroup {
private enum EntityState {
/**
STOPPING,
}
- private static final Logger LOG = LoggerFactory.getLogger(ClusterSingletonServiceGroupImpl.class);
+ private static final Logger LOG = LoggerFactory.getLogger(ActiveServiceGroup.class);
private final DOMEntityOwnershipService entityOwnershipService;
private final String identifier;
private final Map<ServiceRegistration, ServiceInfo> services = new HashMap<>();
// Marker for when any state changed
- private static final AtomicIntegerFieldUpdater<ClusterSingletonServiceGroupImpl> DIRTY_UPDATER =
- AtomicIntegerFieldUpdater.newUpdater(ClusterSingletonServiceGroupImpl.class, "dirty");
+ private static final AtomicIntegerFieldUpdater<ActiveServiceGroup> DIRTY_UPDATER =
+ AtomicIntegerFieldUpdater.newUpdater(ActiveServiceGroup.class, "dirty");
private volatile int dirty;
// Simplified lock: non-reentrant, support tryLock() only
- private static final AtomicIntegerFieldUpdater<ClusterSingletonServiceGroupImpl> LOCK_UPDATER =
- AtomicIntegerFieldUpdater.newUpdater(ClusterSingletonServiceGroupImpl.class, "lock");
+ private static final AtomicIntegerFieldUpdater<ActiveServiceGroup> LOCK_UPDATER =
+ AtomicIntegerFieldUpdater.newUpdater(ActiveServiceGroup.class, "lock");
@SuppressWarnings("unused")
private volatile int lock;
* @param parent parent service
* @param services Services list
*/
- ClusterSingletonServiceGroupImpl(final String identifier, final DOMEntityOwnershipService entityOwnershipService,
+ ActiveServiceGroup(final String identifier, final DOMEntityOwnershipService entityOwnershipService,
final DOMEntity serviceEntity, final DOMEntity cleanupEntity, final List<ServiceRegistration> services) {
checkArgument(!identifier.isEmpty(), "Identifier may not be empty");
this.identifier = identifier;
}
@VisibleForTesting
- ClusterSingletonServiceGroupImpl(final String identifier, final DOMEntity serviceEntity,
+ ActiveServiceGroup(final String identifier, final DOMEntity serviceEntity,
final DOMEntity cleanupEntity, final DOMEntityOwnershipService entityOwnershipService) {
this(identifier, entityOwnershipService, serviceEntity, cleanupEntity, ImmutableList.of());
}
@VisibleForTesting
static final @NonNull String CLOSE_SERVICE_ENTITY_TYPE = "org.opendaylight.mdsal.AsyncServiceCloseEntityType";
- private final ConcurrentMap<String, ClusterSingletonServiceGroup> serviceGroupMap = new ConcurrentHashMap<>();
+ private final ConcurrentMap<String, ServiceGroup> serviceGroupMap = new ConcurrentHashMap<>();
private final DOMEntityOwnershipService entityOwnershipService;
/* EOS Entity Listeners Registration */
serviceEntityListenerReg = null;
final var future = Futures.allAsList(serviceGroupMap.values().stream()
- .map(ClusterSingletonServiceGroup::closeClusterSingletonGroup)
+ .map(ServiceGroup::closeClusterSingletonGroup)
.toList());
try {
LOG.debug("Waiting for service groups to stop");
checkArgument(!Strings.isNullOrEmpty(serviceIdentifier),
"ClusterSingletonService identifier may not be null nor empty");
- final ClusterSingletonServiceGroup serviceGroup;
+ final ServiceGroup serviceGroup;
final var existing = serviceGroupMap.get(serviceIdentifier);
if (existing == null) {
serviceGroup = createGroup(serviceIdentifier, new ArrayList<>(1));
return reg;
}
- private ClusterSingletonServiceGroup createGroup(final String serviceIdentifier,
+ private ServiceGroup createGroup(final String serviceIdentifier,
final List<ServiceRegistration> services) {
- return new ClusterSingletonServiceGroupImpl(serviceIdentifier, entityOwnershipService,
+ return new ActiveServiceGroup(serviceIdentifier, entityOwnershipService,
createEntity(SERVICE_ENTITY_TYPE, serviceIdentifier),
createEntity(CLOSE_SERVICE_ENTITY_TYPE, serviceIdentifier), services);
}
- private void initializeOrRemoveGroup(final ClusterSingletonServiceGroup group)
+ private void initializeOrRemoveGroup(final ServiceGroup group)
throws CandidateAlreadyRegisteredException {
try {
group.initialize();
}
private void removeRegistration(final String serviceIdentifier, final ServiceRegistration reg) {
- final PlaceholderGroup placeHolder;
+ final PlaceholderServiceGroup placeHolder;
final ListenableFuture<?> future;
synchronized (this) {
final var lookup = verifyNotNull(serviceGroupMap.get(serviceIdentifier));
// Close the group and replace it with a placeholder
LOG.debug("Closing service group {}", serviceIdentifier);
- placeHolder = new PlaceholderGroup(lookup, future);
+ placeHolder = new PlaceholderServiceGroup(lookup, future);
final String identifier = reg.getInstance().getIdentifier().getName();
verify(serviceGroupMap.replace(identifier, lookup, placeHolder));
future.addListener(() -> finishShutdown(placeHolder), MoreExecutors.directExecutor());
}
- private synchronized void finishShutdown(final PlaceholderGroup placeHolder) {
+ private synchronized void finishShutdown(final PlaceholderServiceGroup placeHolder) {
final var identifier = placeHolder.getIdentifier();
LOG.debug("Service group {} closed", identifier);
/**
* Intermediate place-holder to catch user requests while asynchronous shutdown of previous incarnation of
- * a {@link ClusterSingletonServiceGroup} finishes.
+ * a {@link ServiceGroup} finishes.
*/
-// FIXME: rename to PlaceholderServiceGroup
-final class PlaceholderGroup extends ClusterSingletonServiceGroup {
- private static final Logger LOG = LoggerFactory.getLogger(PlaceholderGroup.class);
+final class PlaceholderServiceGroup extends ServiceGroup {
+ private static final Logger LOG = LoggerFactory.getLogger(PlaceholderServiceGroup.class);
private final List<ServiceRegistration> services = new ArrayList<>(0);
- private final ClusterSingletonServiceGroup previous;
+ private final ServiceGroup previous;
private final ListenableFuture<?> closeFuture;
- private volatile ClusterSingletonServiceGroup successor;
+ private volatile ServiceGroup successor;
- PlaceholderGroup(final ClusterSingletonServiceGroup previous, final ListenableFuture<?> closeFuture) {
+ PlaceholderServiceGroup(final ServiceGroup previous, final ListenableFuture<?> closeFuture) {
this.previous = requireNonNull(previous);
this.closeFuture = requireNonNull(closeFuture);
}
return services;
}
- void setSuccessor(final ClusterSingletonServiceGroup successor) {
+ void setSuccessor(final ServiceGroup successor) {
verifyNoSuccessor();
this.successor = verifyNotNull(successor);
LOG.debug("{}: successor set to {}", this, successor);
import org.opendaylight.yangtools.concepts.Identifiable;
/**
- * {@link ClusterSingletonServiceGroup} maintains a group of {@link ClusterSingletonService} instances.
+ * {@link ServiceGroup} maintains a group of {@link ClusterSingletonService} instances.
* All EntityOwnershipChange notifications have to applied to all registered services at the same time in the same
* manner. All registered services have only one instantiated service instance in a cluster at one time on same
* Cluster Node. This is realized via a double candidate approach where a service group instance maintains a candidate
* registration for ownership of the service entity in the cluster and also a registration that acts as a guard to
* ensure a service group instance has fully closed prior to relinquishing service ownership. To achieve ownership
* of the service group, a service group candidate must hold ownership of both these entities.
- *
- * @param <P> the instance identifier path type
- * @param <E> the GenericEntity type
*/
-// FIXME: rename to ServiceGroup and seal
-abstract class ClusterSingletonServiceGroup implements Identifiable<String> {
+// FIXME: seal
+abstract class ServiceGroup implements Identifiable<String> {
/**
* This method must be called once on startup to initialize this group and register the relevant group entity
* candidate. It means create relevant Group Entity Candidate Registration.
import com.google.common.util.concurrent.ListenableFuture;
import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jdt.annotation.Nullable;
-import org.opendaylight.mdsal.singleton.dom.impl.ClusterSingletonServiceGroupImpl.ServiceState;
+import org.opendaylight.mdsal.singleton.dom.impl.ActiveServiceGroup.ServiceState;
final class ServiceInfo {
static final @NonNull ServiceInfo STARTED = new ServiceInfo(ServiceState.STARTED, null);
import org.opendaylight.yangtools.concepts.Registration;
/**
- * Testing {@link ClusterSingletonServiceGroupImpl}.
+ * Testing {@link ActiveServiceGroup}.
*/
@RunWith(MockitoJUnitRunner.StrictStubs.class)
-public class ClusterSingletonServiceGroupImplTest {
+public class ActiveServiceGroupTest {
public static final String SERVICE_IDENTIFIER = "TestServiceIdent";
public static final ServiceGroupIdentifier SERVICE_GROUP_IDENT = ServiceGroupIdentifier.create(SERVICE_IDENTIFIER);
@Mock
public DOMEntityOwnershipService mockEosService;
- public ClusterSingletonServiceGroupImpl singletonServiceGroup;
-
- public ServiceRegistration firstReg;
- public ServiceRegistration secondReg;
+ private ActiveServiceGroup singletonServiceGroup;
+ private ServiceRegistration firstReg;
+ private ServiceRegistration secondReg;
/**
* Initialization functionality for every Tests in this suite.
}
};
- singletonServiceGroup = new ClusterSingletonServiceGroupImpl(SERVICE_IDENTIFIER, MAIN_ENTITY, CLOSE_ENTITY,
- mockEosService);
+ singletonServiceGroup = new ActiveServiceGroup(SERVICE_IDENTIFIER, MAIN_ENTITY, CLOSE_ENTITY, mockEosService);
}
/**
@Test
public void instantiationClusterSingletonServiceGroupNullIdentTest() {
assertThrows(NullPointerException.class,
- () -> new ClusterSingletonServiceGroupImpl(null, MAIN_ENTITY, CLOSE_ENTITY, mockEosService));
+ () -> new ActiveServiceGroup(null, MAIN_ENTITY, CLOSE_ENTITY, mockEosService));
}
/**
@Test
public void instantiationClusterSingletonServiceGroupEmptyIdentTest() {
assertThrows(IllegalArgumentException.class,
- () -> new ClusterSingletonServiceGroupImpl("", MAIN_ENTITY, CLOSE_ENTITY, mockEosService));
+ () -> new ActiveServiceGroup("", MAIN_ENTITY, CLOSE_ENTITY, mockEosService));
}
/**
@Test
public void instantiationClusterSingletonServiceGroupNullMainEntityTest() {
assertThrows(NullPointerException.class,
- () -> new ClusterSingletonServiceGroupImpl(SERVICE_IDENTIFIER, null, CLOSE_ENTITY, mockEosService));
+ () -> new ActiveServiceGroup(SERVICE_IDENTIFIER, null, CLOSE_ENTITY, mockEosService));
}
/**
@Test
public void instantiationClusterSingletonServiceGroupNullCloseEntityTest() {
assertThrows(NullPointerException.class,
- () -> new ClusterSingletonServiceGroupImpl(SERVICE_IDENTIFIER, MAIN_ENTITY, null, mockEosService));
+ () -> new ActiveServiceGroup(SERVICE_IDENTIFIER, MAIN_ENTITY, null, mockEosService));
}
/**
@Test
public void instantiationClusterSingletonServiceGroupNullEOS_Test() {
assertThrows(NullPointerException.class,
- () -> new ClusterSingletonServiceGroupImpl(SERVICE_IDENTIFIER, MAIN_ENTITY, CLOSE_ENTITY, null));
+ () -> new ActiveServiceGroup(SERVICE_IDENTIFIER, MAIN_ENTITY, CLOSE_ENTITY, null));
}
/**