diff --git a/agent-api/src/main/java/com/intergral/deep/agent/api/DeepRuntimeException.java b/agent-api/src/main/java/com/intergral/deep/agent/api/DeepRuntimeException.java
new file mode 100644
index 0000000..d8b7deb
--- /dev/null
+++ b/agent-api/src/main/java/com/intergral/deep/agent/api/DeepRuntimeException.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2023 Intergral GmbH
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package com.intergral.deep.agent.api;
+
+public class DeepRuntimeException extends RuntimeException {
+
+ public DeepRuntimeException(final String message) {
+ super(message);
+ }
+
+ public DeepRuntimeException(final String message, final Throwable cause) {
+ super(message, cause);
+ }
+}
diff --git a/agent-api/src/main/java/com/intergral/deep/agent/api/IDeep.java b/agent-api/src/main/java/com/intergral/deep/agent/api/IDeep.java
index 389ad71..2148787 100644
--- a/agent-api/src/main/java/com/intergral/deep/agent/api/IDeep.java
+++ b/agent-api/src/main/java/com/intergral/deep/agent/api/IDeep.java
@@ -30,6 +30,25 @@
*/
public interface IDeep {
+ /**
+ * Get the current state of deep.
+ *
+ * @return {@code true} if deep is currently enabled and sending requests, else {@code false}
+ */
+ boolean isEnabled();
+
+
+ /**
+ * This method can be used to disabled or enable Deep.
+ *
+ * Changing the state to {@code false} (ie disabled) will cause deep to uninstall all the tracepoints and clear the current config.
+ * Meaning that when deep is enabled again it will have to reinstall the configuration. It is therefore advised to not call this function
+ * too frequently.
+ *
+ * @param enabled the new state to become
+ */
+ void setEnabled(final boolean enabled);
+
/**
* Get the version of deep being used.
*
diff --git a/agent-api/src/main/java/com/intergral/deep/agent/api/IRegistration.java b/agent-api/src/main/java/com/intergral/deep/agent/api/IRegistration.java
index 973d1da..df32f9c 100644
--- a/agent-api/src/main/java/com/intergral/deep/agent/api/IRegistration.java
+++ b/agent-api/src/main/java/com/intergral/deep/agent/api/IRegistration.java
@@ -20,10 +20,17 @@
/**
* This is a generic interface from the result of a registration.
*/
-public interface IRegistration {
+public interface IRegistration {
/**
* Unregister the item registered.
*/
void unregister();
+
+ /**
+ * Get the registered item
+ *
+ * @return the item that this registration is for.
+ */
+ T get();
}
diff --git a/agent-api/src/main/java/com/intergral/deep/agent/api/auth/AuthProvider.java b/agent-api/src/main/java/com/intergral/deep/agent/api/auth/AuthProvider.java
index 8d59b82..4a4227d 100644
--- a/agent-api/src/main/java/com/intergral/deep/agent/api/auth/AuthProvider.java
+++ b/agent-api/src/main/java/com/intergral/deep/agent/api/auth/AuthProvider.java
@@ -17,6 +17,8 @@
package com.intergral.deep.agent.api.auth;
+import com.intergral.deep.agent.api.DeepRuntimeException;
+import com.intergral.deep.agent.api.plugin.IPlugin;
import com.intergral.deep.agent.api.settings.ISettings;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
@@ -32,6 +34,18 @@ public static IAuthProvider provider(final ISettings settings) {
if (serviceAuthProvider == null || serviceAuthProvider.trim().isEmpty()) {
return NOOP_PROVIDER;
}
+
+ // check if we have a plugin of this name that we can use
+ final IPlugin plugin = settings.getPlugin(serviceAuthProvider);
+ if (plugin != null) {
+ if (plugin instanceof IAuthProvider) {
+ return (IAuthProvider) plugin;
+ } else {
+ throw new DeepRuntimeException(
+ String.format("Cannot use plugin %s as auth provider, must implement IAuthProvider interface.", plugin.name()));
+ }
+ }
+
try {
final Class> aClass = Class.forName(serviceAuthProvider);
final Constructor> constructor = aClass.getConstructor(ISettings.class);
@@ -42,7 +56,7 @@ public static IAuthProvider provider(final ISettings settings) {
| InvocationTargetException
| InstantiationException
| IllegalAccessException e) {
- throw new RuntimeException(e);
+ throw new RuntimeException(String.format("Cannot load auth provider %s", serviceAuthProvider), e);
}
}
diff --git a/agent-api/src/main/java/com/intergral/deep/agent/api/plugin/IPlugin.java b/agent-api/src/main/java/com/intergral/deep/agent/api/plugin/IPlugin.java
index b0fd9a7..1a05365 100644
--- a/agent-api/src/main/java/com/intergral/deep/agent/api/plugin/IPlugin.java
+++ b/agent-api/src/main/java/com/intergral/deep/agent/api/plugin/IPlugin.java
@@ -70,7 +70,14 @@ default boolean isActive(final ISettings settings) {
/**
* This type describes a registered plugin.
*/
- interface IPluginRegistration extends IRegistration {
+ interface IPluginRegistration extends IRegistration {
+ /**
+ * Indicates if this plugin is currently set to be the auth provider
+ *
+ * @return {@code true} if the registered plugin is an {@link com.intergral.deep.agent.api.auth.IAuthProvider} and deep is configured to
+ * use this provider, else {@code false}
+ */
+ boolean isAuthProvider();
}
}
diff --git a/agent-api/src/main/java/com/intergral/deep/agent/api/resource/ConfigurationException.java b/agent-api/src/main/java/com/intergral/deep/agent/api/resource/ConfigurationException.java
index c4a2edd..c560a81 100644
--- a/agent-api/src/main/java/com/intergral/deep/agent/api/resource/ConfigurationException.java
+++ b/agent-api/src/main/java/com/intergral/deep/agent/api/resource/ConfigurationException.java
@@ -12,6 +12,8 @@ public final class ConfigurationException extends RuntimeException {
/**
* Create a new configuration exception with specified {@code message} and without a cause.
+ *
+ * @param message The exception message
*/
public ConfigurationException(String message) {
super(message);
@@ -19,6 +21,9 @@ public ConfigurationException(String message) {
/**
* Create a new configuration exception with specified {@code message} and {@code cause}.
+ *
+ * @param message The exception message
+ * @param cause The root cause of this exception
*/
public ConfigurationException(String message, Throwable cause) {
super(message, cause);
diff --git a/agent-api/src/main/java/com/intergral/deep/agent/api/settings/ISettings.java b/agent-api/src/main/java/com/intergral/deep/agent/api/settings/ISettings.java
index c071d03..8fa00c0 100644
--- a/agent-api/src/main/java/com/intergral/deep/agent/api/settings/ISettings.java
+++ b/agent-api/src/main/java/com/intergral/deep/agent/api/settings/ISettings.java
@@ -17,14 +17,43 @@
package com.intergral.deep.agent.api.settings;
+import com.intergral.deep.agent.api.plugin.IPlugin;
import com.intergral.deep.agent.api.resource.Resource;
import java.util.Map;
public interface ISettings {
+ /**
+ * This is the settings key for the configured auth provider
+ */
+ String KEY_AUTH_PROVIDER = "service.auth.provider";
+
+ /**
+ * This is the setting key for enabling or disabling deep.
+ */
+ String KEY_ENABLED = "enabled";
+
+ /**
+ * This is the setting key for the service url
+ */
+ String KEY_SERVICE_URL = "service.url";
+
T getSettingAs(String key, Class clazz);
Map getMap(String attributeProperty);
+ /**
+ * Returns the resource that describes this client
+ *
+ * @return the {@link Resource}
+ */
Resource getResource();
+
+ /**
+ * Look for a plugin with the given name or class name.
+ *
+ * @param name the plugin name or the plugin class name
+ * @return the {@link IPlugin} or {@code null}
+ */
+ IPlugin getPlugin(final String name);
}
diff --git a/agent-api/src/main/java/com/intergral/deep/agent/api/spi/Ordered.java b/agent-api/src/main/java/com/intergral/deep/agent/api/spi/Ordered.java
index afb1d4a..c1b8f74 100644
--- a/agent-api/src/main/java/com/intergral/deep/agent/api/spi/Ordered.java
+++ b/agent-api/src/main/java/com/intergral/deep/agent/api/spi/Ordered.java
@@ -16,6 +16,8 @@ public interface Ordered {
* Returns the order of applying the SPI implementing this interface. Higher values are applied
* later, for example: an SPI with order=1 will run after an SPI with order=0. SPI implementations
* with equal values will be run in a non-deterministic order.
+ *
+ * @return the order value
*/
default int order() {
return 0;
diff --git a/agent-api/src/main/java/com/intergral/deep/agent/api/tracepoint/ITracepoint.java b/agent-api/src/main/java/com/intergral/deep/agent/api/tracepoint/ITracepoint.java
index de79c61..4216275 100644
--- a/agent-api/src/main/java/com/intergral/deep/agent/api/tracepoint/ITracepoint.java
+++ b/agent-api/src/main/java/com/intergral/deep/agent/api/tracepoint/ITracepoint.java
@@ -41,7 +41,7 @@ default String id() {
return UUID.randomUUID().toString();
}
- interface ITracepointRegistration extends IRegistration {
+ interface ITracepointRegistration extends IRegistration {
}
}
diff --git a/agent/src/main/java/com/intergral/deep/agent/DeepAgent.java b/agent/src/main/java/com/intergral/deep/agent/DeepAgent.java
index 273e0a9..48126b1 100644
--- a/agent/src/main/java/com/intergral/deep/agent/DeepAgent.java
+++ b/agent/src/main/java/com/intergral/deep/agent/DeepAgent.java
@@ -19,9 +19,12 @@
import com.intergral.deep.agent.api.DeepVersion;
import com.intergral.deep.agent.api.IDeep;
+import com.intergral.deep.agent.api.auth.IAuthProvider;
import com.intergral.deep.agent.api.plugin.IPlugin;
import com.intergral.deep.agent.api.plugin.IPlugin.IPluginRegistration;
import com.intergral.deep.agent.api.resource.Resource;
+import com.intergral.deep.agent.api.settings.ISettings;
+import com.intergral.deep.agent.api.tracepoint.ITracepoint;
import com.intergral.deep.agent.api.tracepoint.ITracepoint.ITracepointRegistration;
import com.intergral.deep.agent.grpc.GrpcService;
import com.intergral.deep.agent.plugins.PluginLoader;
@@ -74,7 +77,29 @@ public String getVersion() {
public IPluginRegistration registerPlugin(final IPlugin plugin) {
this.settings.addPlugin(plugin);
- return () -> this.settings.removePlugin(plugin);
+ final boolean isAuthProvider;
+ if (plugin instanceof IAuthProvider) {
+ final String settingAs = this.settings.getSettingAs(ISettings.KEY_AUTH_PROVIDER, String.class);
+ isAuthProvider = settingAs != null && settingAs.equals(plugin.getClass().getName());
+ } else {
+ isAuthProvider = false;
+ }
+ return new IPluginRegistration() {
+ @Override
+ public boolean isAuthProvider() {
+ return isAuthProvider;
+ }
+
+ @Override
+ public void unregister() {
+ settings.removePlugin(plugin);
+ }
+
+ @Override
+ public IPlugin get() {
+ return plugin;
+ }
+ };
}
@Override
@@ -86,6 +111,62 @@ public ITracepointRegistration registerTracepoint(final String path, final int l
public ITracepointRegistration registerTracepoint(final String path, final int line, final Map args,
final Collection watches) {
final TracePointConfig tracePointConfig = this.tracepointConfig.addCustom(path, line, args, watches);
- return () -> this.tracepointConfig.removeCustom(tracePointConfig);
+ return new ITracepointRegistration() {
+ @Override
+ public void unregister() {
+ tracepointConfig.removeCustom(tracePointConfig);
+ }
+
+ @Override
+ public ITracepoint get() {
+ return new ITracepoint() {
+ @Override
+ public String path() {
+ return path;
+ }
+
+ @Override
+ public int line() {
+ return line;
+ }
+
+ @Override
+ public Map args() {
+ return args;
+ }
+
+ @Override
+ public Collection watches() {
+ return watches;
+ }
+
+ @Override
+ public String id() {
+ return tracePointConfig.getId();
+ }
+ };
+ }
+ };
+ }
+
+ @Override
+ public boolean isEnabled() {
+ return this.settings.getSettingAs(ISettings.KEY_ENABLED, Boolean.class);
+ }
+
+ @Override
+ public synchronized void setEnabled(final boolean enabled) {
+ // we are already the desired state - so do nothing
+ if (isEnabled() == enabled) {
+ return;
+ }
+
+ // update config to new state
+ this.settings.setActive(enabled);
+
+ // if we are disabling then we need to clear configs
+ if (!enabled) {
+ this.tracepointConfig.configUpdate(0, null, Collections.emptyList());
+ }
}
}
diff --git a/agent/src/main/java/com/intergral/deep/agent/poll/LongPollService.java b/agent/src/main/java/com/intergral/deep/agent/poll/LongPollService.java
index 560c7d7..bb7ed72 100644
--- a/agent/src/main/java/com/intergral/deep/agent/poll/LongPollService.java
+++ b/agent/src/main/java/com/intergral/deep/agent/poll/LongPollService.java
@@ -56,6 +56,11 @@ public void start(final ITracepointConfig tracepointConfig) {
@Override
public void run(long now) {
+ if (!this.settings.isActive()) {
+ // we have been disabled so skip this poll
+ // we will pause like normal and try again later
+ return;
+ }
final PollConfigGrpc.PollConfigBlockingStub blockingStub = this.grpcService.pollService();
final PollRequest.Builder builder = PollRequest.newBuilder();
@@ -69,7 +74,10 @@ public void run(long now) {
.build();
final PollResponse response = blockingStub.poll(pollRequest);
-
+ // check we are still active
+ if (!this.settings.isActive()) {
+ return;
+ }
if (response.getResponseType() == ResponseType.NO_CHANGE) {
this.tracepointConfig.noChange(response.getTsNanos());
} else {
diff --git a/agent/src/main/java/com/intergral/deep/agent/settings/Settings.java b/agent/src/main/java/com/intergral/deep/agent/settings/Settings.java
index ca35824..90e7866 100644
--- a/agent/src/main/java/com/intergral/deep/agent/settings/Settings.java
+++ b/agent/src/main/java/com/intergral/deep/agent/settings/Settings.java
@@ -35,11 +35,13 @@
import java.util.Map;
import java.util.Optional;
import java.util.Properties;
+import java.util.concurrent.atomic.AtomicBoolean;
import java.util.logging.Level;
import java.util.regex.Pattern;
public class Settings implements ISettings {
+ private static final AtomicBoolean IS_ACTIVE = new AtomicBoolean(true);
private final Properties properties;
private Resource resource;
private Collection plugins;
@@ -62,6 +64,12 @@ public static Settings build(final Map agentArgs) {
propertiesStream = Settings.class.getResourceAsStream("/deep_settings.properties");
}
+ // we have special handling for is active as it is the only value that we allow to change during run time.
+ final String isActive = readProperty(ISettings.KEY_ENABLED, agentArgs);
+ if (isActive != null && !Boolean.parseBoolean(isActive)) {
+ IS_ACTIVE.set(false);
+ }
+
return build(agentArgs, propertiesStream);
}
@@ -178,6 +186,10 @@ private static List makeList(final String str) {
}
public T getSettingAs(String key, Class clazz) {
+ // special handling for enabled key
+ if(key.equals(ISettings.KEY_ENABLED)){
+ return coerc(String.valueOf(isActive()), clazz);
+ }
final String property = this.properties.getProperty(key);
if (property != null) {
@@ -206,33 +218,33 @@ public Map getMap(String key) {
}
public String getServiceHost() {
- final String serviceUrl = getSettingAs("service.url", String.class);
+ final String serviceUrl = getSettingAs(ISettings.KEY_SERVICE_URL, String.class);
if (serviceUrl.contains("://")) {
try {
return new URL(serviceUrl).getHost();
} catch (MalformedURLException e) {
- throw new InvalidConfigException("service.url", serviceUrl, e);
+ throw new InvalidConfigException(ISettings.KEY_SERVICE_URL, serviceUrl, e);
}
} else if (serviceUrl.contains(":")) {
return serviceUrl.split(":")[0];
}
- throw new InvalidConfigException("service.url", serviceUrl);
+ throw new InvalidConfigException(ISettings.KEY_SERVICE_URL, serviceUrl);
}
public int getServicePort() {
- final String serviceUrl = getSettingAs("service.url", String.class);
+ final String serviceUrl = getSettingAs(ISettings.KEY_SERVICE_URL, String.class);
if (serviceUrl.contains("://")) {
try {
return new URL(serviceUrl).getPort();
} catch (MalformedURLException e) {
- throw new InvalidConfigException("service.url", serviceUrl, e);
+ throw new InvalidConfigException(ISettings.KEY_SERVICE_URL, serviceUrl, e);
}
} else if (serviceUrl.contains(":")) {
return Integer.parseInt(serviceUrl.split(":")[1]);
}
- throw new InvalidConfigException("service.url", serviceUrl);
+ throw new InvalidConfigException(ISettings.KEY_SERVICE_URL, serviceUrl);
}
@Override
@@ -274,6 +286,25 @@ public void removePlugin(final IPlugin plugin) {
this.plugins.removeIf(iPlugin -> iPlugin.name().equals(plugin.name()));
}
+ @Override
+ public IPlugin getPlugin(final String name) {
+ final Collection allPlugins = this.getPlugins();
+ for (IPlugin plugin : allPlugins) {
+ if (plugin.name().equals(name) || plugin.getClass().getName().equals(name)) {
+ return plugin;
+ }
+ }
+ return null;
+ }
+
+ public boolean isActive() {
+ return IS_ACTIVE.get();
+ }
+
+ public void setActive(boolean state) {
+ IS_ACTIVE.set(state);
+ }
+
public static class InvalidConfigException extends RuntimeException {
public InvalidConfigException(String key, String value) {
diff --git a/examples/dynamic-load/src/main/java/com/intergral/deep/examples/Main.java b/examples/dynamic-load/src/main/java/com/intergral/deep/examples/Main.java
index e4d5e85..a336fec 100644
--- a/examples/dynamic-load/src/main/java/com/intergral/deep/examples/Main.java
+++ b/examples/dynamic-load/src/main/java/com/intergral/deep/examples/Main.java
@@ -23,6 +23,7 @@
import com.intergral.deep.agent.api.IDeep;
import com.intergral.deep.agent.api.reflection.IReflection;
import com.intergral.deep.agent.api.resource.Resource;
+import com.intergral.deep.agent.api.settings.ISettings;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Collections;
@@ -51,7 +52,7 @@ public static void main(String[] args) throws Throwable {
// Dynamically configure and attach the deep agent
Deep.config()
.setJarPath(jarPath.toAbsolutePath().toString())
- .setValue("service.url", "localhost:43315")
+ .setValue(ISettings.KEY_SERVICE_URL, "localhost:43315")
.setValue("service.secure", false)
.start();