Java tutorial
/* * 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.sxp.route; import com.google.common.collect.Sets; import com.google.common.util.concurrent.FutureCallback; import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.ListeningScheduledExecutorService; import com.google.common.util.concurrent.MoreExecutors; import java.util.List; import java.util.Objects; import java.util.Set; import java.util.concurrent.CancellationException; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import javax.annotation.Nullable; import org.opendaylight.controller.config.yang.sxp.controller.conf.SxpControllerInstance; import org.opendaylight.controller.md.sal.binding.api.DataBroker; import org.opendaylight.mdsal.singleton.common.api.ClusterSingletonService; import org.opendaylight.mdsal.singleton.common.api.ClusterSingletonServiceProvider; import org.opendaylight.mdsal.singleton.common.api.ClusterSingletonServiceRegistration; import org.opendaylight.mdsal.singleton.common.api.ServiceGroupIdentifier; import org.opendaylight.sxp.controller.core.DatastoreAccess; import org.opendaylight.sxp.route.core.FollowerSyncStatusTaskFactory; import org.opendaylight.sxp.util.time.SxpTimerTask; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Purpose: provides workaround for closing {@link ClusterSingletonService}, if {@link ClusterSingletonServiceProvider} does not close them * TODO remove when cluster will always close its instances when switching */ public class ClusterSanityWatchdogInstance implements AutoCloseable, ClusterSingletonService { protected static final Logger LOG = LoggerFactory.getLogger(ClusterSanityWatchdogInstance.class); private final ListeningScheduledExecutorService scheduledExecutorService; private final FollowerSyncStatusTaskFactory taskFactory; private final AtomicBoolean isActive = new AtomicBoolean(false); private final Set<ClusterSingletonService> singletonServices = Sets.newConcurrentHashSet(); private final DataBroker dataBroker; private DatastoreAccess datastoreAccess; private ClusterSingletonServiceRegistration clusterServiceRegistration; private ListenableFuture<Boolean> timer = Futures.immediateCancelledFuture(); /** * @param broker service providing access to Datastore * @param clusterSingletonServiceProvider service used for registration * @param period period of watchdog feed * @param failLimit acceptable missed watchdog feeds */ public ClusterSanityWatchdogInstance(final DataBroker broker, final ClusterSingletonServiceProvider clusterSingletonServiceProvider, final int period, final int failLimit) { LOG.info("Cluster sanity watchdog initiating .."); this.dataBroker = Objects.requireNonNull(broker); this.scheduledExecutorService = MoreExecutors.listeningDecorator(Executors.newScheduledThreadPool(1)); taskFactory = new FollowerSyncStatusTaskFactory(period, failLimit); clusterServiceRegistration = Objects.requireNonNull(clusterSingletonServiceProvider) .registerClusterSingletonService(this); LOG.info("Cluster sanity watchdog initiated"); } @Override public synchronized void close() throws Exception { if (clusterServiceRegistration != null) { clusterServiceRegistration.close(); clusterServiceRegistration = null; } else { return; } scheduledExecutorService.shutdown(); datastoreAccess.close(); timer.cancel(true); } /** * @param task containing logic that determines if cluster is healthy */ private synchronized void schedule(final SxpTimerTask<Boolean> task) { if (!timer.isDone()) { LOG.warn("double scheduling occurred!"); return; } timer = scheduledExecutorService.schedule(Objects.requireNonNull(task), task.getPeriod(), TimeUnit.SECONDS); Futures.addCallback(timer, new FutureCallback<Boolean>() { @Override public void onSuccess(@Nullable final Boolean clusterHealthy) { if (clusterHealthy != null && clusterHealthy) { if (isActive.get()) { LOG.debug("Regular rescheduling of cluster health status task"); schedule(task); } } else { LOG.info("Detected cluster isolation condition"); singletonServices.forEach(ClusterSingletonService::closeServiceInstance); } } @Override public void onFailure(@Nullable final Throwable t) { LOG.info("Failed to track cluster health status", t); if (isActive.get() && !(t instanceof CancellationException)) { LOG.debug("Rescheduling of cluster health status task after failure"); schedule(taskFactory.createFollowerSyncStatusTask(datastoreAccess)); } } }); } /** * Adds services that will be guarded by {@link ClusterSanityWatchdogInstance} * * @param services services that will be added */ public void setServices(List<ClusterSingletonService> services) { singletonServices.addAll(Objects.requireNonNull(services)); } @Override public synchronized void instantiateServiceInstance() { LOG.debug("Watched service woke up - firing scheduler"); if (isActive.getAndSet(true)) { return; } datastoreAccess = DatastoreAccess.getInstance(dataBroker); schedule(taskFactory.createFollowerSyncStatusTask(datastoreAccess)); } @Override public synchronized ListenableFuture<Void> closeServiceInstance() { LOG.debug("Watched service fall asleep - tearing down scheduler"); datastoreAccess.close(); timer.cancel(true); isActive.set(false); return Futures.immediateFuture(null); } @Override public ServiceGroupIdentifier getIdentifier() { return SxpControllerInstance.IDENTIFIER; } }