+ @Override
+ public void onSlaveRoleNotAcquired(final DeviceInfo deviceInfo) {
+ this.withoutRoleChains.remove(deviceInfo);
+ ContextChain contextChain = contextChainMap.get(deviceInfo);
+ if (contextChain != null) {
+ destroyContextChain(deviceInfo);
+ }
+ }
+
+ @Override
+ public void onDeviceDisconnected(final ConnectionContext connectionContext) {
+
+ final DeviceInfo deviceInfo = connectionContext.getDeviceInfo();
+ if (deviceInfo != null) {
+ ContextChain chain = contextChainMap.get(deviceInfo);
+ if (chain != null) {
+ if (chain.auxiliaryConnectionDropped(connectionContext)) {
+ LOG.info("Auxiliary connection from device {} disconnected.", deviceInfo.getLOGValue());
+ } else {
+ LOG.info("Device {} disconnected.", deviceInfo.getLOGValue());
+ Futures.transform(chain.connectionDropped(), new Function<Void, Object>() {
+ @Nullable
+ @Override
+ public Object apply(@Nullable Void aVoid) {
+ destroyContextChain(deviceInfo);
+ return null;
+ }
+ });
+ }
+ }
+ }
+ }
+
+ @Override
+ public void changeEntityOwnershipService(final EntityOwnershipService entityOwnershipService) {
+ if (Objects.nonNull(this.eosListenerRegistration)) {
+ LOG.warn("EOS Listener already registered.");
+ } else {
+ this.eosListenerRegistration = Verify.verifyNotNull(entityOwnershipService.registerListener
+ (ASYNC_SERVICE_ENTITY_TYPE, this));
+ }
+ }
+
+ private void startTimerRole() {
+ this.timerIsRunningRole = true;
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("There is a context chain without role, starting timer.");
+ }
+ timer.newTimeout(new RoleTimerTask(), this.checkRoleMaster, TimeUnit.MILLISECONDS);
+ }
+
+ private void stopTimerRole() {
+ this.timerIsRunningRole = false;
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("There are no context chains, stopping timer.");
+ }
+ }
+
+ private void timerTickRole() {
+ if (!withoutRoleChains.isEmpty()) {
+ this.withoutRoleChains.forEach((deviceInfo, contextChain) -> contextChain.makeDeviceSlave());
+ timer.newTimeout(new RoleTimerTask(), this.checkRoleMaster, TimeUnit.MILLISECONDS);
+ } else {
+ final Set<DeviceInfo> setOfClosedChains = new ConcurrentSet<>();
+ if (!this.contextChainMap.isEmpty()) {
+ this.contextChainMap.forEach((deviceInfo, contextChain) -> {
+ if (!contextChain.hasState()) {
+ LOG.warn("Context chain {} is long time without state. Closing.", deviceInfo);
+ setOfClosedChains.add(deviceInfo);
+ contextChain.close();
+ }
+ });
+ setOfClosedChains.forEach(this.contextChainMap::remove);
+ }
+ if (this.contextChainMap.isEmpty()) {
+ this.stopTimerRole();
+ } else {
+ timer.newTimeout(new RoleTimerTask(), this.checkRoleMaster, TimeUnit.MILLISECONDS);
+ }
+ }
+ }
+
+ @VisibleForTesting
+ boolean checkAllManagers() {
+ return Objects.nonNull(deviceManager) && Objects.nonNull(rpcManager) && Objects.nonNull(statisticsManager);
+ }
+
+ @Override
+ public void close() throws Exception {
+ this.contextChainMap.forEach((deviceInfo, contextChain) -> {
+ if (contextChain.isMastered(ContextChainMastershipState.CHECK)) {
+ contextChain.stopChain(true);
+ }
+ contextChain.close();
+ });
+ if (Objects.nonNull(eosListenerRegistration)) {
+ eosListenerRegistration.close();
+ }
+ }
+
+ @Override
+ public void ownershipChanged(EntityOwnershipChange entityOwnershipChange) {
+ if (!entityOwnershipChange.hasOwner()) {
+ final YangInstanceIdentifier yii = entityOwnershipChange.getEntity().getId();
+ final YangInstanceIdentifier.NodeIdentifierWithPredicates niiwp =
+ (YangInstanceIdentifier.NodeIdentifierWithPredicates) yii.getLastPathArgument();
+ String entityName = niiwp.getKeyValues().values().iterator().next().toString();
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("Entity {} has no owner", entityName);
+ }
+
+ if (entityName != null ){
+ final NodeId nodeId = new NodeId(entityName);
+ DeviceInfo inMap = null;
+ for (Map.Entry<DeviceInfo, ContextChain> entry : contextChainMap.entrySet()) {
+ if (entry.getKey().getNodeId().equals(nodeId)) {
+ inMap = entry.getKey();
+ break;
+ }
+ }
+ if (Objects.nonNull(inMap)) {
+ markToBeRemoved.add(inMap);
+ } else {
+ try {
+ LOG.info("Removing device: {} from DS", nodeId);
+ deviceManager
+ .removeDeviceFromOperationalDS(DeviceStateUtil.createNodeInstanceIdentifier(nodeId))
+ .checkedGet(5L, TimeUnit.SECONDS);
+ } catch (TimeoutException | TransactionCommitFailedException e) {
+ LOG.info("Not able to remove device {} from DS. Probably removed by another cluster node.",
+ nodeId);
+ }
+ }
+ }
+ }
+ }
+
+ private void sendNotificationNodeAdded(final DeviceInfo deviceInfo) {
+ this.deviceManager.sendNodeAddedNotification(deviceInfo);
+ }
+
+ private class RoleTimerTask implements TimerTask {
+
+ @Override
+ public void run(Timeout timeout) throws Exception {
+ timerTickRole();
+ }
+
+ }