Move to kernel style SPDX license identifiers
[lttng-ust.git] / liblttng-ust-java-agent / java / lttng-ust-agent-common / org / lttng / ust / agent / context / ContextInfoManager.java
index 90a79acc646a43e2135589f756e368b11da33999..22efe022e794c3b1930570f560df5b408567ea2a 100644 (file)
@@ -1,24 +1,18 @@
 /*
- * Copyright (C) 2015 - EfficiOS Inc., Alexandre Montplaisir <alexmonthy@efficios.com>
+ * SPDX-License-Identifier: LGPL-2.1-only
  *
- * This library is free software; you can redistribute it and/or modify it
- * under the terms of the GNU Lesser General Public License, version 2.1 only,
- * as published by the Free Software Foundation.
- *
- * This library is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
- * for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this library; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ * Copyright (C) 2015 EfficiOS Inc.
+ * Copyright (C) 2015 Alexandre Montplaisir <alexmonthy@efficios.com>
  */
 
 package org.lttng.ust.agent.context;
 
-import java.util.Set;
-import java.util.concurrent.CopyOnWriteArraySet;
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
 
 /**
  * The singleton manager of {@link IContextInfoRetriever} objects.
@@ -27,9 +21,20 @@ import java.util.concurrent.CopyOnWriteArraySet;
  */
 public final class ContextInfoManager {
 
-       private static final ContextInfoManager INSTANCE = new ContextInfoManager();
+       private static final String SHARED_LIBRARY_NAME = "lttng-ust-context-jni";
+
+       private static final Pattern VALID_CONTEXT_NAME_PATTERN = Pattern.compile("^[a-zA-Z0-9_\\.]+$");
+
+       private static ContextInfoManager instance;
+
+       private final Map<String, IContextInfoRetriever> contextInfoRetrievers = new ConcurrentHashMap<String, IContextInfoRetriever>();
+       private final Map<String, Long> contextInforRetrieverRefs = new HashMap<String, Long>();
 
-       private final Set<IContextInfoRetriever> cirs = new CopyOnWriteArraySet<IContextInfoRetriever>();
+       /**
+        * Lock used to keep the two maps above in sync when retrievers are
+        * registered or unregistered.
+        */
+       private final Object retrieverLock = new Object();
 
        /** Singleton class, constructor should not be accessed directly */
        private ContextInfoManager() {
@@ -38,25 +43,81 @@ public final class ContextInfoManager {
        /**
         * Get the singleton instance.
         *
+        * <p>
+        * Usage of this class requires the "liblttng-ust-context-jni.so" native
+        * library to be present on the system and available (passing
+        * -Djava.library.path=path to the JVM may be needed).
+        * </p>
+        *
         * @return The singleton instance
-        * @deprecated The context-retrieving facilities are not yet implemented.
+        * @throws IOException
+        *             If the shared library cannot be found.
+        * @throws SecurityException
+        *             We will forward any SecurityExcepion that may be thrown when
+        *             trying to load the JNI library.
         */
-       @Deprecated
-       public static ContextInfoManager getInstance() {
-               return INSTANCE;
+       public static synchronized ContextInfoManager getInstance() throws IOException, SecurityException {
+               if (instance == null) {
+                       try {
+                               System.loadLibrary(SHARED_LIBRARY_NAME);
+                       } catch (UnsatisfiedLinkError e) {
+                               throw new IOException(e);
+                       }
+                       instance = new ContextInfoManager();
+               }
+               return instance;
        }
 
        /**
         * Register a new context info retriever.
         *
-        * This method has no effect if the exact same retriever is already
-        * registered.
+        * <p>
+        * Each context info retriever is registered with a given "retriever name",
+        * which specifies the namespace of the context elements. This name is
+        * specified separately from the retriever objects, which would allow
+        * register the same retriever under different namespaces for example.
+        * </p>
         *
-        * @param cir
+        * <p>
+        * If the method returns false (indicating registration failure), then the
+        * retriever object will *not* be used for context information.
+        * </p>
+        *
+        * @param retrieverName
+        *            The name to register to the context retriever object with.
+        * @param contextInfoRetriever
         *            The context info retriever to register
+        * @return True if the retriever was successfully registered, false if there
+        *         was an error, for example if a retriever is already registered
+        *         with that name.
         */
-       public void addContextInfoRetriever(IContextInfoRetriever cir) {
-               cirs.add(cir);
+       public boolean registerContextInfoRetriever(String retrieverName, IContextInfoRetriever contextInfoRetriever) {
+               synchronized (retrieverLock) {
+                       if (!validateRetrieverName(retrieverName)) {
+                               return false;
+                       }
+
+                       if (contextInfoRetrievers.containsKey(retrieverName)) {
+                               /*
+                                * There is already a retriever registered with that name,
+                                * refuse the new registration.
+                                */
+                               return false;
+                       }
+                       /*
+                        * Inform LTTng-UST of the new retriever. The names have to start
+                        * with "$app." on the UST side!
+                        */
+                       long ref = LttngContextApi.registerProvider("$app." + retrieverName);
+                       if (ref == 0) {
+                               return false;
+                       }
+
+                       contextInfoRetrievers.put(retrieverName, contextInfoRetriever);
+                       contextInforRetrieverRefs.put(retrieverName, Long.valueOf(ref));
+
+                       return true;
+               }
        }
 
        /**
@@ -64,21 +125,65 @@ public final class ContextInfoManager {
         *
         * This method has no effect if the retriever was not already registered.
         *
-        * @param cir
+        * @param retrieverName
         *            The context info retriever to unregister
+        * @return True if unregistration was successful, false if there was an
+        *         error
         */
-       public void removeContextInfoRetriever(IContextInfoRetriever cir) {
-               cirs.remove(cir);
+       public boolean unregisterContextInfoRetriever(String retrieverName) {
+               synchronized (retrieverLock) {
+                       if (!contextInfoRetrievers.containsKey(retrieverName)) {
+                               /*
+                                * There was no retriever registered with that name.
+                                */
+                               return false;
+                       }
+                       contextInfoRetrievers.remove(retrieverName);
+                       long ref = contextInforRetrieverRefs.remove(retrieverName).longValue();
+
+                       /* Unregister the retriever on the UST side too */
+                       LttngContextApi.unregisterProvider(ref);
+
+                       return true;
+               }
        }
 
        /**
-        * Return a read-only view (does not support
-        * {@link java.util.Iterator#remove}) of the currently registered context
-        * info retrievers.
+        * Return the context info retriever object registered with the given name.
         *
-        * @return The current context info retrievers
+        * @param retrieverName
+        *            The retriever name to look for
+        * @return The corresponding retriever object, or <code>null</code> if there
+        *         was none
+        */
+       public IContextInfoRetriever getContextInfoRetriever(String retrieverName) {
+               /*
+                * Note that this method does not take the retrieverLock, it lets
+                * concurrent threads access the ConcurrentHashMap directly.
+                *
+                * It's fine for a get() to happen during a registration or
+                * unregistration, it's first-come-first-serve.
+                */
+               return contextInfoRetrievers.get(retrieverName);
+       }
+
+       /**
+        * Validate that the given retriever name contains only the allowed
+        * characters, which are alphanumerical characters, period "." and
+        * underscore "_". The name must also not start with a number.
         */
-       public Iterable<IContextInfoRetriever> getContextInfoRetrievers() {
-               return cirs;
+       private static boolean validateRetrieverName(String contextName) {
+               if (contextName.isEmpty()) {
+                       return false;
+               }
+
+               /* First character must not be a number */
+               if (Character.isDigit(contextName.charAt(0))) {
+                       return false;
+               }
+
+               /* Validate the other characters of the string */
+               Matcher matcher = VALID_CONTEXT_NAME_PATTERN.matcher(contextName);
+               return matcher.matches();
        }
 }
This page took 0.025446 seconds and 4 git commands to generate.