/** * 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.openflowplugin.applications.statistics.manager.impl; import com.google.common.base.Optional; import com.google.common.base.Preconditions; import org.opendaylight.controller.md.sal.binding.api.DataBroker; import org.opendaylight.controller.md.sal.binding.api.DataTreeIdentifier; import org.opendaylight.controller.md.sal.binding.api.DataTreeModification; import org.opendaylight.controller.md.sal.binding.api.ReadOnlyTransaction; import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType; import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException; import org.opendaylight.controller.sal.binding.api.NotificationProviderService; import org.opendaylight.openflowplugin.applications.statistics.manager.StatListeningCommiter; import org.opendaylight.openflowplugin.applications.statistics.manager.StatNodeRegistration; import org.opendaylight.openflowplugin.applications.statistics.manager.StatisticsManager; import org.opendaylight.openflowplugin.common.wait.SimpleTaskRetryLooper; import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node; import org.opendaylight.yangtools.concepts.ListenerRegistration; import org.opendaylight.yangtools.yang.binding.DataObject; import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; import org.opendaylight.yangtools.yang.binding.NotificationListener; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.ArrayList; import java.util.Collection; import java.util.Map; import java.util.concurrent.Callable; import java.util.concurrent.ConcurrentHashMap; /** * statistics-manager * org.opendaylight.openflowplugin.applications.statistics.manager.impl * * StatAbstractListeneningCommiter * Class is abstract implementation for all Configuration/DataStore DataTreeModification * listenable DataObjects like flows, groups, meters. It is a holder for common * functionality needed by construction/destruction class and for DataTreeModification * event processing. */ public abstract class StatAbstractListenCommit<T extends DataObject, N extends NotificationListener> extends StatAbstractNotifyCommit<N> implements StatListeningCommiter<T,N> { private static final Logger LOG = LoggerFactory.getLogger(StatAbstractListenCommit.class); private ListenerRegistration<StatAbstractListenCommit<T, N>> listenerRegistration; protected final Map<InstanceIdentifier<Node>, Map<InstanceIdentifier<T>, Integer>> mapNodesForDelete = new ConcurrentHashMap<>(); protected final Map<InstanceIdentifier<Node>, Integer> mapNodeFeautureRepeater = new ConcurrentHashMap<>(); protected final Map<InstanceIdentifier<Node>, ArrayList<T>> removedDataBetweenStatsCycle = new ConcurrentHashMap<>(); private final Class<T> clazz; protected final DataBroker dataBroker; protected final StatNodeRegistration nodeRegistrationManager; private ReadOnlyTransaction currentReadTx; private volatile boolean currentReadTxStale; private static final int STARTUP_LOOP_TICK = 500; private static final int STARTUP_LOOP_MAX_RETRIES = 8; private final DataTreeIdentifier<T> treeId = new DataTreeIdentifier<>(LogicalDatastoreType.CONFIGURATION, getWildCardedRegistrationPath()); /* Constructor has to make a registration */ public StatAbstractListenCommit(final StatisticsManager manager, final DataBroker db, final NotificationProviderService nps, final Class<T> clazz, final StatNodeRegistration nodeRegistrationManager) { super(manager,nps, nodeRegistrationManager); this.clazz = Preconditions.checkNotNull(clazz, "Referenced Class can not be null"); Preconditions.checkArgument(db != null, "DataBroker can not be null!"); this.dataBroker = db; this.nodeRegistrationManager = nodeRegistrationManager; SimpleTaskRetryLooper looper = new SimpleTaskRetryLooper(STARTUP_LOOP_TICK, STARTUP_LOOP_MAX_RETRIES); try { listenerRegistration = looper.loopUntilNoException(new Callable<ListenerRegistration<StatAbstractListenCommit<T, N>>>() { @Override public ListenerRegistration<StatAbstractListenCommit<T, N>> call() throws Exception { return db.registerDataTreeChangeListener(treeId,StatAbstractListenCommit.this); } }); } catch (final Exception ex) { LOG.debug(" StatAbstractListenCommit DataTreeChangeListener registration failed {}", ex.getMessage()); throw new IllegalStateException("Notification supplier startup fail! System needs restart.", ex); } } /** * Method returns WildCarded Path which is used for registration as a listening path changes in * {@link org.opendaylight.controller.md.sal.binding.api.DataTreeChangeListener} * @return */ protected abstract InstanceIdentifier<T> getWildCardedRegistrationPath(); protected abstract void processDataChange(Collection<DataTreeModification<T>> changes); @Override public void onDataTreeChanged(Collection<DataTreeModification<T>> changes) { Preconditions.checkNotNull(changes, "Changes must not be null!"); /* * If we have opened read transaction for configuration data store, we need to mark it as stale. * * Latest read transaction will be allocated on another read using readLatestConfiguration */ currentReadTxStale = true; processDataChange(changes); } @SuppressWarnings("unchecked") protected void removeData(final InstanceIdentifier<?> key, final Integer value) { if (clazz.equals(key.getTargetType())) { final InstanceIdentifier<Node> nodeIdent = key.firstIdentifierOf(Node.class); Map<InstanceIdentifier<T>, Integer> map = null; if (mapNodesForDelete.containsKey(nodeIdent)) { map = mapNodesForDelete.get(nodeIdent); } if (map == null) { map = new ConcurrentHashMap<>(); mapNodesForDelete.put(nodeIdent, map); } map.put((InstanceIdentifier<T>) key, value); } } @Override public void cleanForDisconnect(final InstanceIdentifier<Node> nodeIdent) { mapNodesForDelete.remove(nodeIdent); removedDataBetweenStatsCycle.remove(nodeIdent); } @Override public void close() { if (listenerRegistration != null) { try { listenerRegistration.close(); } catch (final Exception e) { LOG.error("Error by stop {} DataTreeChangeListener StatListeningCommiter.", clazz.getSimpleName(), e); } listenerRegistration = null; } super.close(); } /** * Method return actual DataObject identified by InstanceIdentifier from Config/DS * @param path * @return */ protected final <K extends DataObject> Optional<K> readLatestConfiguration(final InstanceIdentifier<K> path) { for(int i = 0; i < 2; i++) { boolean localReadTxStale = currentReadTxStale; // This non-volatile read piggy backs the volatile currentReadTxStale read above to // ensure visibility in case this method is called across threads (although not concurrently). ReadOnlyTransaction localReadTx = currentReadTx; if(localReadTx == null || localReadTxStale) { if(localReadTx != null) { localReadTx.close(); } localReadTx = dataBroker.newReadOnlyTransaction(); currentReadTx = localReadTx; // Note - this volatile write also publishes the non-volatile currentReadTx write above. currentReadTxStale = false; } try { return localReadTx.read(LogicalDatastoreType.CONFIGURATION, path).checkedGet(); } catch (final ReadFailedException e) { LOG.debug("It wasn't possible to read {} from datastore. Exception: {}", path, e); // Loop back and try again with a new Tx. currentReadTxStale = true; } } return Optional.absent(); } }