Skip to content
Open
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
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,10 @@

## Unreleased

### Internal
### Performance

- Probe class availability without initializing the class during SDK init ([#5635](https://github.com/getsentry/sentry-java/pull/5635))
- Cache reflective class lookups and avoid double-probing during SDK init ([#5636](https://github.com/getsentry/sentry-java/pull/5636))

## 8.45.0

Expand Down
33 changes: 16 additions & 17 deletions sentry/src/main/java/io/sentry/ScopesStorageFactory.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,24 +23,23 @@ public final class ScopesStorageFactory {
private static @NotNull IScopesStorage createInternal(
final @NotNull LoadClass loadClass, final @NotNull ILogger logger) {
if (Platform.isJvm()) {
if (loadClass.isClassAvailable(OTEL_SCOPES_STORAGE, logger)) {
Class<?> otelScopesStorageClazz = loadClass.loadClass(OTEL_SCOPES_STORAGE, logger);
if (otelScopesStorageClazz != null) {
try {
final @Nullable Object otelScopesStorage =
otelScopesStorageClazz.getDeclaredConstructor().newInstance();
if (otelScopesStorage instanceof IScopesStorage) {
return (IScopesStorage) otelScopesStorage;
}
} catch (InstantiationException e) {
// TODO log
} catch (IllegalAccessException e) {
// TODO log
} catch (InvocationTargetException e) {
// TODO log
} catch (NoSuchMethodException e) {
// TODO log
final @Nullable Class<?> otelScopesStorageClazz =
loadClass.loadClass(OTEL_SCOPES_STORAGE, logger);
if (otelScopesStorageClazz != null) {
try {
final @Nullable Object otelScopesStorage =
otelScopesStorageClazz.getDeclaredConstructor().newInstance();
if (otelScopesStorage instanceof IScopesStorage) {
return (IScopesStorage) otelScopesStorage;
}
} catch (InstantiationException e) {
// TODO log
} catch (IllegalAccessException e) {
// TODO log
} catch (InvocationTargetException e) {
// TODO log
} catch (NoSuchMethodException e) {
// TODO log
}
}
}
Expand Down
33 changes: 16 additions & 17 deletions sentry/src/main/java/io/sentry/SpanFactoryFactory.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,24 +15,23 @@ public final class SpanFactoryFactory {
public static @NotNull ISpanFactory create(
final @NotNull LoadClass loadClass, final @NotNull ILogger logger) {
if (Platform.isJvm()) {
if (loadClass.isClassAvailable(OTEL_SPAN_FACTORY, logger)) {
Class<?> otelSpanFactoryClazz = loadClass.loadClass(OTEL_SPAN_FACTORY, logger);
if (otelSpanFactoryClazz != null) {
try {
final @Nullable Object otelSpanFactory =
otelSpanFactoryClazz.getDeclaredConstructor().newInstance();
if (otelSpanFactory instanceof ISpanFactory) {
return (ISpanFactory) otelSpanFactory;
}
} catch (InstantiationException e) {
// TODO log
} catch (IllegalAccessException e) {
// TODO log
} catch (InvocationTargetException e) {
// TODO log
} catch (NoSuchMethodException e) {
// TODO log
final @Nullable Class<?> otelSpanFactoryClazz =
loadClass.loadClass(OTEL_SPAN_FACTORY, logger);
if (otelSpanFactoryClazz != null) {
try {
final @Nullable Object otelSpanFactory =
otelSpanFactoryClazz.getDeclaredConstructor().newInstance();
if (otelSpanFactory instanceof ISpanFactory) {
return (ISpanFactory) otelSpanFactory;
}
} catch (InstantiationException e) {
// TODO log
} catch (IllegalAccessException e) {
// TODO log
} catch (InvocationTargetException e) {
// TODO log
} catch (NoSuchMethodException e) {
// TODO log
}
}
}
Expand Down
21 changes: 20 additions & 1 deletion sentry/src/main/java/io/sentry/util/LoadClass.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,25 @@
import io.sentry.ILogger;
import io.sentry.SentryLevel;
import io.sentry.SentryOptions;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

/** An Adapter for making Class.forName testable */
@Open
public class LoadClass {

/** Sentinel cached for class names that are known to be unavailable. */
private static final Object NOT_AVAILABLE = new Object();

/**
* Whether a class is on the classpath does not change during the lifetime of the process, so
* results are cached to avoid repeated {@link Class#forName} lookups (and the exceptions they
* throw for absent classes) when the same class is probed more than once.
*/
private static final Map<String, Object> CLASSES = new ConcurrentHashMap<>();

/**
* Try to load a class via reflection
*
Expand All @@ -19,11 +31,18 @@ public class LoadClass {
* @return a Class&lt;?&gt; if it's available, or null
*/
public @Nullable Class<?> loadClass(final @NotNull String clazz, final @Nullable ILogger logger) {
final @Nullable Object cached = CLASSES.get(clazz);
if (cached != null) {
return cached == NOT_AVAILABLE ? null : (Class<?>) cached;
}
try {
// Don't initialize the class just to probe for availability; it gets initialized lazily on
// first use. This avoids running unrelated static initializers during SDK init.
return Class.forName(clazz, false, LoadClass.class.getClassLoader());
final Class<?> loadedClass = Class.forName(clazz, false, LoadClass.class.getClassLoader());
CLASSES.put(clazz, loadedClass);
return loadedClass;
} catch (ClassNotFoundException e) {
CLASSES.put(clazz, NOT_AVAILABLE);
if (logger != null) {
logger.log(SentryLevel.INFO, "Class not available: " + clazz);
}
Expand Down
Loading