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