--- /dev/null
+/*
+ * Copyright (C) 2015 - EfficiOS Inc., Alexandre Montplaisir <alexmonthy@efficios.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+import java.io.IOException;
+import java.util.logging.Level;
+
+import org.lttng.ust.agent.ILttngHandler;
+import org.lttng.ust.agent.filter.FilterChangeNotifier;
+import org.lttng.ust.agent.filter.IFilterChangeListener;
+import org.lttng.ust.agent.jul.LttngLogHandler;
+import org.lttng.ust.agent.session.EventRule;
+import org.lttng.ust.agent.session.LogLevelSelector;
+
+/**
+ * Example usage of a {@link IFilterChangeListener}.
+ *
+ * This listener will simply print to stdout the notifications it receives. To
+ * try it, run the program, then issue "lttng enable-event" and
+ * "lttng disable-event" commands for the JUL domain.
+ *
+ * @author Alexandre Montplaisir
+ */
+public class FilerChangeListenerExample {
+
+ private static class ExampleFilterChangeListener implements IFilterChangeListener {
+
+ @Override
+ public void eventRuleAdded(EventRule rule) {
+ System.out.println();
+ System.out.println("New event rule enabled:");
+ System.out.println("Event name: " + rule.getEventName());
+ System.out.println(printLogLevel(rule.getLogLevelSelector()));
+ System.out.println("Filter string: " + rule.getFilterString());
+ }
+
+ @Override
+ public void eventRuleRemoved(EventRule rule) {
+ System.out.println();
+ System.out.println("Event rule disabled:");
+ System.out.println("Event name: " + rule.getEventName());
+ System.out.println(printLogLevel(rule.getLogLevelSelector()));
+ System.out.println("Filter string: " + rule.getFilterString());
+ }
+
+ /**
+ * Convenience method to print integer log level values into their JUL
+ * equivalent.
+ */
+ private static String printLogLevel(LogLevelSelector lls) {
+ String llname = Level.parse(String.valueOf(lls.getLogLevel())).getName();
+ return "Log level: " + llname + ", filter type: " + lls.getLogLevelType().toString();
+ }
+ }
+
+ /**
+ * Run the program.
+ *
+ * @param args
+ * Command-line arguments (not used)
+ * @throws IOException
+ */
+ public static void main(String args[]) throws IOException {
+ /* We need at least one log handler to activate the LTTng agent */
+ ILttngHandler handler = new LttngLogHandler();
+
+ /* Create a listener and register it to the manager */
+ IFilterChangeListener listener = new ExampleFilterChangeListener();
+ FilterChangeNotifier.getInstance().registerListener(listener);
+
+ System.out.println("Press Enter to finish.");
+ System.in.read();
+
+ /* Unregister the listener */
+ FilterChangeNotifier.getInstance().unregisterListener(listener);
+
+ /* Cleanup the log handler we created */
+ handler.close();
+ }
+}
\ No newline at end of file
.java.class:
$(JC) $(JFLAGS) $*.java
-CLASSES = Hello.java
+CLASSES = Hello.java FilerChangeListenerExample.java
all: classes
$(pkgpath)/client/SessiondDisableEventCommand.java \
$(pkgpath)/client/SessiondEnableEventCommand.java \
$(pkgpath)/client/SessiondListLoggersCommand.java \
+ $(pkgpath)/filter/FilterChangeNotifier.java \
+ $(pkgpath)/filter/IFilterChangeListener.java \
$(pkgpath)/session/EventRule.java \
$(pkgpath)/session/LogLevelSelector.java
jar_DATA = $(jarfile)
-classes = $(pkgpath)/*.class $(pkgpath)/client/*.class $(pkgpath)/session/*.class
+classes = $(pkgpath)/*.class $(pkgpath)/client/*.class $(pkgpath)/filter/*.class $(pkgpath)/session/*.class
$(jarfile): classnoinst.stamp
$(JAR) cfm $(JARFLAGS) $@ $(jarfile_manifest) $(classes) && rm -f $(jarfile_symlink) && $(LN_S) $@ $(jarfile_symlink)
uninstall-hook:
cd $(DESTDIR)/$(jardir) && rm -f $(jarfile_symlink)
-CLEANFILES = $(jarfile) $(pkgpath)/*.class $(pkgpath)/client/*.class $(pkgpath)/session/*.class
+CLEANFILES = $(jarfile) $(pkgpath)/*.class $(pkgpath)/client/*.class $(pkgpath)/filter/*.class $(pkgpath)/session/*.class
import org.lttng.ust.agent.client.ILttngTcpClientListener;
import org.lttng.ust.agent.client.LttngTcpSessiondClient;
+import org.lttng.ust.agent.filter.FilterChangeNotifier;
import org.lttng.ust.agent.session.EventRule;
/**
@Override
public boolean eventEnabled(EventRule eventRule) {
+ /* Notify the filter change manager of the command */
+ FilterChangeNotifier.getInstance().addEventRule(eventRule);
+
String eventName = eventRule.getEventName();
if (eventName.equals(WILDCARD)) {
@Override
public boolean eventDisabled(String eventName) {
+ /* Notify the filter change manager of the command */
+ FilterChangeNotifier.getInstance().removeEventRules(eventName);
+
if (eventName.equals(WILDCARD)) {
int newCount = enabledWildcards.decrementAndGet();
if (newCount < 0) {
--- /dev/null
+/*
+ * Copyright (C) 2015 - EfficiOS Inc., Alexandre Montplaisir <alexmonthy@efficios.com>
+ *
+ * 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
+ */
+
+package org.lttng.ust.agent.filter;
+
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+
+import org.lttng.ust.agent.session.EventRule;
+
+/**
+ * Singleton class managing the filter notifications.
+ *
+ * Applications can register a {@link IFilterChangeListener} to be notified when
+ * event filtering rules change in the tracing sessions.
+ *
+ * @author Alexandre Montplaisir
+ */
+public final class FilterChangeNotifier {
+
+ /** Lazy-loaded singleton instance object */
+ private static FilterChangeNotifier instance = null;
+
+ private final Map<EventRule, Integer> enabledEventRules = new HashMap<EventRule, Integer>();
+ private final Collection<IFilterChangeListener> registeredListeners = new LinkedList<IFilterChangeListener>();
+
+
+ /**
+ * Private constructor, singleton class should not be instantiated directly.
+ */
+ private FilterChangeNotifier() {
+ }
+
+ /**
+ * Get the singleton instance, initializing it if needed.
+ *
+ * @return The singleton instance
+ */
+ public static synchronized FilterChangeNotifier getInstance() {
+ if (instance == null) {
+ instance = new FilterChangeNotifier();
+ }
+ return instance;
+ }
+
+ /**
+ * Notify the filter manager that a new rule was enabled in a tracing
+ * session ("lttng enable-event ...")
+ *
+ * This is meant to be called by the LTTng Agent only. External Java
+ * applications should not call this.
+ *
+ * @param rule
+ * The rule that was added
+ */
+ public synchronized void addEventRule(EventRule rule) {
+ Integer count = enabledEventRules.get(rule);
+ if (count == null) {
+ /*
+ * This is the first instance of this rule being enabled. Add it to
+ * the map and send notifications to the registered notifiers.
+ */
+ enabledEventRules.put(rule, Integer.valueOf(1));
+ notifyForAddedRule(rule);
+ return;
+ }
+ if (count.intValue() <= 0) {
+ /* It should not have been in the map! */
+ throw new IllegalStateException();
+ }
+ /*
+ * This exact event rule was already enabled, just increment its
+ * refcount without sending notifications
+ */
+ enabledEventRules.put(rule, Integer.valueOf(count.intValue() + 1));
+ }
+
+ /**
+ * Notify the filter manager that an event name was disabled in the tracing
+ * sessions ("lttng disable-event ...").
+ *
+ * The "disable-event" only specifies an event name. This means all the
+ * rules containing this event name are to be disabled.
+ *
+ * This is meant to be called by the LTTng Agent only. External Java
+ * applications should not call this.
+ *
+ * @param eventName
+ * The event name to disable
+ */
+ public synchronized void removeEventRules(String eventName) {
+ List<EventRule> rulesToRemove = new LinkedList<EventRule>();
+
+ for (EventRule eventRule : enabledEventRules.keySet()) {
+ if (eventRule.getEventName().equals(eventName)) {
+ rulesToRemove.add(eventRule);
+ }
+ }
+ /*
+ * We cannot modify the map while iterating on it. We have to do the
+ * removal separately from the iteration above.
+ */
+ for (EventRule rule : rulesToRemove) {
+ removeEventRule(rule);
+ }
+ }
+
+ private synchronized void removeEventRule(EventRule eventRule) {
+ Integer count = enabledEventRules.get(eventRule);
+ if (count == null || count.intValue() <= 0) {
+ /*
+ * We were asked us to disable an event rule that was not enabled
+ * previously. Command error?
+ */
+ throw new IllegalStateException();
+ }
+ if (count.intValue() == 1) {
+ /*
+ * This is the last instance of this event rule being disabled,
+ * remove it from the map and send notifications of this rule being
+ * gone.
+ */
+ enabledEventRules.remove(eventRule);
+ notifyForRemovedRule(eventRule);
+ return;
+ }
+ /*
+ * Other sessions/daemons are still looking for this event rule, simply
+ * decrement its refcount, and do not send notifications.
+ */
+ enabledEventRules.put(eventRule, Integer.valueOf(count.intValue() - 1));
+
+ }
+
+ /**
+ * Register a new listener to the manager.
+ *
+ * @param listener
+ * The listener to add
+ */
+ public synchronized void registerListener(IFilterChangeListener listener) {
+ registeredListeners.add(listener);
+
+ /* Send the current rules to the new listener ("statedump") */
+ for (EventRule rule : enabledEventRules.keySet()) {
+ listener.eventRuleAdded(rule);
+ }
+ }
+
+ /**
+ * Unregister a listener from the manager.
+ *
+ * @param listener
+ * The listener to remove
+ */
+ public synchronized void unregisterListener(IFilterChangeListener listener) {
+ registeredListeners.remove(listener);
+ }
+
+ private void notifyForAddedRule(final EventRule rule) {
+ for (IFilterChangeListener notifier : registeredListeners) {
+ notifier.eventRuleAdded(rule);
+ }
+ }
+
+ private void notifyForRemovedRule(final EventRule rule) {
+ for (IFilterChangeListener notifier : registeredListeners) {
+ notifier.eventRuleRemoved(rule);
+ }
+ }
+}
--- /dev/null
+/*
+ * Copyright (C) 2015 - EfficiOS Inc., Alexandre Montplaisir <alexmonthy@efficios.com>
+ *
+ * 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
+ */
+
+package org.lttng.ust.agent.filter;
+
+import org.lttng.ust.agent.session.EventRule;
+
+/**
+ * Filter notification listener interface.
+ * <p>
+ * Applications wanting to be notified of event filtering rule changes should
+ * implement this interface, then register their listener using
+ * {@link FilterChangeNotifier#registerListener}.
+ * </p>
+ * <p>
+ * The callbacks defined in this interface will be called whenever an event rule
+ * is added or removed. The manager will take care of the reference-counting in
+ * case multiple tracing sessions enable the exact same rules. For example, the
+ * {@link #eventRuleRemoved} callback is only called when there are no more
+ * session interested into it.
+ * </p>
+ * <p>
+ * Do not forget to unregister the listener after use, using
+ * {@link FilterChangeNotifier#unregisterListener}. If you do not, or if
+ * you use an anonymous listener for example, these will remain attached until
+ * the complete shutdown of the application.
+ * </p>
+ * <p>
+ * Only one thread is used to dispatch notifications, sequentially. This means
+ * that if a callback hangs it will prevent other listeners from receiving
+ * notifications. Please take care of not blocking inside the listener
+ * callbacks, and use separate threads for potentially long or blocking
+ * operations.
+ * </p>
+ *
+ * @author Alexandre Montplaisir
+ */
+public interface IFilterChangeListener {
+
+ /**
+ * Notification that a new event rule is now enabled in the tracing
+ * sessions.
+ *
+ * @param rule
+ * The event rule that was enabled
+ */
+ void eventRuleAdded(EventRule rule);
+
+ /**
+ * Notification that an existing event rule is now disabled in the tracing
+ * sessions.
+ *
+ * @param rule
+ * The event rule that was disabled
+ */
+ void eventRuleRemoved(EventRule rule);
+}