7f99824601d986d2527cba3e3dfcfb03168ab863
[controller.git] / opendaylight / md-sal / mdsal-trace / dom-impl / src / main / java / org / opendaylight / controller / md / sal / trace / closetracker / impl / CloseTrackedRegistry.java
1 /*
2  * Copyright (c) 2017 Red Hat, Inc. and others. All rights reserved.
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6  * and is available at http://www.eclipse.org/legal/epl-v10.html
7  */
8 package org.opendaylight.controller.md.sal.trace.closetracker.impl;
9
10 import static java.util.Arrays.asList;
11 import static java.util.Collections.emptyList;
12
13 import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
14 import java.util.Arrays;
15 import java.util.Collections;
16 import java.util.Comparator;
17 import java.util.HashMap;
18 import java.util.HashSet;
19 import java.util.List;
20 import java.util.Map;
21 import java.util.Set;
22 import java.util.concurrent.ConcurrentSkipListSet;
23 import javax.annotation.concurrent.ThreadSafe;
24
25 /**
26  * Registry of {@link CloseTracked} instances.
27  *
28  * @author Michael Vorburger.ch
29  */
30 @ThreadSafe
31 public class CloseTrackedRegistry<T extends CloseTracked<T>> {
32
33     private final Object anchor;
34     private final String createDescription;
35
36     private final Set<CloseTracked<T>> tracked =
37         new ConcurrentSkipListSet<>(Comparator.comparingInt(System::identityHashCode));
38
39     private final boolean isDebugContextEnabled;
40
41     /**
42      * Constructor.
43      *
44      * @param anchor
45      *            object where this registry is stored in, used for human output in
46      *            logging and other output
47      * @param createDescription
48      *            description of creator of instances of this registry, typically
49      *            e.g. name of method in the anchor class
50      * @param isDebugContextEnabled
51      *            whether or not the call stack should be preserved; this is (of
52      *            course) an expensive operation, and should only be used during
53      *            troubleshooting
54      */
55     public CloseTrackedRegistry(Object anchor, String createDescription, boolean isDebugContextEnabled) {
56         this.anchor = anchor;
57         this.createDescription = createDescription;
58         this.isDebugContextEnabled = isDebugContextEnabled;
59     }
60
61     public boolean isDebugContextEnabled() {
62         return isDebugContextEnabled;
63     }
64
65     public Object getAnchor() {
66         return anchor;
67     }
68
69     public String getCreateDescription() {
70         return createDescription;
71     }
72
73     // package protected, not public; only CloseTrackedTrait invokes this
74     void add(CloseTracked<T> closeTracked) {
75         tracked.add(closeTracked);
76     }
77
78     // package protected, not public; only CloseTrackedTrait invokes this
79     void remove(CloseTracked<T> closeTracked) {
80         tracked.remove(closeTracked);
81     }
82
83     /**
84      * Creates and returns a "report" of (currently) tracked but not (yet) closed
85      * instances.
86      *
87      * @return Set of CloseTrackedRegistryReportEntry, of which each the stack trace
88      *         element identifies a unique allocation context (or an empty List if
89      *         debugContextEnabled is false), and value is the number of open
90      *         instances created at that place in the code.
91      */
92     // For some reason, FB sees 'map' as useless but it clearly isn't.
93     @SuppressFBWarnings("UC_USELESS_OBJECT")
94     public Set<CloseTrackedRegistryReportEntry<T>> getAllUnique() {
95         Map<List<StackTraceElement>, Long> map = new HashMap<>();
96         Set<CloseTracked<T>> copyOfTracked = new HashSet<>(tracked);
97         for (CloseTracked<T> closeTracked : copyOfTracked) {
98             final StackTraceElement[] stackTraceArray = closeTracked.getAllocationContextStackTrace();
99             List<StackTraceElement> stackTraceElements =
100                     stackTraceArray != null ? Arrays.asList(stackTraceArray) : Collections.emptyList();
101             map.merge(stackTraceElements, 1L, (oldValue, value) -> oldValue + 1);
102         }
103
104         Set<CloseTrackedRegistryReportEntry<T>> report = new HashSet<>();
105         map.forEach((stackTraceElements, number) -> copyOfTracked.stream().filter(closeTracked -> {
106             StackTraceElement[] closeTrackedStackTraceArray = closeTracked.getAllocationContextStackTrace();
107             List<StackTraceElement> closeTrackedStackTraceElements =
108                 closeTrackedStackTraceArray != null ? asList(closeTrackedStackTraceArray) : emptyList();
109             return closeTrackedStackTraceElements.equals(stackTraceElements);
110         }).findAny().ifPresent(exampleCloseTracked -> report.add(
111             new CloseTrackedRegistryReportEntry<>(exampleCloseTracked, number, stackTraceElements))));
112         return report;
113     }
114
115 }