/* * Copyright (c) 2016 Ericsson India Global Services Pvt Ltd. 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.genius.datastoreutils; import com.google.common.base.Preconditions; import java.util.Collection; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; import javax.annotation.PostConstruct; import javax.annotation.PreDestroy; import org.opendaylight.controller.md.sal.binding.api.DataBroker; import org.opendaylight.controller.md.sal.binding.api.DataObjectModification; import org.opendaylight.controller.md.sal.binding.api.DataTreeChangeListener; import org.opendaylight.controller.md.sal.binding.api.DataTreeIdentifier; import org.opendaylight.controller.md.sal.binding.api.DataTreeModification; import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType; import org.opendaylight.genius.utils.SuperTypeUtil; import org.opendaylight.infrautils.utils.concurrent.ThreadFactoryProvider; import org.opendaylight.yangtools.concepts.ListenerRegistration; import org.opendaylight.yangtools.yang.binding.DataObject; import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @Deprecated public abstract class AsyncDataTreeChangeListenerBase> implements DataTreeChangeListener, ChainableDataTreeChangeListener, AutoCloseable { private static final Logger LOG = LoggerFactory.getLogger(AsyncDataTreeChangeListenerBase.class); private static final int DATATREE_CHANGE_HANDLER_THREAD_POOL_CORE_SIZE = 1; private static final int DATATREE_CHANGE_HANDLER_THREAD_POOL_MAX_SIZE = 1; private static final int DATATREE_CHANGE_HANDLER_THREAD_POOL_KEEP_ALIVE_TIME_SECS = 300; private ListenerRegistration listenerRegistration; private final ChainableDataTreeChangeListenerImpl chainingDelegate = new ChainableDataTreeChangeListenerImpl<>(); private final ThreadPoolExecutor dataTreeChangeHandlerExecutor = new ThreadPoolExecutor( DATATREE_CHANGE_HANDLER_THREAD_POOL_CORE_SIZE, DATATREE_CHANGE_HANDLER_THREAD_POOL_MAX_SIZE, DATATREE_CHANGE_HANDLER_THREAD_POOL_KEEP_ALIVE_TIME_SECS, TimeUnit.SECONDS, new LinkedBlockingQueue<>(), ThreadFactoryProvider.builder() .namePrefix("AsyncDataTreeChangeListenerBase-DataTreeChangeHandler") .logger(LOG) .build().get()); protected final Class clazz; protected AsyncDataTreeChangeListenerBase() { this.clazz = SuperTypeUtil.getTypeParameter(getClass(), 0); } @Deprecated public AsyncDataTreeChangeListenerBase(Class clazz, Class eventClazz) { this.clazz = Preconditions.checkNotNull(clazz, "Class can not be null!"); } @Override public void addBeforeListener(DataTreeChangeListener listener) { chainingDelegate.addBeforeListener(listener); } @Override public void addAfterListener(DataTreeChangeListener listener) { chainingDelegate.addAfterListener(listener); } @Override public void onDataTreeChanged(Collection> changes) { if (changes == null || changes.isEmpty()) { return; } DataTreeChangeHandler dataTreeChangeHandler = new DataTreeChangeHandler(changes); dataTreeChangeHandlerExecutor.execute(dataTreeChangeHandler); } public void registerListener(LogicalDatastoreType dsType, final DataBroker db) { final DataTreeIdentifier treeId = new DataTreeIdentifier<>(dsType, getWildCardPath()); listenerRegistration = db.registerDataTreeChangeListener(treeId, getDataTreeChangeListener()); } /** * Subclasses override this and place initialization logic here, notably * calls to registerListener(). Note that the overriding method MUST repeat * the PostConstruct annotation, because JSR 250 specifies that lifecycle * methods "are called unless a subclass of the declaring class overrides * the method without repeating the annotation". (The blueprint-maven-plugin * would gen. XML which calls PostConstruct annotated methods even if they are * in a subclass without repeating the annotation, but this is wrong and not * JSR 250 compliant, and while working in BP, then causes issues e.g. when * wiring with Guice for tests, so do always repeat it.) */ @PostConstruct protected void init() { } @Override @PreDestroy public void close() { dataTreeChangeHandlerExecutor.shutdownNow(); if (listenerRegistration != null) { try { listenerRegistration.close(); } finally { listenerRegistration = null; } } } protected abstract InstanceIdentifier getWildCardPath(); protected abstract void remove(InstanceIdentifier key, T dataObjectModification); protected abstract void update(InstanceIdentifier key, T dataObjectModificationBefore, T dataObjectModificationAfter); protected abstract void add(InstanceIdentifier key, T dataObjectModification); protected abstract K getDataTreeChangeListener(); public class DataTreeChangeHandler implements Runnable { private final Collection> changes; public DataTreeChangeHandler(Collection> changes) { chainingDelegate.notifyBeforeOnDataTreeChanged(changes); this.changes = changes; } @Override public void run() { for (DataTreeModification change : changes) { final InstanceIdentifier key = change.getRootPath().getRootIdentifier(); final DataObjectModification mod = change.getRootNode(); switch (mod.getModificationType()) { case DELETE: remove(key, mod.getDataBefore()); break; case SUBTREE_MODIFIED: update(key, mod.getDataBefore(), mod.getDataAfter()); break; case WRITE: if (mod.getDataBefore() == null) { add(key, mod.getDataAfter()); } else { update(key, mod.getDataBefore(), mod.getDataAfter()); } break; default: // FIXME: May be not a good idea to throw. throw new IllegalArgumentException("Unhandled modification type " + mod.getModificationType()); } } chainingDelegate.notifyAfterOnDataTreeChanged(changes); } } }