import static java.util.Objects.requireNonNull;
-import com.google.common.collect.ClassToInstanceMap;
-import com.google.common.collect.MutableClassToInstanceMap;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import java.io.PrintStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
-import org.opendaylight.mdsal.binding.dom.codec.api.BindingNormalizedNodeSerializer;
+import org.opendaylight.mdsal.binding.dom.codec.api.BindingCodecTree;
import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
import org.opendaylight.mdsal.dom.api.ClusteredDOMDataTreeChangeListener;
import org.opendaylight.mdsal.dom.api.DOMDataBroker;
-import org.opendaylight.mdsal.dom.api.DOMDataBrokerExtension;
-import org.opendaylight.mdsal.dom.api.DOMDataTreeChangeListener;
import org.opendaylight.mdsal.dom.api.DOMDataTreeChangeService;
-import org.opendaylight.mdsal.dom.api.DOMDataTreeIdentifier;
import org.opendaylight.mdsal.dom.api.DOMDataTreeReadTransaction;
import org.opendaylight.mdsal.dom.api.DOMDataTreeReadWriteTransaction;
import org.opendaylight.mdsal.dom.api.DOMDataTreeWriteTransaction;
import org.opendaylight.mdsal.dom.api.DOMTransactionChain;
-import org.opendaylight.mdsal.dom.api.DOMTransactionChainListener;
import org.opendaylight.mdsal.trace.api.TracingDOMDataBroker;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.mdsaltrace.rev160908.Config;
-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.data.api.YangInstanceIdentifier;
@SuppressWarnings("checkstyle:JavadocStyle")
//...because otherwise it whines about the elements in the @code block even though it's completely valid Javadoc
-
/**
* TracingBroker logs "write" operations and listener registrations to the md-sal. It logs the instance identifier path,
* the objects themselves, as well as the stack trace of the call invoking the registration or write operation.
* watchRegistrations and allow all registrations to be logged.
* </li>
* </ul>
- *
*/
public class TracingBroker implements TracingDOMDataBroker {
- @SuppressFBWarnings("SLF4J_LOGGER_SHOULD_BE_PRIVATE")
- static final Logger LOG = LoggerFactory.getLogger(TracingBroker.class);
+ private static final Logger LOG = LoggerFactory.getLogger(TracingBroker.class);
private static final int STACK_TRACE_FIRST_RELEVANT_FRAME = 2;
- private final String type; // "default" VS "pingpong"
- private final BindingNormalizedNodeSerializer codec;
+ private final BindingCodecTree codec;
private final DOMDataBroker delegate;
private final List<Watch> registrationWatches = new ArrayList<>();
private final List<Watch> writeWatches = new ArrayList<>();
final String iidString;
final LogicalDatastoreType store;
- Watch(String iidString, LogicalDatastoreType storeOrNull) {
- this.store = storeOrNull;
+ Watch(final String iidString, final LogicalDatastoreType storeOrNull) {
+ store = storeOrNull;
this.iidString = iidString;
}
- private String toIidCompString(YangInstanceIdentifier iid) {
+ private String toIidCompString(final YangInstanceIdentifier iid) {
StringBuilder builder = new StringBuilder();
toPathString(iid, builder);
- builder.append('/');
- return builder.toString();
+ return builder.append('/').toString();
}
- private boolean isParent(String parent, String child) {
+ private boolean isParent(final String parent, final String child) {
int parentOffset = 0;
if (parent.length() > 0 && parent.charAt(0) == '<') {
parentOffset = parent.indexOf('>') + 1;
}
@SuppressWarnings({ "checkstyle:hiddenField", "hiding" })
- public boolean subtreesOverlap(YangInstanceIdentifier iid, LogicalDatastoreType store) {
+ public boolean subtreesOverlap(final YangInstanceIdentifier iid, final LogicalDatastoreType store) {
if (this.store != null && !this.store.equals(store)) {
return false;
}
}
@SuppressWarnings({ "checkstyle:hiddenField", "hiding" })
- public boolean eventIsOfInterest(YangInstanceIdentifier iid, LogicalDatastoreType store) {
+ public boolean eventIsOfInterest(final YangInstanceIdentifier iid, final LogicalDatastoreType store) {
if (this.store != null && !this.store.equals(store)) {
return false;
}
}
}
- public TracingBroker(String type, DOMDataBroker delegate, Config config, BindingNormalizedNodeSerializer codec) {
- this.type = requireNonNull(type, "type");
+ public TracingBroker(final DOMDataBroker delegate, final Config config, final BindingCodecTree codec) {
this.delegate = requireNonNull(delegate, "delegate");
this.codec = requireNonNull(codec, "codec");
configure(config);
- if (config.isTransactionDebugContextEnabled() != null) {
- this.isDebugging = config.isTransactionDebugContextEnabled();
- } else {
- this.isDebugging = false;
- }
+ isDebugging = Boolean.TRUE.equals(config.getTransactionDebugContextEnabled());
final String db = "DataBroker";
- this.transactionChainsRegistry = new CloseTrackedRegistry<>(db, "createTransactionChain()", isDebugging);
- this.readOnlyTransactionsRegistry = new CloseTrackedRegistry<>(db, "newReadOnlyTransaction()", isDebugging);
- this.writeTransactionsRegistry = new CloseTrackedRegistry<>(db, "newWriteOnlyTransaction()", isDebugging);
- this.readWriteTransactionsRegistry = new CloseTrackedRegistry<>(db, "newReadWriteTransaction()", isDebugging);
+ transactionChainsRegistry = new CloseTrackedRegistry<>(db, "createTransactionChain()", isDebugging);
+ readOnlyTransactionsRegistry = new CloseTrackedRegistry<>(db, "newReadOnlyTransaction()", isDebugging);
+ writeTransactionsRegistry = new CloseTrackedRegistry<>(db, "newWriteOnlyTransaction()", isDebugging);
+ readWriteTransactionsRegistry = new CloseTrackedRegistry<>(db, "newReadWriteTransaction()", isDebugging);
}
- private void configure(Config config) {
+ private void configure(final Config config) {
registrationWatches.clear();
- List<String> paths = config.getRegistrationWatches();
+ Set<String> paths = config.getRegistrationWatches();
if (paths != null) {
for (String path : paths) {
watchRegistrations(path, null);
* @param iidString the iid path of the root of the subtree
* @param store Which LogicalDataStore? or null for both
*/
- public void watchRegistrations(String iidString, LogicalDatastoreType store) {
+ public void watchRegistrations(final String iidString, final LogicalDatastoreType store) {
LOG.info("Watching registrations to {} in {}", iidString, store);
registrationWatches.add(new Watch(iidString, store));
}
* @param iidString the iid path of the root of the subtree
* @param store Which LogicalDataStore? or null for both
*/
- public void watchWrites(String iidString, LogicalDatastoreType store) {
+ public void watchWrites(final String iidString, final LogicalDatastoreType store) {
LOG.info("Watching writes to {} in {}", iidString, store);
Watch watch = new Watch(iidString, store);
writeWatches.add(watch);
}
- private boolean isRegistrationWatched(YangInstanceIdentifier iid, LogicalDatastoreType store) {
+ private boolean isRegistrationWatched(final YangInstanceIdentifier iid, final LogicalDatastoreType store) {
if (registrationWatches.isEmpty()) {
return true;
}
return false;
}
- boolean isWriteWatched(YangInstanceIdentifier iid, LogicalDatastoreType store) {
+ boolean isWriteWatched(final YangInstanceIdentifier iid, final LogicalDatastoreType store) {
if (writeWatches.isEmpty()) {
return true;
}
return false;
}
- static void toPathString(InstanceIdentifier<? extends DataObject> iid, StringBuilder builder) {
- for (InstanceIdentifier.PathArgument pathArg : iid.getPathArguments()) {
- builder.append('/').append(pathArg.getType().getSimpleName());
+ static void toPathString(final InstanceIdentifier<? extends DataObject> iid, final StringBuilder builder) {
+ for (var pathArg : iid.getPathArguments()) {
+ builder.append('/').append(pathArg.type().getSimpleName());
}
}
- String toPathString(YangInstanceIdentifier yiid) {
+ String toPathString(final YangInstanceIdentifier yiid) {
StringBuilder sb = new StringBuilder();
toPathString(yiid, sb);
return sb.toString();
}
- private void toPathString(YangInstanceIdentifier yiid, StringBuilder sb) {
- InstanceIdentifier<?> iid = codec.fromYangInstanceIdentifier(yiid);
+ private void toPathString(final YangInstanceIdentifier yiid, final StringBuilder sb) {
+ InstanceIdentifier<?> iid = codec.getInstanceIdentifierCodec().toBinding(yiid);
if (null == iid) {
reconstructIidPathString(yiid, sb);
} else {
}
}
- private static void reconstructIidPathString(YangInstanceIdentifier yiid, StringBuilder sb) {
+ private static void reconstructIidPathString(final YangInstanceIdentifier yiid, final StringBuilder sb) {
sb.append("<RECONSTRUCTED FROM: \"").append(yiid.toString()).append("\">");
for (YangInstanceIdentifier.PathArgument pathArg : yiid.getPathArguments()) {
- if (pathArg instanceof YangInstanceIdentifier.AugmentationIdentifier) {
- sb.append('/').append("AUGMENTATION");
- continue;
- }
sb.append('/').append(pathArg.getNodeType().getLocalName());
}
}
}
@Override
- public DOMTransactionChain createTransactionChain(DOMTransactionChainListener transactionChainListener) {
- return new TracingTransactionChain(delegate.createTransactionChain(transactionChainListener), this,
- transactionChainsRegistry);
+ public DOMTransactionChain createTransactionChain() {
+ return new TracingTransactionChain(delegate.createTransactionChain(), this, transactionChainsRegistry);
+ }
+
+ @Override
+ public DOMTransactionChain createMergingTransactionChain() {
+ return new TracingTransactionChain(delegate.createMergingTransactionChain(), this, transactionChainsRegistry);
}
@Override
}
@Override
- public ClassToInstanceMap<DOMDataBrokerExtension> getExtensions() {
- final ClassToInstanceMap<DOMDataBrokerExtension> delegateExt = delegate.getExtensions();
- final DOMDataTreeChangeService treeChangeSvc = delegateExt.getInstance(DOMDataTreeChangeService.class);
- if (treeChangeSvc == null) {
- return delegateExt;
- }
-
- final ClassToInstanceMap<DOMDataBrokerExtension> res = MutableClassToInstanceMap.create(delegateExt);
- res.put(DOMDataTreeChangeService.class, new DOMDataTreeChangeService() {
- @Override
- public <L extends DOMDataTreeChangeListener> ListenerRegistration<L> registerDataTreeChangeListener(
- DOMDataTreeIdentifier domDataTreeIdentifier, L listener) {
- if (isRegistrationWatched(domDataTreeIdentifier.getRootIdentifier(),
- domDataTreeIdentifier.getDatastoreType())) {
+ public <T extends Extension> T extension(final Class<T> type) {
+ final var ext = delegate.extension(type);
+ if (DOMDataTreeChangeService.class.equals(type) && ext instanceof DOMDataTreeChangeService treeChange) {
+ return type.cast((DOMDataTreeChangeService) (domDataTreeIdentifier, listener) -> {
+ final var rootId = domDataTreeIdentifier.path();
+ if (isRegistrationWatched(rootId, domDataTreeIdentifier.datastore())) {
LOG.warn("{} registration (registerDataTreeChangeListener) for {} from {}.",
- listener instanceof ClusteredDOMDataTreeChangeListener ? "Clustered" : "Non-clustered",
- toPathString(domDataTreeIdentifier.getRootIdentifier()), getStackSummary());
+ listener instanceof ClusteredDOMDataTreeChangeListener ? "Clustered" : "Non-clustered",
+ toPathString(rootId), getStackSummary());
}
- return treeChangeSvc.registerDataTreeChangeListener(domDataTreeIdentifier, listener);
- }
- });
-
- return res;
+ return treeChange.registerDataTreeChangeListener(domDataTreeIdentifier, listener);
+ });
+ }
+ return ext;
}
@Override
- public boolean printOpenTransactions(PrintStream ps, int minOpenTXs) {
+ public boolean printOpenTransactions(final PrintStream ps, final int minOpenTXs) {
if (transactionChainsRegistry.getAllUnique().isEmpty()
&& readOnlyTransactionsRegistry.getAllUnique().isEmpty()
&& writeTransactionsRegistry.getAllUnique().isEmpty()
&& readWriteTransactionsRegistry.getAllUnique().isEmpty()) {
- ps.println(type + ": No open transactions, great!");
+ ps.println("No open transactions, great!");
return false;
}
- ps.println(type + ": " + getClass().getSimpleName()
- + " found some not yet (or never..) closed transaction[chain]s!");
+ ps.println(getClass().getSimpleName() + " found some not yet (or never..) closed transaction[chain]s!");
ps.println("[NB: If no stack traces are shown below, then "
+ "enable transaction-debug-context-enabled in mdsaltrace_config.xml]");
ps.println();
return hasFound;
}
- private <T extends CloseTracked<T>> boolean print(
- CloseTrackedRegistry<T> registry, PrintStream ps, String indent, int minOpenTransactions) {
+ final void logEmptySet(final YangInstanceIdentifier yiid) {
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("Empty data set write to {}", toPathString(yiid));
+ }
+ }
+
+ @SuppressFBWarnings(value = "SLF4J_SIGN_ONLY_FORMAT", justification = "pre-formatted logs")
+ static final void logOperations(final Object identifier, final List<?> operations) {
+ if (LOG.isWarnEnabled()) {
+ LOG.warn("Transaction {} contains the following operations:", identifier);
+ for (var operation : operations) {
+ LOG.warn("{}", operation);
+ }
+ }
+ }
+
+ private <T extends CloseTracked<T>> boolean print(final CloseTrackedRegistry<T> registry, final PrintStream ps,
+ final String indent, final int minOpenTransactions) {
Set<CloseTrackedRegistryReportEntry<T>> unsorted = registry.getAllUnique();
if (unsorted.size() < minOpenTransactions) {
return false;
return true;
}
- private void printStackTraceElements(PrintStream ps, String indent, List<StackTraceElement> stackTraceElements) {
+ private void printStackTraceElements(final PrintStream ps, final String indent,
+ final List<StackTraceElement> stackTraceElements) {
boolean ellipsis = false;
for (final StackTraceElement stackTraceElement : stackTraceElements) {
if (isStackTraceElementInteresting(stackTraceElement)) {
}
}
- private boolean isStackTraceElementInteresting(StackTraceElement element) {
+ private boolean isStackTraceElementInteresting(final StackTraceElement element) {
final String className = element.getClassName();
return !className.startsWith(getClass().getPackage().getName())
&& !className.startsWith(CloseTracked.class.getPackage().getName())