Skip to content
This repository has been archived by the owner on Aug 30, 2023. It is now read-only.

WIP: load SentryOptions from external sources. #536

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package io.sentry.core.config.location;

import org.jetbrains.annotations.Nullable;

import java.util.Arrays;
import java.util.Collection;

/**
* Wraps multiple resource locators and returns the first non-null configuration file path.
*/
public final class CompoundResourceLocator implements ConfigurationResourceLocator {
private final Collection<ConfigurationResourceLocator> locators;

/**
* Instantiates a new compound configuration resource locator.
* @param locators the locators to iterate through
*/
public CompoundResourceLocator(ConfigurationResourceLocator ... locators) {
this.locators = Arrays.asList(locators);
}

/**
* Tries to find the location of the resource containing the Sentry configuration file.
*
* @return the first non-null configuration file path (in the iteration order of the collection provided to
* the constructor) or null if none such exists.
*/
@Override
@Nullable
public String getConfigurationResourcePath() {
for (ConfigurationResourceLocator l : locators) {
String path = l.getConfigurationResourcePath();
if (path != null) {
return path;
}
}

return null;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package io.sentry.core.config.location;

import org.jetbrains.annotations.Nullable;

/**
* Tries to find the Sentry configuration file.
*/
public interface ConfigurationResourceLocator {
/**
* Tries to find the location of the resource containing the Sentry configuration file.
*
* @return the location on which some {@link io.sentry.core.config.provider.resource.ResourceLoader} can find the configuration file or null if this
* locator could not find any.
*/
@Nullable
String getConfigurationResourcePath();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package io.sentry.core.config.location;

import org.jetbrains.annotations.Nullable;

/**
* Tries to find the location of the Sentry configuration file in some environment variable.
*/
public final class EnvironmentBasedLocator implements ConfigurationResourceLocator {
/**
* The default environment variable to use for obtaining the location of the Sentry configuration file.
*/
public static final String DEFAULT_ENV_VAR_NAME = "SENTRY_PROPERTIES_FILE";

private final String envVarName;

/**
* Constructs a new instance that will use the environment variable defined in {@link #DEFAULT_ENV_VAR_NAME}.
*/
public EnvironmentBasedLocator() {
this(DEFAULT_ENV_VAR_NAME);
}

/**
* Constructs a new instance that will use the provided environment variable.
*
* @param envVarName the name of the environment variable to use
*/
public EnvironmentBasedLocator(String envVarName) {
this.envVarName = envVarName;
}

@Override
@Nullable
public String getConfigurationResourcePath() {
return System.getenv(envVarName);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package io.sentry.core.config.location;

/**
* Provides the configuration file location using a file name provided at instantiation time.
*/
public final class StaticFileLocator implements ConfigurationResourceLocator {
/**
* The default file name of the Sentry configuration file.
*/
public static final String DEFAULT_FILE_PATH = "sentry.properties";

private final String path;

/**
* Constructs a new instance using the {@link #DEFAULT_FILE_PATH}.
*/
public StaticFileLocator() {
this(DEFAULT_FILE_PATH);
}

/**
* Constructs a new instance that will return the provided path as the path of the Sentry configuration.
*
* @param filePath the path to the Sentry configuration
*/
public StaticFileLocator(String filePath) {
this.path = filePath;
}

@Override
public String getConfigurationResourcePath() {
return path;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package io.sentry.core.config.location;

import org.jetbrains.annotations.Nullable;

/**
* Tries to find the location of the Sentry configuration file using the value of some system property.
*/
public final class SystemPropertiesBasedLocator implements ConfigurationResourceLocator {
/**
* The default system property to use for obtaining the location of the Sentry configuration file.
*/
public static final String DEFAULT_PROPERTY_NAME = "sentry.properties.file";

private final String propertyName;

/**
* Constructs a new instance that will use the {@link #DEFAULT_PROPERTY_NAME}.
*/
public SystemPropertiesBasedLocator() {
this(DEFAULT_PROPERTY_NAME);
}

/**
* Constructs a new instance that will use the provided system property name.
*
* @param propertyName the name of the property to load the location of the Sentry configuration file from
*/
public SystemPropertiesBasedLocator(String propertyName) {
this.propertyName = propertyName;
}

@Override
@Nullable
public String getConfigurationResourcePath() {
return System.getProperty(propertyName);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package io.sentry.core.config.provider;

import org.jetbrains.annotations.Nullable;

import java.util.Collection;

/**
* Wraps a couple of other configuration providers to act as one, returning the first non-null value for given
* configuration key, in the iteration order of the wrapped providers.
*/
final class CompoundConfigurationProvider implements ConfigurationProvider {
private final Collection<ConfigurationProvider> providers;

/**
* Instantiates the new compound provider by wrapping the provided collection of providers.
* @param providers the providers to wrap
*/
public CompoundConfigurationProvider(Collection<ConfigurationProvider> providers) {
this.providers = providers;
}

@Nullable
@Override
public String getProperty(String key) {
for (ConfigurationProvider p : providers) {
String val = p.getProperty(key);
if (val != null) {
return val;
}
}

return null;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package io.sentry.core.config.provider;

import org.jetbrains.annotations.Nullable;

/**
* Sentry is able to load configuration from various sources. This interface is implemented by each one of them.
*/
public interface ConfigurationProvider {

/**
* Returns the value of the configuration property with the provided key.
*
* @param key the name of the configuration property
* @return the value of the property as found in this provider or null if none found
*/
@Nullable
String getProperty(String key);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package io.sentry.core.config.provider;

import org.jetbrains.annotations.Nullable;

/**
* Tries to find the configuration properties in the environment.
*/
final class EnvironmentConfigurationProvider implements ConfigurationProvider {
/**
* The prefix of the environment variables holding Sentry configuration.
*/
public static final String ENV_VAR_PREFIX = "SENTRY_";

// private static final Logger logger = LoggerFactory.getLogger(EnvironmentConfigurationProvider.class);

@Nullable
@Override
public String getProperty(String key) {
String ret = System.getenv(ENV_VAR_PREFIX + key.replace(".", "_").toUpperCase());

if (ret != null) {
// logger.debug("Found {}={} in System Environment Variables.", key, ret);
}

return ret;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
package io.sentry.core.config.provider;


import io.sentry.core.ILogger;
import io.sentry.core.SentryLevel;

import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.naming.NoInitialContextException;
Comment on lines +7 to +10
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this cant live in the sentry-core package.
Android SDK has a subset of the Java SDK, javax is not one of them


/**
* A configuration provider that looks up the configuration in the JNDI registry.
*
* @see JndiSupport to check whether Jndi is available in the current JVM without needing to load this class
*/
final class JndiConfigurationProvider implements ConfigurationProvider {
/**
* The default prefix of the JNDI names of Sentry configuration. This, concatenated with the configuration key name
* provided to the {@link #getProperty(String)} method, must form a valid JNDI name.
*/
public static final String DEFAULT_JNDI_PREFIX = "java:comp/env/sentry/";

private final String prefix;
private final JndiContextProvider contextProvider;
private final ILogger logger;

/**
* Constructs a new instance using the {@link #DEFAULT_JNDI_PREFIX} and {@link JndiContextProvider} that returns
* a new {@link InitialContext} each time it is asked for a context. This ensures that any changes in the JNDI
* environment are taken into account on the next configuration property lookup.
*/
public JndiConfigurationProvider(ILogger logger) {
this(DEFAULT_JNDI_PREFIX, new JndiContextProvider() {
@Override
public Context getContext() throws NamingException {
return new InitialContext();
}
}, logger);
}

/**
* Constructs a new instance that will look up the Sentry configuration keys using the provided prefix and using
* the context obtained from the JNDI context provider. Because the JNDI environment is dynamic, Sentry uses the
* JNDI context provider to obtain a fresh copy of the environment (if the provider chooses to return such).
* @param jndiNamePrefix The prefix of the JNDI names of Sentry configuration. This, concatenated with
* the configuration key name provided to the {@link #getProperty(String)} method, must form
* a valid JNDI name.
* @param contextProvider an object able to provide instances of the JNDI context
* @param logger the logger
*/
public JndiConfigurationProvider(String jndiNamePrefix, JndiContextProvider contextProvider, ILogger logger) {
this.prefix = jndiNamePrefix;
this.contextProvider = contextProvider;
this.logger = logger;
}

@Override
public String getProperty(String key) {
String value = null;
try {
Context ctx = contextProvider.getContext();
value = (String) ctx.lookup(prefix + key);
} catch (NoInitialContextException e) {
logger.log(SentryLevel.DEBUG, "JNDI not configured for Sentry (NoInitialContextEx)");
} catch (NamingException e) {
logger.log(SentryLevel.DEBUG, "No " + prefix + key + " in JNDI");
} catch (RuntimeException e) {
logger.log(SentryLevel.WARNING, "Odd RuntimeException while testing for JNDI", e);
}
return value;
}

/**
* A helper interface to be able to obtain application-specific JNDI context during Sentry configuration lookup.
*/
public interface JndiContextProvider {
/**
* Returns the context to use when looking for a property in JNDI. Note that it is supposed that this method
* returns a new context each time, but that might not be required or even correct depending on that
* requirements of the supplier of the implementation of this interface.
*
* @return a possibly new instance of the JNDI context
* @throws NamingException on JNDI error
*/
Context getContext() throws NamingException;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package io.sentry.core.config.provider;


/**
* A helper class to check whether JNDI is available in the current JVM.
*/
final class JndiSupport {
// private static final Logger logger = LoggerFactory.getLogger(JndiSupport.class);

private JndiSupport() {
}

/**
* Checks whether JNDI is available.
* @return true if JNDI is available, false otherwise
*/
public static boolean isAvailable() {
try {
Class.forName("javax.naming.InitialContext", false, JndiSupport.class.getClassLoader());
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This isnt' very Android friendly.
We'd need a clear way to init without going through all these Java ways

Copy link
Contributor

@marandaneto marandaneto Sep 2, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd say that none of the providers should be "executed" on Android, but just its own AndroidProvider.

it doesnt make sense to try to read system envs, locate the file etc if the only thing that makes sense for android is its own provider.
ideally, the options have a list of providers, and its empty by default, so depending on the integration, they add their own providers that make sense, something like that.
eg sentry-spring would have a system env, file system provider etc...
sentry-android would have only its own provider, that reads from the manifest

we'll also need a new module/package like io.sentry.config that has non android related things, I dont want customers needing to add extra rules to not fail their build because of unknown classes, this happened with the older version of the SDK and a lot of people complained.

return true;
} catch (ClassNotFoundException | NoClassDefFoundError e) {
// logger.trace("JNDI is not available: " + e.getMessage());
return false;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package io.sentry.core.config.provider;

import io.sentry.core.ILogger;
import io.sentry.core.config.provider.resource.ResourceLoader;
import io.sentry.core.config.location.ConfigurationResourceLocator;
import io.sentry.core.config.provider.resource.ResourceLoaderConfigurationProvider;
import org.jetbrains.annotations.NotNull;

import java.io.IOException;
import java.nio.charset.Charset;

/**
* Similar to {@link ResourceLoaderConfigurationProvider} but uses a {@link ConfigurationResourceLocator} to find
* the path to the configuration file.
*/
final class LocatorBasedConfigurationProvider extends ResourceLoaderConfigurationProvider {
/**
* Instantiates a new configuration provider using the parameters.
*
* @param rl the resource loader to load the contents of the configuration file with
* @param locator the locator to find the configuration file with
* @param charset the charset of the configuration file
* @param logger the logger
* @throws IOException on failure to process the configuration file
*/
public LocatorBasedConfigurationProvider(ResourceLoader rl, ConfigurationResourceLocator locator, Charset charset, @NotNull ILogger logger)
throws IOException {
super(rl, locator.getConfigurationResourcePath(), charset, logger);
}
}
Loading