Commit | Line | Data |
---|---|---|
27dbdc00 | 1 | /* |
c0c0989a | 2 | * SPDX-License-Identifier: LGPL-2.1-only |
27dbdc00 | 3 | * |
c0c0989a MJ |
4 | * Copyright (C) 2015 EfficiOS Inc. |
5 | * Copyright (C) 2015 Alexandre Montplaisir <alexmonthy@efficios.com> | |
27dbdc00 AM |
6 | */ |
7 | ||
8 | package org.lttng.ust.agent.context; | |
9 | ||
8ab5c06b AM |
10 | import java.io.IOException; |
11 | import java.util.HashMap; | |
12 | import java.util.Map; | |
13 | import java.util.concurrent.ConcurrentHashMap; | |
cad6b749 AM |
14 | import java.util.regex.Matcher; |
15 | import java.util.regex.Pattern; | |
27dbdc00 AM |
16 | |
17 | /** | |
18 | * The singleton manager of {@link IContextInfoRetriever} objects. | |
19 | * | |
20 | * @author Alexandre Montplaisir | |
21 | */ | |
22 | public final class ContextInfoManager { | |
23 | ||
8ab5c06b | 24 | private static final String SHARED_LIBRARY_NAME = "lttng-ust-context-jni"; |
27dbdc00 | 25 | |
cad6b749 AM |
26 | private static final Pattern VALID_CONTEXT_NAME_PATTERN = Pattern.compile("^[a-zA-Z0-9_\\.]+$"); |
27 | ||
8ab5c06b AM |
28 | private static ContextInfoManager instance; |
29 | ||
30 | private final Map<String, IContextInfoRetriever> contextInfoRetrievers = new ConcurrentHashMap<String, IContextInfoRetriever>(); | |
31 | private final Map<String, Long> contextInforRetrieverRefs = new HashMap<String, Long>(); | |
32 | ||
831b4b08 AM |
33 | /** |
34 | * Lock used to keep the two maps above in sync when retrievers are | |
35 | * registered or unregistered. | |
36 | */ | |
8ab5c06b | 37 | private final Object retrieverLock = new Object(); |
27dbdc00 AM |
38 | |
39 | /** Singleton class, constructor should not be accessed directly */ | |
40 | private ContextInfoManager() { | |
41 | } | |
42 | ||
43 | /** | |
44 | * Get the singleton instance. | |
45 | * | |
8ab5c06b AM |
46 | * <p> |
47 | * Usage of this class requires the "liblttng-ust-context-jni.so" native | |
48 | * library to be present on the system and available (passing | |
49 | * -Djava.library.path=path to the JVM may be needed). | |
50 | * </p> | |
51 | * | |
27dbdc00 | 52 | * @return The singleton instance |
8ab5c06b AM |
53 | * @throws IOException |
54 | * If the shared library cannot be found. | |
55 | * @throws SecurityException | |
56 | * We will forward any SecurityExcepion that may be thrown when | |
57 | * trying to load the JNI library. | |
27dbdc00 | 58 | */ |
8ab5c06b AM |
59 | public static synchronized ContextInfoManager getInstance() throws IOException, SecurityException { |
60 | if (instance == null) { | |
61 | try { | |
62 | System.loadLibrary(SHARED_LIBRARY_NAME); | |
63 | } catch (UnsatisfiedLinkError e) { | |
64 | throw new IOException(e); | |
65 | } | |
66 | instance = new ContextInfoManager(); | |
67 | } | |
68 | return instance; | |
27dbdc00 AM |
69 | } |
70 | ||
71 | /** | |
72 | * Register a new context info retriever. | |
73 | * | |
8ab5c06b AM |
74 | * <p> |
75 | * Each context info retriever is registered with a given "retriever name", | |
76 | * which specifies the namespace of the context elements. This name is | |
77 | * specified separately from the retriever objects, which would allow | |
78 | * register the same retriever under different namespaces for example. | |
79 | * </p> | |
80 | * | |
81 | * <p> | |
82 | * If the method returns false (indicating registration failure), then the | |
83 | * retriever object will *not* be used for context information. | |
84 | * </p> | |
27dbdc00 | 85 | * |
8ab5c06b AM |
86 | * @param retrieverName |
87 | * The name to register to the context retriever object with. | |
88 | * @param contextInfoRetriever | |
27dbdc00 | 89 | * The context info retriever to register |
8ab5c06b AM |
90 | * @return True if the retriever was successfully registered, false if there |
91 | * was an error, for example if a retriever is already registered | |
92 | * with that name. | |
27dbdc00 | 93 | */ |
8ab5c06b AM |
94 | public boolean registerContextInfoRetriever(String retrieverName, IContextInfoRetriever contextInfoRetriever) { |
95 | synchronized (retrieverLock) { | |
cad6b749 AM |
96 | if (!validateRetrieverName(retrieverName)) { |
97 | return false; | |
98 | } | |
99 | ||
8ab5c06b AM |
100 | if (contextInfoRetrievers.containsKey(retrieverName)) { |
101 | /* | |
102 | * There is already a retriever registered with that name, | |
103 | * refuse the new registration. | |
104 | */ | |
105 | return false; | |
106 | } | |
107 | /* | |
108 | * Inform LTTng-UST of the new retriever. The names have to start | |
109 | * with "$app." on the UST side! | |
110 | */ | |
111 | long ref = LttngContextApi.registerProvider("$app." + retrieverName); | |
112 | if (ref == 0) { | |
113 | return false; | |
114 | } | |
115 | ||
116 | contextInfoRetrievers.put(retrieverName, contextInfoRetriever); | |
117 | contextInforRetrieverRefs.put(retrieverName, Long.valueOf(ref)); | |
118 | ||
119 | return true; | |
120 | } | |
27dbdc00 AM |
121 | } |
122 | ||
123 | /** | |
124 | * Unregister a previously added context info retriever. | |
125 | * | |
126 | * This method has no effect if the retriever was not already registered. | |
127 | * | |
8ab5c06b | 128 | * @param retrieverName |
27dbdc00 | 129 | * The context info retriever to unregister |
8ab5c06b AM |
130 | * @return True if unregistration was successful, false if there was an |
131 | * error | |
27dbdc00 | 132 | */ |
8ab5c06b AM |
133 | public boolean unregisterContextInfoRetriever(String retrieverName) { |
134 | synchronized (retrieverLock) { | |
135 | if (!contextInfoRetrievers.containsKey(retrieverName)) { | |
136 | /* | |
137 | * There was no retriever registered with that name. | |
138 | */ | |
139 | return false; | |
140 | } | |
141 | contextInfoRetrievers.remove(retrieverName); | |
142 | long ref = contextInforRetrieverRefs.remove(retrieverName).longValue(); | |
143 | ||
144 | /* Unregister the retriever on the UST side too */ | |
145 | LttngContextApi.unregisterProvider(ref); | |
146 | ||
147 | return true; | |
148 | } | |
27dbdc00 AM |
149 | } |
150 | ||
151 | /** | |
8ab5c06b | 152 | * Return the context info retriever object registered with the given name. |
27dbdc00 | 153 | * |
8ab5c06b AM |
154 | * @param retrieverName |
155 | * The retriever name to look for | |
156 | * @return The corresponding retriever object, or <code>null</code> if there | |
157 | * was none | |
27dbdc00 | 158 | */ |
8ab5c06b | 159 | public IContextInfoRetriever getContextInfoRetriever(String retrieverName) { |
831b4b08 AM |
160 | /* |
161 | * Note that this method does not take the retrieverLock, it lets | |
162 | * concurrent threads access the ConcurrentHashMap directly. | |
163 | * | |
164 | * It's fine for a get() to happen during a registration or | |
165 | * unregistration, it's first-come-first-serve. | |
166 | */ | |
8ab5c06b | 167 | return contextInfoRetrievers.get(retrieverName); |
27dbdc00 | 168 | } |
cad6b749 AM |
169 | |
170 | /** | |
171 | * Validate that the given retriever name contains only the allowed | |
172 | * characters, which are alphanumerical characters, period "." and | |
173 | * underscore "_". The name must also not start with a number. | |
174 | */ | |
175 | private static boolean validateRetrieverName(String contextName) { | |
176 | if (contextName.isEmpty()) { | |
177 | return false; | |
178 | } | |
179 | ||
180 | /* First character must not be a number */ | |
181 | if (Character.isDigit(contextName.charAt(0))) { | |
182 | return false; | |
183 | } | |
184 | ||
185 | /* Validate the other characters of the string */ | |
186 | Matcher matcher = VALID_CONTEXT_NAME_PATTERN.matcher(contextName); | |
187 | return matcher.matches(); | |
188 | } | |
27dbdc00 | 189 | } |