/*
* 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.topology.manager;
import com.google.common.base.Preconditions;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import org.opendaylight.controller.md.sal.binding.api.BindingTransactionChain;
import org.opendaylight.controller.md.sal.binding.api.DataBroker;
import org.opendaylight.controller.md.sal.binding.api.ReadWriteTransaction;
import org.opendaylight.controller.md.sal.common.api.data.AsyncTransaction;
import org.opendaylight.controller.md.sal.common.api.data.TransactionChain;
import org.opendaylight.controller.md.sal.common.api.data.TransactionChainListener;
import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public final class OperationProcessor implements AutoCloseable, Runnable, TransactionChainListener {
private static final Logger LOG = LoggerFactory.getLogger(OperationProcessor.class);
private static final int MAX_TRANSACTION_OPERATIONS = 100;
private static final int OPERATION_QUEUE_DEPTH = 500;
private final BlockingQueue<TopologyOperation> queue = new LinkedBlockingQueue<>(OPERATION_QUEUE_DEPTH);
private final DataBroker dataBroker;
private final Thread thread;
private BindingTransactionChain transactionChain;
private volatile boolean finishing = false;
public OperationProcessor(final DataBroker dataBroker) {
this.dataBroker = Preconditions.checkNotNull(dataBroker);
transactionChain = this.dataBroker.createTransactionChain(this);
thread = new Thread(this);
thread.setDaemon(true);
thread.setName("FlowCapableTopologyExporter-" + FlowCapableTopologyProvider.TOPOLOGY_ID);
}
void enqueueOperation(final TopologyOperation task) {
try {
queue.put(task);
} catch (InterruptedException e) {
LOG.warn("Interrupted while submitting task {}", task, e);
}
}
public void start() {
thread.start();
}
@Override
public void run() {
while (!finishing) {
try {
TopologyOperation op = queue.take();
LOG.debug("New {} operation available, starting transaction", op);
final ReadWriteTransaction tx = transactionChain.newReadWriteTransaction();
int ops = 0;
do {
op.applyOperation(tx);
ops++;
if (ops < MAX_TRANSACTION_OPERATIONS) {
op = queue.poll();
} else {
op = null;
}
LOG.debug("Next operation {}", op);
} while (op != null);
LOG.debug("Processed {} operations, submitting transaction", ops);
submitTransaction(tx);
} catch (final IllegalStateException e) {
LOG.warn("Stat DataStoreOperation unexpected State!", e);
transactionChain.close();
transactionChain = dataBroker.createTransactionChain(this);
cleanDataStoreOperQueue();
} catch (final InterruptedException e) {
// This should mean we're shutting down.
LOG.debug("Stat Manager DS Operation thread interrupted!", e);
finishing = true;
} catch (final Exception e) {
LOG.warn("Stat DataStore Operation executor fail!", e);
}
}
// Drain all events, making sure any blocked threads are unblocked
cleanDataStoreOperQueue();
}
private void submitTransaction(ReadWriteTransaction tx) {
try {
tx.submit().checkedGet();
} catch (final TransactionCommitFailedException e) {
LOG.warn("Stat DataStoreOperation unexpected State!", e);
transactionChain.close();
transactionChain = dataBroker.createTransactionChain(this);
cleanDataStoreOperQueue();
}
}
private void cleanDataStoreOperQueue() {
while (!queue.isEmpty()) {
queue.poll();
}
}
@Override
public void onTransactionChainFailed(TransactionChain<?, ?> chain, AsyncTransaction<?, ?> transaction, Throwable cause) {
LOG.warn("Failed to export Topology manager operations, Transaction {} failed: {}", transaction.getIdentifier(), cause.getMessage());
LOG.debug("Failed to export Topology manager operations.. ", cause);
transactionChain.close();
transactionChain = dataBroker.createTransactionChain(this);
cleanDataStoreOperQueue();
}
@Override
public void onTransactionChainSuccessful(TransactionChain<?, ?> chain) {
//NOOP
}
@Override
public void close() {
thread.interrupt();
try {
thread.join();
} catch(InterruptedException e) {
LOG.debug("Join of thread {} was interrupted", thread.getName(), e);
}
if (transactionChain != null) {
transactionChain.close();
}
LOG.debug("OperationProcessor closed");
}
}