/* * 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.controller.md.sal.dom.store.impl; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker.DataChangeScope; import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeEvent; import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeListener; import org.opendaylight.controller.sal.core.spi.data.DOMStore; import org.opendaylight.controller.sal.core.spi.data.DOMStoreReadTransaction; import org.opendaylight.controller.sal.core.spi.data.DOMStoreReadWriteTransaction; import org.opendaylight.controller.sal.core.spi.data.DOMStoreThreePhaseCommitCohort; import org.opendaylight.yangtools.concepts.ListenerRegistration; import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; import com.google.common.base.Preconditions; import com.google.common.util.concurrent.SettableFuture; public class DatastoreTestTask { private final DOMStore store; private AsyncDataChangeListener> changeListener; private WriteTransactionCustomizer setup; private WriteTransactionCustomizer write; private ReadTransactionVerifier read; private WriteTransactionCustomizer cleanup; private YangInstanceIdentifier changePath; private DataChangeScope changeScope; private volatile boolean postSetup = false; private final ChangeEventListener internalListener; private final TestDCLExecutorService dclExecutorService; public DatastoreTestTask(final DOMStore datastore, final TestDCLExecutorService dclExecutorService) { this.store = datastore; this.dclExecutorService = dclExecutorService; internalListener = new ChangeEventListener(); } public DatastoreTestTask changeListener(final YangInstanceIdentifier path, final DataChangeScope scope, final AsyncDataChangeListener> changeListener) { this.changeListener = changeListener; this.changePath = path; this.changeScope = scope; return this; } public DatastoreTestTask changeListener(final YangInstanceIdentifier path, final DataChangeScope scope) { this.changePath = path; this.changeScope = scope; return this; } public DatastoreTestTask setup(final WriteTransactionCustomizer setup) { this.setup = setup; return this; } public DatastoreTestTask test(final WriteTransactionCustomizer write) { this.write = write; return this; } public DatastoreTestTask read(final ReadTransactionVerifier read) { this.read = read; return this; } public DatastoreTestTask cleanup(final WriteTransactionCustomizer cleanup) { this.cleanup = cleanup; return this; } public void run() throws InterruptedException, ExecutionException, TimeoutException { if (setup != null) { execute(setup); } ListenerRegistration registration = null; if (changePath != null) { registration = store.registerChangeListener(changePath, internalListener, changeScope); } Preconditions.checkState(write != null, "Write Transaction must be set."); postSetup = true; dclExecutorService.afterTestSetup(); execute(write); if (registration != null) { registration.close(); } if (changeListener != null) { changeListener.onDataChanged(getChangeEvent()); } if (read != null) { read.verify(store.newReadOnlyTransaction()); } if (cleanup != null) { execute(cleanup); } } public AsyncDataChangeEvent> getChangeEvent() { try { return internalListener.receivedChange.get(10, TimeUnit.SECONDS); } catch( Exception e ) { fail( "Error getting the AsyncDataChangeEvent from the Future: " + e ); } // won't get here return null; } public void verifyNoChangeEvent() { try { Object unexpected = internalListener.receivedChange.get(500, TimeUnit.MILLISECONDS); fail( "Got unexpected AsyncDataChangeEvent from the Future: " + unexpected ); } catch( TimeoutException e ) { // Expected } catch( Exception e ) { fail( "Error getting the AsyncDataChangeEvent from the Future: " + e ); } } private void execute(final WriteTransactionCustomizer writeCustomizer) throws InterruptedException, ExecutionException { DOMStoreReadWriteTransaction tx = store.newReadWriteTransaction(); writeCustomizer.customize(tx); DOMStoreThreePhaseCommitCohort cohort = tx.ready(); assertTrue(cohort.canCommit().get()); cohort.preCommit().get(); cohort.commit().get(); } public interface WriteTransactionCustomizer { public void customize(DOMStoreReadWriteTransaction tx); } public interface ReadTransactionVerifier { public void verify(DOMStoreReadTransaction tx); } private final class ChangeEventListener implements AsyncDataChangeListener> { protected final SettableFuture>> receivedChange = SettableFuture .create(); @Override public void onDataChanged(final AsyncDataChangeEvent> change) { if (postSetup) { receivedChange.set(change); } } } public static final WriteTransactionCustomizer simpleWrite(final YangInstanceIdentifier path, final NormalizedNode data) { return new WriteTransactionCustomizer() { @Override public void customize(final DOMStoreReadWriteTransaction tx) { tx.write(path, data); } }; } public static final WriteTransactionCustomizer simpleMerge(final YangInstanceIdentifier path, final NormalizedNode data) { return new WriteTransactionCustomizer() { @Override public void customize(final DOMStoreReadWriteTransaction tx) { tx.merge(path, data); } }; } public static final WriteTransactionCustomizer simpleDelete(final YangInstanceIdentifier path) { return new WriteTransactionCustomizer() { @Override public void customize(final DOMStoreReadWriteTransaction tx) { tx.delete(path); } }; } }