diff --git a/.buildkite/pipeline.yml b/.buildkite/pipeline.yml
index 75d6dbb2..36a73f20 100644
--- a/.buildkite/pipeline.yml
+++ b/.buildkite/pipeline.yml
@@ -22,32 +22,6 @@ steps:
run: java-common
command: './gradlew check test'
- - label: ':docker: Mazerunner java8 tests batch 1'
- key: 'java-mazerunner-tests-1'
- depends_on: 'java-jvm-build'
- timeout_in_minutes: 30
- plugins:
- - docker-compose#v3.7.0:
- run: java8-mazerunner
- - artifacts#v1.9.0:
- download: "maven-repository.zip"
- command:
- - 'features/scripts/assemble-fixtures.sh'
- - 'bundle exec maze-runner --exclude=features/[^a-m].*.feature'
-
- - label: ':docker: Mazerunner java8 tests batch 2'
- key: 'java-mazerunner-tests-2'
- depends_on: 'java-jvm-build'
- timeout_in_minutes: 30
- plugins:
- - docker-compose#v3.7.0:
- run: java8-mazerunner
- - artifacts#v1.9.0:
- download: "maven-repository.zip"
- command:
- - 'features/scripts/assemble-fixtures.sh'
- - 'bundle exec maze-runner --exclude=features/[^n-z].*.feature'
-
- label: ':docker: Mazerunner java17 tests batch 1'
key: 'java-mazerunner-tests-3'
depends_on: 'java-jvm-build'
@@ -57,6 +31,7 @@ steps:
run: java17-mazerunner
- artifacts#v1.9.0:
download: "maven-repository.zip"
+ upload: "maze_output/maze_output.zip"
command:
- 'features/scripts/assemble-fixtures.sh'
- 'bundle exec maze-runner --exclude=features/[^a-m].*.feature'
@@ -70,6 +45,7 @@ steps:
run: java17-mazerunner
- artifacts#v1.9.0:
download: "maven-repository.zip"
+ upload: "maze_output/maze_output.zip"
command:
- 'features/scripts/assemble-fixtures.sh'
- 'bundle exec maze-runner --exclude=features/[^n-z].*.feature'
diff --git a/Gemfile b/Gemfile
index 85813553..1dcbd4b1 100644
--- a/Gemfile
+++ b/Gemfile
@@ -2,3 +2,5 @@ source 'https://rubygems.org'
gem 'bugsnag-maze-runner', '~>9.0'
gem 'os'
+# Pin power_assert to avoid compatibility issues with test-unit
+gem 'power_assert', '< 3.0.0'
diff --git a/bugsnag-spring/build.gradle b/bugsnag-spring/build.gradle
deleted file mode 100644
index 379cc762..00000000
--- a/bugsnag-spring/build.gradle
+++ /dev/null
@@ -1,204 +0,0 @@
-ext {
- jakartaSpringVersion = '6.0.0'
- jakartaSpringBootVersion = '3.0.0'
-
- javaxSpringVersion = '5.3.20'
- javaxSpringBootVersion = '2.5.14'
-}
-
-apply plugin: 'java-library'
-
-repositories {
- mavenCentral()
-}
-
-java {
- withJavadocJar()
- toolchain {
- languageVersion.set(JavaLanguageVersion.of(8))
- }
-}
-
-// We use 3 custom configurations and matching sourceSets to avoid the standard behaviours which interfere with
-// our compilation structure
-configurations {
- compileCommon
- compileJavax
- compileJakarta
-}
-
-sourceSets {
- common {
- java
- // common.compileClasspath includes the compileJavax classpath in order to give the common classes access
- // to Spring without confusing the dependencies, including it here ensures that any use of javax.* packages
- // in src/common will result in test failures (although not compile failures)
- // we need the javax.* packages included here so that the compiler can type-check all the way up the hierarchy
- // for things like "ServletRequestBindingException extends ServletException"
- compileClasspath += configurations.compileCommon + configurations.compileJavax
- }
- javax {
- java
- compileClasspath += configurations.compileJavax + common.output + configurations.compileCommon
- }
- jakarta {
- java
- compileClasspath += configurations.compileJakarta + common.output + configurations.compileCommon
- }
-}
-
-// test sourceSets
-sourceSets {
- commonTest {
- java
- }
- javaxTest {
- java
- compileClasspath += javax.output + javax.compileClasspath + commonTest.output + commonTest.runtimeClasspath + common.output
- runtimeClasspath = compileClasspath
- }
- jakartaTest {
- java
- compileClasspath += jakarta.output + jakarta.compileClasspath + commonTest.output + commonTest.runtimeClasspath + common.output
- runtimeClasspath = compileClasspath
- }
-}
-
-tasks.register('sourceJar', Jar).configure {
- from(
- sourceSets.common.allJava,
- sourceSets.javax.allJava,
- sourceSets.jakarta.allJava
- )
-}
-
-// do not move this higher - sourceJar must be registered before we apply common.gradle
-apply from: '../common.gradle'
-
-compileJava.dependsOn(compileCommonJava, compileJavaxJava, compileJakartaJava)
-
-// Separated Javax / Jakarta tests -------------------------------------------------------------------------------------
-
-compileJakartaJava {
- // set the compiler for the `jakarta` sourceSet to Java17
- javaCompiler.set(
- javaToolchains.compilerFor {
- languageVersion = JavaLanguageVersion.of(17)
- }
- )
-}
-
-compileJakartaTestJava {
- // set the compiler for the `jakartaTest` sourceSet to Java17
- javaCompiler.set(
- javaToolchains.compilerFor {
- languageVersion = JavaLanguageVersion.of(17)
- }
- )
-}
-
-testClasses.dependsOn(javaxTestClasses, jakartaTestClasses)
-
-tasks.register('testJakarta', Test) {
- testClassesDirs = sourceSets.jakartaTest.output.classesDirs
- classpath = sourceSets.jakartaTest.output.classesDirs + sourceSets.jakartaTest.runtimeClasspath
- javaLauncher.set(
- javaToolchains.launcherFor {
- languageVersion = JavaLanguageVersion.of(17)
- }
- )
-
- dependsOn(jakartaTestClasses)
-}
-
-tasks.register('testJavax', Test) {
- testClassesDirs = sourceSets.javaxTest.output.classesDirs
- classpath = sourceSets.javaxTest.output.classesDirs + sourceSets.javaxTest.runtimeClasspath
- javaLauncher.set(
- javaToolchains.launcherFor {
- languageVersion = JavaLanguageVersion.of(8)
- }
- )
-
- dependsOn(javaxTestClasses)
-}
-
-test.dependsOn(testJakarta, testJavax)
-
-dependencies {
- compileCommon project(':bugsnag')
-
- compileCommon "ch.qos.logback:logback-core:${logbackVersion}"
- compileCommon "org.slf4j:slf4j-api:${slf4jApiVersion}"
-
- compileJavax "javax.servlet:javax.servlet-api:${javaxServletApiVersion}"
- compileJavax "org.springframework:spring-webmvc:${javaxSpringVersion}"
- compileJavax "org.springframework.boot:spring-boot:${javaxSpringBootVersion}"
- compileJavax "org.springframework:spring-aop:${javaxSpringVersion}"
-
- compileJakarta "jakarta.servlet:jakarta.servlet-api:${jakartaServletApiVersion}"
- compileJakarta "org.springframework:spring-webmvc:${jakartaSpringVersion}"
- compileJakarta "org.springframework.boot:spring-boot:${jakartaSpringBootVersion}"
- compileJakarta "org.springframework:spring-aop:${jakartaSpringVersion}"
-
-
- commonTestImplementation project(':bugsnag').sourceSets.test.output
- commonTestImplementation project(':bugsnag')
-
- commonTestImplementation "junit:junit:${junitVersion}"
-
- commonTestCompileOnly "org.mockito:mockito-core:2.10.0"
-
- jakartaTestImplementation "org.mockito:mockito-core:${mockitoVersion}"
- jakartaTestImplementation "jakarta.servlet:jakarta.servlet-api:${jakartaServletApiVersion}"
- jakartaTestImplementation "org.springframework.boot:spring-boot-starter-test:${jakartaSpringBootVersion}"
- jakartaTestImplementation "org.springframework.boot:spring-boot-starter-web:${jakartaSpringBootVersion}"
- jakartaTestImplementation "org.springframework:spring-aop:${jakartaSpringVersion}"
-
- javaxTestImplementation "org.mockito:mockito-core:2.10.0"
- javaxTestImplementation "javax.servlet:javax.servlet-api:${javaxServletApiVersion}"
- javaxTestImplementation "org.springframework.boot:spring-boot-starter-test:${javaxSpringBootVersion}"
- javaxTestImplementation "org.springframework.boot:spring-boot-starter-web:${javaxSpringBootVersion}"
- javaxTestImplementation "org.springframework:spring-aop:${javaxSpringVersion}"
-}
-
-dependencies {
- // ensure that the published .pom file includes a dependency on com.bugsnag:bugsnag
- // this 'api' dependency has no impact on the compilation of this projects source,
- // so we keep it in it's own dependencies block
- api project(':bugsnag')
-}
-
-// here is where we merge all of the class outputs into the default classes directory doing this as its own step avoids
-// circular dependencies between the sourceSets and their output directories (if they all use 'main.output.classesDirs'
-// then jakarta+javax both depend on it as well)
-tasks.register('mergeClasses', Copy) {
- from sourceSets.common.output.classesDirs
- from sourceSets.jakarta.output.classesDirs
- from sourceSets.javax.output.classesDirs
-
- into sourceSets.main.output.classesDirs.asPath
-}
-
-classes.dependsOn('mergeClasses')
-
-if (project.hasProperty('releasing')) {
- publishing {
- publications {
- Publication(MavenPublication) {
- // this is vital: we use the version details from the "javax" side of the project
- // this ensures that Gradle considers that as the baseline set of required versions
- // allowing old projects that still use Java8 and the javax.servlet packages to use
- // bugsnag-java without any secondary artifacts
- versionMapping {
- usage('java-api') {
- fromResolutionOf('compileCommon')
- }
- usage('java-runtime') {
- fromResolutionOf('compileCommon')
- }
- }
- }
- }
- }
-}
diff --git a/bugsnag-spring/build.gradle.kts b/bugsnag-spring/build.gradle.kts
new file mode 100644
index 00000000..89b66137
--- /dev/null
+++ b/bugsnag-spring/build.gradle.kts
@@ -0,0 +1,50 @@
+plugins {
+ `java-library`
+}
+
+repositories {
+ mavenCentral()
+}
+
+java {
+ withJavadocJar()
+ toolchain {
+ languageVersion.set(JavaLanguageVersion.of(17))
+ }
+}
+
+val sourceJar by tasks.registering(Jar::class) {
+ from(sourceSets.main.get().allJava)
+}
+
+// do not move this higher - sourceJar must be registered before we apply common.gradle.kts
+apply(from = "../common.gradle.kts")
+
+dependencies {
+ implementation(project(":bugsnag"))
+
+ implementation(libs.logback.core)
+ implementation(libs.slf4j.api)
+
+ implementation(libs.jakarta.servlet.api)
+ implementation(libs.spring.webmvc)
+ implementation(libs.springBoot3.boot)
+ implementation(libs.spring.aop)
+
+ testImplementation(project(":bugsnag").dependencyProject.sourceSets["test"].output)
+ testImplementation(project(":bugsnag"))
+ testImplementation(libs.junit)
+ testImplementation(libs.springBoot3.starter.test)
+ testImplementation(libs.springBoot3.starter.web)
+ testImplementation(libs.junit.jupiter)
+ testCompileOnly(libs.mockito.core.legacy)
+}
+
+/** ---- Publishing config ----
+ * Pulls in publishing+signing rules from the shared release.gradle.kts.
+ * This will create tasks like:
+ * :bugsnag:publishMavenJavaPublicationToTestRepository
+ * :bugsnag:publishMavenJavaPublicationToOssrhStagingRepository
+ */
+apply(from = "${rootProject.projectDir}/release.gradle.kts")
+
diff --git a/bugsnag-spring/javax/build.gradle b/bugsnag-spring/javax/build.gradle
deleted file mode 100644
index edb7f763..00000000
--- a/bugsnag-spring/javax/build.gradle
+++ /dev/null
@@ -1,41 +0,0 @@
-ext {
- springVersion = '5.3.20'
- springBootVersion = '2.5.14'
-}
-
-apply plugin: 'java'
-apply plugin: 'java-library'
-
-apply from: '../../common.gradle'
-
-java {
- toolchain {
- languageVersion = JavaLanguageVersion.of(8)
- }
-}
-
-compileJava {
- sourceCompatibility = '1.8'
- targetCompatibility = '1.8'
-}
-
-repositories {
- mavenCentral()
-}
-
-dependencies {
- api project(':bugsnag')
- testImplementation project(':bugsnag').sourceSets.test.output
-
- compileOnly "javax.servlet:javax.servlet-api:${javaxServletApiVersion}"
- compileOnly "org.springframework:spring-webmvc:${springVersion}"
- compileOnly "org.springframework.boot:spring-boot:${springBootVersion}"
- compileOnly "ch.qos.logback:logback-core:${logbackVersion}"
- compileOnly "org.slf4j:slf4j-api:${slf4jApiVersion}"
-
- testImplementation "junit:junit:${junitVersion}"
- testImplementation "javax.servlet:javax.servlet-api:${javaxServletApiVersion}"
- testImplementation "org.springframework.boot:spring-boot-starter-test:${springBootVersion}"
- testImplementation "org.springframework.boot:spring-boot-starter-web:${springBootVersion}"
- testImplementation "org.mockito:mockito-core:${mockitoVersion}"
-}
\ No newline at end of file
diff --git a/bugsnag-spring/javax/src/test/resources/logback.xml b/bugsnag-spring/javax/src/test/resources/logback.xml
deleted file mode 100644
index 073f1525..00000000
--- a/bugsnag-spring/javax/src/test/resources/logback.xml
+++ /dev/null
@@ -1,13 +0,0 @@
-
-
-
-
-
- apiKey
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/bugsnag-spring/src/common/java/com/bugsnag/BugsnagImportSelector.java b/bugsnag-spring/src/common/java/com/bugsnag/BugsnagImportSelector.java
deleted file mode 100644
index 36f10e1a..00000000
--- a/bugsnag-spring/src/common/java/com/bugsnag/BugsnagImportSelector.java
+++ /dev/null
@@ -1,57 +0,0 @@
-package com.bugsnag;
-
-import org.springframework.context.annotation.ImportSelector;
-import org.springframework.core.SpringVersion;
-import org.springframework.core.type.AnnotationMetadata;
-
-public class BugsnagImportSelector implements ImportSelector {
-
- private static final String[] SPRING_JAKARTA_CLASSES = {
- "com.bugsnag.SpringBootJakartaConfiguration",
- "com.bugsnag.JakartaMvcConfiguration",
- "com.bugsnag.ScheduledTaskConfiguration"
- };
-
- private static final String[] SPRING_JAVAX_CLASSES = {
- "com.bugsnag.SpringBootJavaxConfiguration",
- "com.bugsnag.JavaxMvcConfiguration",
- "com.bugsnag.ScheduledTaskConfiguration"
- };
-
- @Override
- public String[] selectImports(AnnotationMetadata importingClassMetadata) {
- if (isSpringJakartaCompatible() && isJava17Compatible()) {
- return SPRING_JAKARTA_CLASSES;
- }
-
- return SPRING_JAVAX_CLASSES;
- }
-
- private static boolean isSpringJakartaCompatible() {
- return getMajorVersion(SpringVersion.getVersion()) >= 6;
- }
-
- private static boolean isJava17Compatible() {
- return getMajorVersion(System.getProperty("java.version")) >= 17;
- }
-
- private static int getMajorVersion(String version) {
- if (version == null) {
- return 0;
- }
- int firstDot = version.indexOf(".");
- String majorVersion;
-
- if (firstDot == -1) {
- majorVersion = version;
- } else {
- majorVersion = version.substring(0, firstDot);
- }
-
- try {
- return Integer.parseInt(majorVersion);
- } catch (NumberFormatException nfe) {
- return 0;
- }
- }
-}
diff --git a/bugsnag-spring/src/common/java/com/bugsnag/ScheduledTaskConfiguration.java b/bugsnag-spring/src/common/java/com/bugsnag/ScheduledTaskConfiguration.java
deleted file mode 100644
index 6c9718a9..00000000
--- a/bugsnag-spring/src/common/java/com/bugsnag/ScheduledTaskConfiguration.java
+++ /dev/null
@@ -1,119 +0,0 @@
-package com.bugsnag;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.springframework.aop.framework.AopProxyUtils;
-import org.springframework.aop.support.AopUtils;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.context.annotation.Configuration;
-import org.springframework.scheduling.TaskScheduler;
-import org.springframework.scheduling.annotation.SchedulingConfigurer;
-import org.springframework.scheduling.concurrent.ConcurrentTaskScheduler;
-import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
-import org.springframework.scheduling.config.ScheduledTaskRegistrar;
-
-import org.springframework.util.ErrorHandler;
-
-import java.lang.reflect.Field;
-import java.util.concurrent.ScheduledExecutorService;
-
-/**
- * Add configuration for reporting unhandled exceptions for scheduled tasks.
- */
-@Configuration
-class ScheduledTaskConfiguration implements SchedulingConfigurer {
-
- private static final Logger LOGGER = LoggerFactory.getLogger(ScheduledTaskConfiguration.class);
-
- @Autowired
- private Bugsnag bugsnag;
-
- @Autowired
- private ScheduledTaskBeanLocator beanLocator;
-
- /**
- * Add bugsnag error handling to a task scheduler
- */
- @Override
- public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
- BugsnagScheduledTaskExceptionHandler bugsnagErrorHandler =
- new BugsnagScheduledTaskExceptionHandler(bugsnag);
-
- // Decision process for finding a TaskScheduler, in order of preference:
- //
- // 1. use the scheduler from the task registrar
- // 2. search for a TaskScheduler bean, by type, then by name
- // 3. search for a ScheduledExecutorService bean by type, then by name,
- // and wrap it in a TaskScheduler
- // 4. create our own TaskScheduler
-
- TaskScheduler registrarScheduler = taskRegistrar.getScheduler();
- TaskScheduler taskScheduler = registrarScheduler != null
- ? registrarScheduler : beanLocator.resolveTaskScheduler();
-
- if (taskScheduler != null) {
- //check if taskSchedular is a proxy
- if (AopUtils.isAopProxy(taskScheduler)) {
- //if it's a proxy then get the target class and cast as necessary
- Class> targetClass = AopProxyUtils.ultimateTargetClass(taskScheduler);
- if (TaskScheduler.class.isAssignableFrom(targetClass)) {
- taskScheduler = (TaskScheduler) AopProxyUtils.getSingletonTarget(taskScheduler);
- }
- }
- configureExistingTaskScheduler(taskScheduler, bugsnagErrorHandler);
- } else {
- ScheduledExecutorService executorService = beanLocator.resolveScheduledExecutorService();
- taskScheduler = createNewTaskScheduler(executorService, bugsnagErrorHandler);
- taskRegistrar.setScheduler(taskScheduler);
- }
- }
-
- private TaskScheduler createNewTaskScheduler(
- ScheduledExecutorService executorService,
- BugsnagScheduledTaskExceptionHandler errorHandler) {
- if (executorService != null) {
- // create a task scheduler which delegates to the existing Executor
- ConcurrentTaskScheduler scheduler = new ConcurrentTaskScheduler(executorService);
- scheduler.setErrorHandler(errorHandler);
- return scheduler;
- } else {
- // If no task scheduler has been defined by the application, create one
- // and add the bugsnag error handler.
- ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler();
- scheduler.setErrorHandler(errorHandler);
- scheduler.initialize();
- return scheduler;
- }
- }
-
- /**
- * If a task scheduler has been defined by the application, get it so that
- * bugsnag error handling can be added.
- *
- * Reflection is the simplest way to get and set an error handler
- * because the error handler setter is only defined in the concrete classes,
- * not the TaskScheduler interface.
- *
- * @param taskScheduler the task scheduler
- */
- private void configureExistingTaskScheduler(TaskScheduler taskScheduler,
- BugsnagScheduledTaskExceptionHandler errorHandler) {
- try {
- Field errorHandlerField =
- taskScheduler.getClass().getDeclaredField("errorHandler");
- errorHandlerField.setAccessible(true);
- Object existingErrorHandler = errorHandlerField.get(taskScheduler);
-
- // If an error handler has already been defined then make the Bugsnag handler
- // call this afterwards
- if (existingErrorHandler instanceof ErrorHandler) {
- errorHandler.setExistingErrorHandler((ErrorHandler) existingErrorHandler);
- }
-
- // Add the bugsnag error handler to the scheduler.
- errorHandlerField.set(taskScheduler, errorHandler);
- } catch (Throwable ex) {
- LOGGER.warn("Bugsnag scheduled task exception handler could not be configured");
- }
- }
-}
diff --git a/bugsnag-spring/src/javax/java/com/bugsnag/BugsnagJavaxMvcExceptionHandler.java b/bugsnag-spring/src/javax/java/com/bugsnag/BugsnagJavaxMvcExceptionHandler.java
deleted file mode 100644
index 62cbf9ea..00000000
--- a/bugsnag-spring/src/javax/java/com/bugsnag/BugsnagJavaxMvcExceptionHandler.java
+++ /dev/null
@@ -1,49 +0,0 @@
-package com.bugsnag;
-
-import com.bugsnag.HandledState.SeverityReasonType;
-
-import org.springframework.core.Ordered;
-import org.springframework.core.annotation.Order;
-import org.springframework.web.servlet.HandlerExceptionResolver;
-import org.springframework.web.servlet.ModelAndView;
-
-import java.util.Collections;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-
-/**
- * Reports uncaught exceptions thrown from handler mapping or execution to Bugsnag
- * and then passes the exception to the next handler in the chain.
- *
- * Set to highest precedence so that it should be called before other exception
- * resolvers.
- */
-@Order(Ordered.HIGHEST_PRECEDENCE)
-class BugsnagJavaxMvcExceptionHandler implements HandlerExceptionResolver {
-
- private final Bugsnag bugsnag;
-
- BugsnagJavaxMvcExceptionHandler(final Bugsnag bugsnag) {
- this.bugsnag = bugsnag;
- }
-
- @Override
- public ModelAndView resolveException(HttpServletRequest request,
- HttpServletResponse response,
- Object handler,
- java.lang.Exception ex) {
-
- if (bugsnag.getConfig().shouldSendUncaughtExceptions()) {
- HandledState handledState = HandledState.newInstance(
- SeverityReasonType.REASON_UNHANDLED_EXCEPTION_MIDDLEWARE,
- Collections.singletonMap("framework", "Spring"),
- Severity.ERROR,
- true);
-
- bugsnag.notify(ex, handledState, Thread.currentThread());
- }
-
- // Returning null passes the exception onto the next resolver in the chain.
- return null;
- }
-}
diff --git a/bugsnag-spring/src/javax/java/com/bugsnag/JavaxMvcConfiguration.java b/bugsnag-spring/src/javax/java/com/bugsnag/JavaxMvcConfiguration.java
deleted file mode 100644
index 8ba8e5e8..00000000
--- a/bugsnag-spring/src/javax/java/com/bugsnag/JavaxMvcConfiguration.java
+++ /dev/null
@@ -1,35 +0,0 @@
-package com.bugsnag;
-
-import org.springframework.beans.factory.InitializingBean;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.context.annotation.Bean;
-import org.springframework.context.annotation.Conditional;
-import org.springframework.context.annotation.Configuration;
-
-/**
- * If spring-webmvc is loaded, add configuration for reporting unhandled exceptions.
- */
-@Configuration
-@Conditional(SpringWebMvcLoadedCondition.class)
-class JavaxMvcConfiguration implements InitializingBean {
-
- @Autowired
- private Bugsnag bugsnag;
-
- /**
- * Register an exception resolver to send unhandled reports to Bugsnag
- * for uncaught exceptions thrown from request handlers.
- */
- @Bean
- BugsnagJavaxMvcExceptionHandler bugsnagHandlerExceptionResolver() {
- return new BugsnagJavaxMvcExceptionHandler(bugsnag);
- }
-
- /**
- * Add a callback to assign specified severities for some Spring exceptions.
- */
- @Override
- public void afterPropertiesSet() {
- bugsnag.addCallback(new ExceptionClassCallback());
- }
-}
diff --git a/bugsnag-spring/src/javax/java/com/bugsnag/SpringBootJavaxConfiguration.java b/bugsnag-spring/src/javax/java/com/bugsnag/SpringBootJavaxConfiguration.java
deleted file mode 100644
index 1f63c81d..00000000
--- a/bugsnag-spring/src/javax/java/com/bugsnag/SpringBootJavaxConfiguration.java
+++ /dev/null
@@ -1,33 +0,0 @@
-package com.bugsnag;
-
-import com.bugsnag.servlet.javax.BugsnagServletRequestListener;
-
-import org.springframework.boot.web.servlet.ServletListenerRegistrationBean;
-import org.springframework.context.annotation.Bean;
-import org.springframework.context.annotation.Conditional;
-import org.springframework.context.annotation.Configuration;
-
-import javax.servlet.ServletRequestListener;
-
-/**
- * If spring-boot is loaded, add configuration specific to Spring Boot
- */
-@Configuration
-@Conditional(SpringBootLoadedCondition.class)
-class SpringBootJavaxConfiguration extends SpringBootConfiguration {
-
- /**
- * The {@link com.bugsnag.servlet.javax.BugsnagServletContainerInitializer} does not work for Spring Boot, need to
- * register the {@link BugsnagServletRequestListener} using a Spring Boot
- * {@link ServletListenerRegistrationBean} instead. This adds session tracking and
- * automatic servlet request metadata collection.
- */
- @Bean
- @Conditional(SpringWebMvcLoadedCondition.class)
- ServletListenerRegistrationBean listenerRegistrationBean() {
- ServletListenerRegistrationBean srb =
- new ServletListenerRegistrationBean();
- srb.setListener(new BugsnagServletRequestListener());
- return srb;
- }
-}
diff --git a/bugsnag-spring/src/javaxTest/java/com/bugsnag/ScheduledTaskBeanLocatorTest.java b/bugsnag-spring/src/javaxTest/java/com/bugsnag/ScheduledTaskBeanLocatorTest.java
deleted file mode 100644
index 74eb856d..00000000
--- a/bugsnag-spring/src/javaxTest/java/com/bugsnag/ScheduledTaskBeanLocatorTest.java
+++ /dev/null
@@ -1,81 +0,0 @@
-package com.bugsnag;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNull;
-import static org.mockito.Mockito.when;
-
-import com.bugsnag.testapp.springboot.TestSpringBootApplication;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.springframework.beans.factory.NoUniqueBeanDefinitionException;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.boot.test.context.SpringBootTest;
-import org.springframework.boot.test.mock.mockito.MockBean;
-import org.springframework.context.ApplicationContext;
-import org.springframework.scheduling.TaskScheduler;
-import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
-import org.springframework.test.context.junit4.SpringRunner;
-
-import java.util.concurrent.Executors;
-import java.util.concurrent.ScheduledExecutorService;
-
-@RunWith(SpringRunner.class)
-@SpringBootTest(classes = TestSpringBootApplication.class)
-public class ScheduledTaskBeanLocatorTest {
-
- @Autowired
- private ScheduledTaskBeanLocator beanLocator;
-
- @MockBean
- private ApplicationContext context;
-
- @Before
- public void setUp() {
- beanLocator.setApplicationContext(context);
- }
-
- @Test
- public void findSchedulerByType() {
- ThreadPoolTaskScheduler expected = new ThreadPoolTaskScheduler();
- when(context.getBean(TaskScheduler.class)).thenReturn(expected);
- assertEquals(expected, beanLocator.resolveTaskScheduler());
- }
-
- @Test
- public void findSchedulerByName() {
- ThreadPoolTaskScheduler expected = new ThreadPoolTaskScheduler();
- Throwable exc = new NoUniqueBeanDefinitionException(TaskScheduler.class);
- when(context.getBean(TaskScheduler.class)).thenThrow(exc);
- when(context.getBean("taskScheduler", TaskScheduler.class)).thenReturn(expected);
- assertEquals(expected, beanLocator.resolveTaskScheduler());
- }
-
- @Test
- public void noTaskSchedulerAvailable() {
- assertNull(beanLocator.resolveTaskScheduler());
- }
-
- @Test
- public void findExecutorByType() {
- ScheduledExecutorService expected = Executors.newScheduledThreadPool(1);
- when(context.getBean(ScheduledExecutorService.class)).thenReturn(expected);
- assertEquals(expected, beanLocator.resolveScheduledExecutorService());
- }
-
- @Test
- public void findExecutorByName() {
- ScheduledExecutorService expected = Executors.newScheduledThreadPool(4);
- Throwable exc = new NoUniqueBeanDefinitionException(ScheduledExecutorService.class);
- when(context.getBean(ScheduledExecutorService.class)).thenThrow(exc);
- when(context.getBean("taskScheduler", ScheduledExecutorService.class))
- .thenReturn(expected);
- assertEquals(expected, beanLocator.resolveScheduledExecutorService());
- }
-
- @Test
- public void noScheduledExecutorAvailable() {
- assertNull(beanLocator.resolveScheduledExecutorService());
- }
-}
diff --git a/bugsnag-spring/src/javaxTest/java/com/bugsnag/ScheduledTaskConfigurationTest.java b/bugsnag-spring/src/javaxTest/java/com/bugsnag/ScheduledTaskConfigurationTest.java
deleted file mode 100644
index f619795d..00000000
--- a/bugsnag-spring/src/javaxTest/java/com/bugsnag/ScheduledTaskConfigurationTest.java
+++ /dev/null
@@ -1,148 +0,0 @@
-package com.bugsnag;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertTrue;
-import static org.junit.jupiter.api.Assertions.assertFalse;
-import static org.mockito.Mockito.when;
-
-import com.bugsnag.testapp.springboot.TestSpringBootApplication;
-
-import org.aopalliance.intercept.MethodInterceptor;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.springframework.aop.framework.ProxyFactory;
-import org.springframework.beans.factory.NoUniqueBeanDefinitionException;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.boot.test.context.SpringBootTest;
-import org.springframework.boot.test.mock.mockito.MockBean;
-import org.springframework.context.ApplicationContext;
-import org.springframework.scheduling.TaskScheduler;
-import org.springframework.scheduling.concurrent.ConcurrentTaskScheduler;
-import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
-import org.springframework.scheduling.config.ScheduledTaskRegistrar;
-import org.springframework.test.context.junit4.SpringRunner;
-
-import java.lang.reflect.Field;
-import java.util.concurrent.Executors;
-import java.util.concurrent.ScheduledExecutorService;
-
-@RunWith(SpringRunner.class)
-@SpringBootTest(classes = TestSpringBootApplication.class)
-public class ScheduledTaskConfigurationTest {
-
- @Autowired
- private ScheduledTaskConfiguration configuration;
-
- @Mock
- private ScheduledTaskRegistrar registrar;
-
- @Autowired
- private ScheduledTaskBeanLocator beanLocator;
-
- @MockBean
- private ApplicationContext context;
-
- @Before
- public void setUp() {
- registrar = new ScheduledTaskRegistrar();
- beanLocator.setApplicationContext(context);
- }
-
- @Test
- public void existingSchedulerUsed() {
- ThreadPoolTaskScheduler expected = new ThreadPoolTaskScheduler();
- registrar.setScheduler(expected);
- configuration.configureTasks(registrar);
- assertEquals(expected, registrar.getScheduler());
- }
-
- @Test
- public void noSchedulersAvailable() {
- configuration.configureTasks(registrar);
- assertTrue(registrar.getScheduler() instanceof ThreadPoolTaskScheduler);
- }
-
- @Test
- public void findSchedulerByType() throws NoSuchFieldException, IllegalAccessException {
- ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler();
- when(context.getBean(TaskScheduler.class)).thenReturn(scheduler);
-
- configuration.configureTasks(registrar);
- assertNull(registrar.getScheduler());
- Object errorHandler = accessField(scheduler, "errorHandler");
- assertTrue(errorHandler instanceof BugsnagScheduledTaskExceptionHandler);
- }
-
- @Test
- public void findSchedulerByName() throws NoSuchFieldException, IllegalAccessException {
- ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler();
- Throwable exc = new NoUniqueBeanDefinitionException(TaskScheduler.class);
- when(context.getBean(TaskScheduler.class)).thenThrow(exc);
- when(context.getBean("taskScheduler", TaskScheduler.class)).thenReturn(scheduler);
-
- configuration.configureTasks(registrar);
- assertNull(registrar.getScheduler());
- Object errorHandler = accessField(scheduler, "errorHandler");
- assertTrue(errorHandler instanceof BugsnagScheduledTaskExceptionHandler);
- }
-
- @Test
- public void findExecutorByType() throws NoSuchFieldException, IllegalAccessException {
- ScheduledExecutorService expected = Executors.newScheduledThreadPool(1);
- when(context.getBean(ScheduledExecutorService.class)).thenReturn(expected);
-
- configuration.configureTasks(registrar);
- TaskScheduler scheduler = registrar.getScheduler();
- assertTrue(scheduler instanceof ConcurrentTaskScheduler);
- assertEquals(expected, accessField(scheduler, "scheduledExecutor"));
- }
-
- @Test
- public void findExecutorByName() throws NoSuchFieldException, IllegalAccessException {
- ScheduledExecutorService expected = Executors.newScheduledThreadPool(4);
- Throwable exc = new NoUniqueBeanDefinitionException(ScheduledExecutorService.class);
- when(context.getBean(ScheduledExecutorService.class)).thenThrow(exc);
- when(context.getBean("taskScheduler", ScheduledExecutorService.class))
- .thenReturn(expected);
-
- configuration.configureTasks(registrar);
- TaskScheduler scheduler = registrar.getScheduler();
- assertTrue(scheduler instanceof ConcurrentTaskScheduler);
- assertEquals(expected, accessField(scheduler, "scheduledExecutor"));
- }
-
- @Test
- public void configureTasks_withProxyWrappedRegistrar() throws NoSuchFieldException, IllegalAccessException {
- ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler();
- when(context.getBean(TaskScheduler.class)).thenReturn(scheduler);
- TaskScheduler proxyScheduler = createProxy(scheduler);
- registrar.setScheduler(proxyScheduler);
- Object errorHandler = accessField(scheduler, "errorHandler");
- assertFalse(
- errorHandler instanceof BugsnagScheduledTaskExceptionHandler,
- "errorHandler should not be BugsnagScheduledTaskExceptionHandler"
- );
- configuration.configureTasks(registrar);
- errorHandler = accessField(scheduler, "errorHandler");
- assertTrue(
- "errorHandler should be BugsnagScheduledTaskExceptionHandler",
- errorHandler instanceof BugsnagScheduledTaskExceptionHandler
- );
- }
-
- private TaskScheduler createProxy(TaskScheduler target) {
- ProxyFactory factory = new ProxyFactory(target);
- factory.addAdvice((MethodInterceptor) invocation -> invocation.proceed());
- return (TaskScheduler) factory.getProxy();
- }
-
- private Object accessField(Object object, String fieldName)
- throws NoSuchFieldException, IllegalAccessException {
- Field field = object.getClass().getDeclaredField(fieldName);
- field.setAccessible(true);
- return field.get(object);
- }
-}
diff --git a/bugsnag-spring/src/javaxTest/java/com/bugsnag/SpringAsyncTest.java b/bugsnag-spring/src/javaxTest/java/com/bugsnag/SpringAsyncTest.java
deleted file mode 100644
index 1bdfa04b..00000000
--- a/bugsnag-spring/src/javaxTest/java/com/bugsnag/SpringAsyncTest.java
+++ /dev/null
@@ -1,90 +0,0 @@
-package com.bugsnag;
-
-import static org.hamcrest.core.Is.is;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertThat;
-import static org.junit.Assert.assertTrue;
-import static org.mockito.Mockito.mock;
-
-import com.bugsnag.HandledState.SeverityReasonType;
-import com.bugsnag.delivery.Delivery;
-import com.bugsnag.testapp.springboot.AsyncService;
-import com.bugsnag.testapp.springboot.TestSpringBootApplication;
-
-import org.junit.Assert;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.boot.test.context.SpringBootTest;
-import org.springframework.test.context.junit4.SpringRunner;
-
-import java.util.Collections;
-
-/**
- * Test that a Spring Boot application configured with the
- * {@link BugsnagSpringConfiguration} performs as expected.
- */
-@RunWith(SpringRunner.class)
-@SpringBootTest(classes = TestSpringBootApplication.class)
-public class SpringAsyncTest {
-
- @Autowired
- private Bugsnag bugsnag;
-
- @Autowired
- private AsyncService asyncService;
-
- private Delivery delivery;
-
- /**
- * Initialize test state
- */
- @Before
- public void setUp() {
- delivery = mock(Delivery.class);
- bugsnag.setDelivery(delivery);
- }
-
- @Test
- public void bugsnagNotifyWhenAsyncVoidReturnTypeException() {
- asyncService.throwExceptionVoid();
-
- Report report = TestUtils.verifyAndGetReport(delivery);
-
- // Assert that the exception was detected correctly
- assertEquals("Async void test", report.getExceptionMessage());
- assertEquals("java.lang.RuntimeException", report.getExceptionName());
-
- // Assert that the severity, severity reason and unhandled values are correct
- Assert.assertEquals(Severity.ERROR.getValue(), report.getSeverity());
- assertEquals(
- SeverityReasonType.REASON_UNHANDLED_EXCEPTION_MIDDLEWARE.toString(),
- report.getSeverityReason().getType());
- assertThat(
- report.getSeverityReason().getAttributes(),
- is(Collections.singletonMap("framework", "Spring")));
- assertTrue(report.getUnhandled());
- }
-
- @Test
- public void bugsnagNotifyWhenAsyncFutureReturnTypeException() {
- asyncService.throwExceptionFuture();
-
- Report report = TestUtils.verifyAndGetReport(delivery);
-
- // Assert that the exception was detected correctly
- assertEquals("Async future test", report.getExceptionMessage());
- assertEquals("java.lang.RuntimeException", report.getExceptionName());
-
- // Assert that the severity, severity reason and unhandled values are correct
- assertEquals(Severity.ERROR.getValue(), report.getSeverity());
- assertEquals(
- SeverityReasonType.REASON_UNHANDLED_EXCEPTION_MIDDLEWARE.toString(),
- report.getSeverityReason().getType());
- assertThat(
- report.getSeverityReason().getAttributes(),
- is(Collections.singletonMap("framework", "Spring")));
- assertTrue(report.getUnhandled());
- }
-}
diff --git a/bugsnag-spring/src/javaxTest/java/com/bugsnag/SpringMvcTest.java b/bugsnag-spring/src/javaxTest/java/com/bugsnag/SpringMvcTest.java
deleted file mode 100644
index d426ba01..00000000
--- a/bugsnag-spring/src/javaxTest/java/com/bugsnag/SpringMvcTest.java
+++ /dev/null
@@ -1,282 +0,0 @@
-package com.bugsnag;
-
-import static com.bugsnag.TestUtils.anyMapOf;
-import static com.bugsnag.TestUtils.verifyAndGetReport;
-import static org.hamcrest.core.Is.is;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertThat;
-import static org.junit.Assert.assertTrue;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-
-import com.bugsnag.HandledState.SeverityReasonType;
-import com.bugsnag.callbacks.Callback;
-import com.bugsnag.delivery.Delivery;
-import com.bugsnag.serialization.Serializer;
-import com.bugsnag.testapp.springboot.TestSpringBootApplication;
-
-import org.junit.Assert;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.boot.SpringBootVersion;
-import org.springframework.boot.test.context.SpringBootTest;
-import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
-import org.springframework.boot.test.web.client.TestRestTemplate;
-import org.springframework.boot.web.server.LocalServerPort;
-import org.springframework.core.SpringVersion;
-import org.springframework.http.HttpEntity;
-import org.springframework.http.HttpHeaders;
-import org.springframework.http.HttpMethod;
-import org.springframework.test.context.junit4.SpringRunner;
-
-import java.lang.reflect.Field;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Map;
-
-/**
- * Test that a Spring Boot application configured with the
- * {@link BugsnagSpringConfiguration} performs as expected.
- */
-@RunWith(SpringRunner.class)
-@SpringBootTest(
- classes = TestSpringBootApplication.class,
- webEnvironment = WebEnvironment.RANDOM_PORT)
-public class SpringMvcTest {
-
- @LocalServerPort
- private int randomServerPort;
-
- @Autowired
- private TestRestTemplate restTemplate;
-
- @Autowired
- private Bugsnag bugsnag;
-
- private Delivery delivery;
-
- private long sessionsStartedBeforeTest;
-
- /**
- * Initialize test state
- */
- @Before
- public void setUp() {
- delivery = mock(Delivery.class);
-
- bugsnag.setDelivery(delivery);
- bugsnag.getConfig().setSendUncaughtExceptions(true);
- bugsnag.getConfig().setAutoCaptureSessions(true);
-
- // Cannot reset the session count on the bugsnag bean for each test, so note
- // the current session count before the test starts instead.
- sessionsStartedBeforeTest = getSessionCount();
- }
-
- @Test
- public void bugsnagNotifyWhenUncaughtControllerException() {
- callRuntimeExceptionEndpoint();
-
- Report report = verifyAndGetReport(delivery);
-
- // Assert that the exception was detected correctly
- assertEquals("Test", report.getExceptionMessage());
- assertEquals("java.lang.RuntimeException", report.getExceptionName());
-
- // Assert that the severity, severity reason and unhandled values are correct
- Assert.assertEquals(Severity.ERROR.getValue(), report.getSeverity());
- assertEquals(
- SeverityReasonType.REASON_UNHANDLED_EXCEPTION_MIDDLEWARE.toString(),
- report.getSeverityReason().getType());
- assertThat(
- report.getSeverityReason().getAttributes(),
- is(Collections.singletonMap("framework", "Spring")));
- assertTrue(report.getUnhandled());
- }
-
- @Test
- public void noBugsnagNotifyWhenSendUncaughtExceptionsFalse() {
- bugsnag.getConfig().setSendUncaughtExceptions(false);
-
- callRuntimeExceptionEndpoint();
-
- verifyNoReport();
- }
-
- @Test
- public void bugsnagSessionStartedWhenAutoCaptureSessions() {
- callRuntimeExceptionEndpoint();
-
- assertSessionsStarted(1);
- }
-
- @Test
- public void noBugsnagSessionStartedWhenAutoCaptureSessionsFalse() {
- bugsnag.getConfig().setAutoCaptureSessions(false);
-
- callRuntimeExceptionEndpoint();
-
- assertSessionsStarted(0);
- }
-
- @Test
- public void requestMetadataSetCorrectly() {
- callRuntimeExceptionEndpoint();
-
- Report report = verifyAndGetReport(delivery);
-
- // Check that the context is set to the HTTP method and URI of the endpoint
- assertEquals("GET /throw-runtime-exception", report.getContext());
-
- // Check that the request metadata is set as expected
- @SuppressWarnings(value = "unchecked") Map requestMetadata =
- (Map) report.getMetaData().get("request");
- assertEquals("http://localhost:" + randomServerPort + "/throw-runtime-exception",
- requestMetadata.get("url"));
- assertEquals("GET", requestMetadata.get("method"));
- assertEquals("127.0.0.1", requestMetadata.get("clientIp"));
-
- // Assert that the request params are as expected
- @SuppressWarnings(value = "unchecked") Map params =
- (Map) requestMetadata.get("params");
- assertEquals("paramVal1", params.get("param1")[0]);
- assertEquals("paramVal2", params.get("param2")[0]);
-
- // Assert that the request headers are as expected, including headers with
- // multiple values represented as a comma-separated string.
- @SuppressWarnings(value = "unchecked") Map headers =
- (Map) requestMetadata.get("headers");
- assertEquals("header1Val1,header1Val2", headers.get("header1"));
- assertEquals("header2Val1", headers.get("header2"));
- }
-
- @Test
- @SuppressWarnings("unchecked")
- public void springVersionSetCorrectly() {
- callRuntimeExceptionEndpoint();
-
- Report report = verifyAndGetReport(delivery);
-
- // Check that the Spring version is set as expected
- Map deviceMetadata = report.getDevice();
- Map runtimeVersions =
- (Map) deviceMetadata.get("runtimeVersions");
- assertEquals(SpringVersion.getVersion(), runtimeVersions.get("springFramework"));
- assertEquals(SpringBootVersion.getVersion(), runtimeVersions.get("springBoot"));
- }
-
- @Test
- public void unhandledTypeMismatchExceptionSeverityInfo() {
- callUnhandledTypeMismatchExceptionEndpoint();
-
- Report report = verifyAndGetReport(delivery);
-
- assertTrue(report.getUnhandled());
- assertEquals("info", report.getSeverity());
- assertEquals("exceptionClass", report.getSeverityReason().getType());
- assertThat(report.getSeverityReason().getAttributes(),
- is(Collections.singletonMap("exceptionClass", "TypeMismatchException")));
- }
-
- @Test
- public void unhandledTypeMismatchExceptionCallbackSeverity()
- throws IllegalAccessException, NoSuchFieldException {
- Report report;
- Callback callback = new Callback() {
- @Override
- public void beforeNotify(Report report) {
- report.setSeverity(Severity.WARNING);
- }
- };
-
- try {
- bugsnag.addCallback(callback);
-
- callUnhandledTypeMismatchExceptionEndpoint();
-
- report = verifyAndGetReport(delivery);
- } finally {
- // Remove the callback via reflection so that subsequent tests do not use it
- Field callbacksField = Configuration.class.getDeclaredField("callbacks");
- @SuppressWarnings(value = "unchecked") Collection callbacks =
- (Collection) callbacksField.get(bugsnag.getConfig());
- callbacks.remove(callback);
- }
-
- assertTrue(report.getUnhandled());
- assertEquals("warning", report.getSeverity());
- assertEquals("userCallbackSetSeverity", report.getSeverityReason().getType());
- }
-
- @Test
- public void handledTypeMismatchExceptionUserSeverity() {
- callHandledTypeMismatchExceptionUserSeverityEndpoint();
-
- Report report = verifyAndGetReport(delivery);
-
- assertFalse(report.getUnhandled());
- assertEquals("warning", report.getSeverity());
- assertEquals("userSpecifiedSeverity", report.getSeverityReason().getType());
- assertThat(report.getSeverityReason().getAttributes(), is(Collections.EMPTY_MAP));
- }
-
- @Test
- public void handledTypeMismatchExceptionCallbackSeverity() {
- callHandledTypeMismatchExceptionCallbackSeverityEndpoint();
-
- Report report = verifyAndGetReport(delivery);
-
- assertFalse(report.getUnhandled());
- assertEquals("warning", report.getSeverity());
- assertEquals("userCallbackSetSeverity", report.getSeverityReason().getType());
- }
-
- private void callUnhandledTypeMismatchExceptionEndpoint() {
- this.restTemplate.getForEntity(
- "/throw-type-mismatch-exception", String.class);
- }
-
- private void callHandledTypeMismatchExceptionUserSeverityEndpoint() {
- this.restTemplate.getForEntity(
- "/handled-type-mismatch-exception-user-severity", String.class);
- }
-
- private void callHandledTypeMismatchExceptionCallbackSeverityEndpoint() {
- this.restTemplate.getForEntity(
- "/handled-type-mismatch-exception-callback-severity", String.class);
- }
-
- private void callRuntimeExceptionEndpoint() {
- HttpHeaders headers = new HttpHeaders();
- headers.add("header1", "header1Val1");
- headers.add("header1", "header1Val2");
- headers.add("header2", "header2Val1");
- HttpEntity entity = new HttpEntity("parameters", headers);
- this.restTemplate.exchange(
- "/throw-runtime-exception?param1=paramVal1¶m2=paramVal2",
- HttpMethod.GET,
- entity,
- String.class);
- }
-
- private void verifyNoReport() {
- verify(delivery, times(0)).deliver(
- any(Serializer.class),
- any(),
- anyMapOf(String.class, String.class));
- }
-
- private void assertSessionsStarted(int sessionsStarted) {
- assertEquals(sessionsStartedBeforeTest + sessionsStarted, getSessionCount());
- }
-
- private long getSessionCount() {
- return bugsnag.getSessionTracker().getBatchCount() != null
- ? bugsnag.getSessionTracker().getBatchCount().getSessionsStarted() : 0;
- }
-}
diff --git a/bugsnag-spring/src/javaxTest/java/com/bugsnag/SpringScheduledTaskTest.java b/bugsnag-spring/src/javaxTest/java/com/bugsnag/SpringScheduledTaskTest.java
deleted file mode 100644
index 7a86ff61..00000000
--- a/bugsnag-spring/src/javaxTest/java/com/bugsnag/SpringScheduledTaskTest.java
+++ /dev/null
@@ -1,95 +0,0 @@
-package com.bugsnag;
-
-import static org.hamcrest.core.Is.is;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertThat;
-import static org.junit.Assert.assertTrue;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-
-import com.bugsnag.HandledState.SeverityReasonType;
-import com.bugsnag.delivery.Delivery;
-import com.bugsnag.testapp.springboot.TestSpringBootApplication;
-
-import org.junit.Assert;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.boot.test.context.SpringBootTest;
-import org.springframework.boot.test.mock.mockito.MockBean;
-import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
-import org.springframework.test.context.junit4.SpringRunner;
-import org.springframework.util.ErrorHandler;
-
-import java.util.Collections;
-import java.util.concurrent.ExecutionException;
-
-/**
- * Test that a Spring Boot application configured with the
- * {@link BugsnagSpringConfiguration} performs as expected.
- */
-@RunWith(SpringRunner.class)
-@SpringBootTest(classes = TestSpringBootApplication.class)
-public class SpringScheduledTaskTest {
-
- @Autowired
- private Bugsnag bugsnag;
-
- @Autowired
- private ThreadPoolTaskScheduler scheduler;
-
- @MockBean
- private ErrorHandler mockErrorHandler;
-
- private Delivery delivery;
-
- /**
- * Initialize test state
- */
- @Before
- public void setUp() {
- delivery = mock(Delivery.class);
- bugsnag.setDelivery(delivery);
- }
-
- @Test
- public void bugsnagNotifyWhenScheduledTaskException()
- throws ExecutionException, InterruptedException {
-
- // The task to schedule
- Runnable exampleRunnable = new Runnable() {
- @Override
- public void run() {
- throw new RuntimeException("Scheduled test");
- }
- };
-
- // Run the task now and wait for it to finish
- scheduler.submit(exampleRunnable).get();
-
- Report report = TestUtils.verifyAndGetReport(delivery);
-
- // Assert that the exception was detected correctly
- assertEquals("Scheduled test", report.getExceptionMessage());
- assertEquals("java.lang.RuntimeException", report.getExceptionName());
-
- // Assert that the severity, severity reason and unhandled values are correct
- Assert.assertEquals(Severity.ERROR.getValue(), report.getSeverity());
- assertEquals(
- SeverityReasonType.REASON_UNHANDLED_EXCEPTION_MIDDLEWARE.toString(),
- report.getSeverityReason().getType());
- assertThat(
- report.getSeverityReason().getAttributes(),
- is(Collections.singletonMap("framework", "Spring")));
- assertTrue(report.getUnhandled());
-
- // Assert that the exception is passed to an existing exception handler
- ArgumentCaptor exceptionCaptor =
- ArgumentCaptor.forClass(RuntimeException.class);
- verify(mockErrorHandler, times(1)).handleError(exceptionCaptor.capture());
- assertEquals("Scheduled test", exceptionCaptor.getValue().getMessage());
- }
-}
diff --git a/bugsnag-spring/src/javaxTest/java/com/bugsnag/testapp/springboot/AsyncService.java b/bugsnag-spring/src/javaxTest/java/com/bugsnag/testapp/springboot/AsyncService.java
deleted file mode 100644
index 54376bac..00000000
--- a/bugsnag-spring/src/javaxTest/java/com/bugsnag/testapp/springboot/AsyncService.java
+++ /dev/null
@@ -1,19 +0,0 @@
-package com.bugsnag.testapp.springboot;
-
-import org.springframework.scheduling.annotation.Async;
-import org.springframework.stereotype.Service;
-
-import java.util.concurrent.Future;
-
-@Service
-public class AsyncService {
- @Async
- public void throwExceptionVoid() {
- throw new RuntimeException("Async void test");
- }
-
- @Async
- public Future throwExceptionFuture() {
- throw new RuntimeException("Async future test");
- }
-}
diff --git a/bugsnag-spring/src/javaxTest/java/com/bugsnag/testapp/springboot/TestConfiguration.java b/bugsnag-spring/src/javaxTest/java/com/bugsnag/testapp/springboot/TestConfiguration.java
deleted file mode 100644
index c1691243..00000000
--- a/bugsnag-spring/src/javaxTest/java/com/bugsnag/testapp/springboot/TestConfiguration.java
+++ /dev/null
@@ -1,50 +0,0 @@
-package com.bugsnag.testapp.springboot;
-
-import com.bugsnag.Bugsnag;
-import com.bugsnag.BugsnagAsyncExceptionHandler;
-import com.bugsnag.BugsnagSpringConfiguration;
-
-import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.context.annotation.Bean;
-import org.springframework.context.annotation.Configuration;
-import org.springframework.context.annotation.Import;
-import org.springframework.scheduling.annotation.AsyncConfigurerSupport;
-import org.springframework.scheduling.annotation.SchedulingConfigurer;
-import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
-import org.springframework.scheduling.config.ScheduledTaskRegistrar;
-import org.springframework.util.ErrorHandler;
-
-/**
- * This test configuration loads the BugsnagSpringConfiguration
- * that will be used for real Spring bugsnag integration.
- */
-@Configuration
-@Import(BugsnagSpringConfiguration.class)
-public class TestConfiguration extends AsyncConfigurerSupport implements SchedulingConfigurer {
-
- @Autowired(required = false)
- private ErrorHandler scheduledTaskErrorHandler;
-
- @Bean
- public Bugsnag bugsnag() {
- return new Bugsnag("apiKey");
- }
-
- @Bean
- ThreadPoolTaskScheduler scheduler() {
- ThreadPoolTaskScheduler taskScheduler = new ThreadPoolTaskScheduler();
- taskScheduler.setErrorHandler(scheduledTaskErrorHandler);
- return taskScheduler;
- }
-
- @Override
- public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
- taskRegistrar.setScheduler(scheduler());
- }
-
- @Override
- public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
- return new BugsnagAsyncExceptionHandler(bugsnag());
- }
-}
diff --git a/bugsnag-spring/src/javaxTest/java/com/bugsnag/testapp/springboot/TestController.java b/bugsnag-spring/src/javaxTest/java/com/bugsnag/testapp/springboot/TestController.java
deleted file mode 100644
index 9d0091b5..00000000
--- a/bugsnag-spring/src/javaxTest/java/com/bugsnag/testapp/springboot/TestController.java
+++ /dev/null
@@ -1,75 +0,0 @@
-package com.bugsnag.testapp.springboot;
-
-import com.bugsnag.Bugsnag;
-import com.bugsnag.Report;
-import com.bugsnag.Severity;
-import com.bugsnag.callbacks.Callback;
-
-import org.springframework.beans.TypeMismatchException;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.RestController;
-
-@RestController
-public class TestController {
-
- @Autowired
- private Bugsnag bugsnag;
-
- /**
- * Throw a runtime exception
- */
- @RequestMapping("/throw-runtime-exception")
- public void throwRuntimeException() {
- throw new RuntimeException("Test");
- }
-
- /**
- * Throw an exception where the severity reason is exceptionClass
- */
- @RequestMapping("/throw-type-mismatch-exception")
- public void throwTypeMismatchException() {
- throw new TypeMismatchException("Test", String.class);
- }
-
- /**
- * Report a handled exception where the severity reason is exceptionClass
- */
- @RequestMapping("/handled-type-mismatch-exception")
- public void handledTypeMismatchException() {
- try {
- throw new TypeMismatchException("Test", String.class);
- } catch (TypeMismatchException ex) {
- bugsnag.notify(ex);
- }
- }
-
- /**
- * Report a handled exception where the severity is set in the notify call
- */
- @RequestMapping("/handled-type-mismatch-exception-user-severity")
- public void handledTypeMismatchExceptionUserSeverity() {
- try {
- throw new TypeMismatchException("Test", String.class);
- } catch (TypeMismatchException ex) {
- bugsnag.notify(ex, Severity.WARNING);
- }
- }
-
- /**
- * Report a handled exception where the severity reason is set in a callback
- */
- @RequestMapping("/handled-type-mismatch-exception-callback-severity")
- public void handledTypeMismatchExceptionCallbackSeverity() {
- try {
- throw new TypeMismatchException("Test", String.class);
- } catch (TypeMismatchException ex) {
- bugsnag.notify(ex, new Callback() {
- @Override
- public void beforeNotify(Report report) {
- report.setSeverity(Severity.WARNING);
- }
- });
- }
- }
-}
diff --git a/bugsnag-spring/src/javaxTest/java/com/bugsnag/testapp/springboot/TestSpringBootApplication.java b/bugsnag-spring/src/javaxTest/java/com/bugsnag/testapp/springboot/TestSpringBootApplication.java
deleted file mode 100644
index f02cde84..00000000
--- a/bugsnag-spring/src/javaxTest/java/com/bugsnag/testapp/springboot/TestSpringBootApplication.java
+++ /dev/null
@@ -1,15 +0,0 @@
-package com.bugsnag.testapp.springboot;
-
-import org.springframework.boot.SpringApplication;
-import org.springframework.boot.autoconfigure.SpringBootApplication;
-import org.springframework.scheduling.annotation.EnableAsync;
-import org.springframework.scheduling.annotation.EnableScheduling;
-
-@SpringBootApplication
-@EnableScheduling
-@EnableAsync
-public class TestSpringBootApplication {
- public static void main(String[] args) {
- SpringApplication.run(TestSpringBootApplication.class, args);
- }
-}
diff --git a/bugsnag-spring/src/common/java/com/bugsnag/BugsnagAsyncExceptionHandler.java b/bugsnag-spring/src/main/java/com/bugsnag/BugsnagAsyncExceptionHandler.java
similarity index 100%
rename from bugsnag-spring/src/common/java/com/bugsnag/BugsnagAsyncExceptionHandler.java
rename to bugsnag-spring/src/main/java/com/bugsnag/BugsnagAsyncExceptionHandler.java
diff --git a/bugsnag-spring/src/jakarta/java/com/bugsnag/BugsnagJakartaMvcExceptionHandler.java b/bugsnag-spring/src/main/java/com/bugsnag/BugsnagJakartaMvcExceptionHandler.java
similarity index 100%
rename from bugsnag-spring/src/jakarta/java/com/bugsnag/BugsnagJakartaMvcExceptionHandler.java
rename to bugsnag-spring/src/main/java/com/bugsnag/BugsnagJakartaMvcExceptionHandler.java
diff --git a/bugsnag-spring/src/common/java/com/bugsnag/BugsnagScheduledTaskExceptionHandler.java b/bugsnag-spring/src/main/java/com/bugsnag/BugsnagScheduledTaskExceptionHandler.java
similarity index 100%
rename from bugsnag-spring/src/common/java/com/bugsnag/BugsnagScheduledTaskExceptionHandler.java
rename to bugsnag-spring/src/main/java/com/bugsnag/BugsnagScheduledTaskExceptionHandler.java
diff --git a/bugsnag-spring/src/common/java/com/bugsnag/BugsnagSpringConfiguration.java b/bugsnag-spring/src/main/java/com/bugsnag/BugsnagSpringConfiguration.java
similarity index 96%
rename from bugsnag-spring/src/common/java/com/bugsnag/BugsnagSpringConfiguration.java
rename to bugsnag-spring/src/main/java/com/bugsnag/BugsnagSpringConfiguration.java
index 86d503ef..36f70550 100644
--- a/bugsnag-spring/src/common/java/com/bugsnag/BugsnagSpringConfiguration.java
+++ b/bugsnag-spring/src/main/java/com/bugsnag/BugsnagSpringConfiguration.java
@@ -15,7 +15,7 @@
* Configuration to integrate Bugsnag with Spring.
*/
@Configuration
-@Import(BugsnagImportSelector.class)
+@Import({SpringBootJakartaConfiguration.class, JakartaMvcConfiguration.class, ScheduledTaskConfiguration.class})
public class BugsnagSpringConfiguration implements InitializingBean {
@Autowired
diff --git a/bugsnag-spring/src/common/java/com/bugsnag/ExceptionClassCallback.java b/bugsnag-spring/src/main/java/com/bugsnag/ExceptionClassCallback.java
similarity index 100%
rename from bugsnag-spring/src/common/java/com/bugsnag/ExceptionClassCallback.java
rename to bugsnag-spring/src/main/java/com/bugsnag/ExceptionClassCallback.java
diff --git a/bugsnag-spring/src/jakarta/java/com/bugsnag/JakartaMvcConfiguration.java b/bugsnag-spring/src/main/java/com/bugsnag/JakartaMvcConfiguration.java
similarity index 100%
rename from bugsnag-spring/src/jakarta/java/com/bugsnag/JakartaMvcConfiguration.java
rename to bugsnag-spring/src/main/java/com/bugsnag/JakartaMvcConfiguration.java
diff --git a/bugsnag-spring/src/common/java/com/bugsnag/ScheduledTaskBeanLocator.java b/bugsnag-spring/src/main/java/com/bugsnag/ScheduledTaskBeanLocator.java
similarity index 100%
rename from bugsnag-spring/src/common/java/com/bugsnag/ScheduledTaskBeanLocator.java
rename to bugsnag-spring/src/main/java/com/bugsnag/ScheduledTaskBeanLocator.java
diff --git a/bugsnag-spring/src/main/java/com/bugsnag/ScheduledTaskConfiguration.java b/bugsnag-spring/src/main/java/com/bugsnag/ScheduledTaskConfiguration.java
new file mode 100644
index 00000000..9188d7f9
--- /dev/null
+++ b/bugsnag-spring/src/main/java/com/bugsnag/ScheduledTaskConfiguration.java
@@ -0,0 +1,197 @@
+package com.bugsnag;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.aop.framework.AopProxyUtils;
+import org.springframework.aop.support.AopUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.scheduling.TaskScheduler;
+import org.springframework.scheduling.annotation.SchedulingConfigurer;
+import org.springframework.scheduling.concurrent.ConcurrentTaskScheduler;
+import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
+import org.springframework.scheduling.config.ScheduledTaskRegistrar;
+import org.springframework.util.ErrorHandler;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.util.concurrent.ScheduledExecutorService;
+
+/**
+ * Add configuration for reporting unhandled exceptions for scheduled tasks.
+ */
+@Configuration
+class ScheduledTaskConfiguration implements SchedulingConfigurer {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(ScheduledTaskConfiguration.class);
+
+ @Autowired
+ private Bugsnag bugsnag;
+
+ @Autowired
+ private ScheduledTaskBeanLocator beanLocator;
+
+ /**
+ * Optional: if the app defines a dedicated ErrorHandler bean for scheduled tasks
+ * (e.g. your @MockBean(name = "scheduledTaskErrorHandler") in tests), we can
+ * still chain it when we replace or wrap the scheduler.
+ */
+ @Autowired(required = false)
+ @Qualifier("scheduledTaskErrorHandler")
+ private ErrorHandler scheduledTaskErrorHandlerBean;
+
+ /**
+ * Add Bugsnag error handling to the task scheduler being used by Spring.
+ */
+ @Override
+ public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
+ BugsnagScheduledTaskExceptionHandler bugsnagErrorHandler =
+ new BugsnagScheduledTaskExceptionHandler(bugsnag);
+
+ // Decision process for finding a TaskScheduler, in order of preference:
+ // 1. use the scheduler from the task registrar
+ // 2. search for a TaskScheduler bean, by type, then by name
+ // 3. search for a ScheduledExecutorService bean by type, then by name, and wrap it
+ // 4. create our own TaskScheduler
+ TaskScheduler registrarScheduler = taskRegistrar.getScheduler();
+ TaskScheduler taskScheduler = registrarScheduler != null
+ ? registrarScheduler
+ : beanLocator.resolveTaskScheduler();
+
+ if (taskScheduler != null) {
+ // Spring Boot 3 creates a TaskSchedulerRouter which cannot be configured.
+ // In this case, create our own scheduler instead (but preserve any bean-level handler).
+ String schedulerClassName = taskScheduler.getClass().getName();
+ if (schedulerClassName.equals("org.springframework.scheduling.config.TaskSchedulerRouter")) {
+ ScheduledExecutorService executorService = beanLocator.resolveScheduledExecutorService();
+ chainExistingBeanHandlerIfPresent(bugsnagErrorHandler);
+ taskScheduler = createNewTaskScheduler(executorService, bugsnagErrorHandler);
+ taskRegistrar.setScheduler(taskScheduler);
+ return;
+ }
+
+ // If it's a proxy, unwrap to the target to allow reflection / method calls.
+ if (AopUtils.isAopProxy(taskScheduler)) {
+ Class> targetClass = AopProxyUtils.ultimateTargetClass(taskScheduler);
+ if (TaskScheduler.class.isAssignableFrom(targetClass)) {
+ TaskScheduler target = (TaskScheduler) AopProxyUtils.getSingletonTarget(taskScheduler);
+ if (target != null) {
+ taskScheduler = target;
+ }
+ }
+ }
+
+ configureExistingTaskScheduler(taskScheduler, bugsnagErrorHandler);
+ } else {
+ // No scheduler has been defined by the application, create one and add the Bugsnag error handler.
+ ScheduledExecutorService executorService = beanLocator.resolveScheduledExecutorService();
+ chainExistingBeanHandlerIfPresent(bugsnagErrorHandler);
+ taskScheduler = createNewTaskScheduler(executorService, bugsnagErrorHandler);
+ taskRegistrar.setScheduler(taskScheduler);
+ }
+ }
+
+ private TaskScheduler createNewTaskScheduler(
+ ScheduledExecutorService executorService,
+ BugsnagScheduledTaskExceptionHandler errorHandler
+ ) {
+ if (executorService != null) {
+ // Create a task scheduler which delegates to the existing Executor
+ ConcurrentTaskScheduler scheduler = new ConcurrentTaskScheduler(executorService);
+ scheduler.setErrorHandler(errorHandler);
+ return scheduler;
+ } else {
+ // If no task scheduler has been defined by the application, create one and add the Bugsnag error handler.
+ ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler();
+ scheduler.setErrorHandler(errorHandler);
+ scheduler.initialize();
+ return scheduler;
+ }
+ }
+
+ /**
+ * If a task scheduler has been defined by the application, configure it so that Bugsnag error handling is added.
+ * We first capture any existing ErrorHandler (e.g. your mock bean), chain it into the Bugsnag handler,
+ * then set the Bugsnag handler via setter if available (Boot 3+) or via field reflection.
+ */
+ private void configureExistingTaskScheduler(
+ TaskScheduler taskScheduler,
+ BugsnagScheduledTaskExceptionHandler errorHandler
+ ) {
+ // (1) Capture whatever handler is already configured on the scheduler
+ ErrorHandler existing = extractExistingErrorHandler(taskScheduler);
+ if (existing != null) {
+ errorHandler.setExistingErrorHandler(existing);
+ } else if (scheduledTaskErrorHandlerBean != null) {
+ // Fallback: chain the bean-level handler if the scheduler didn't have one yet
+ errorHandler.setExistingErrorHandler(scheduledTaskErrorHandlerBean);
+ }
+
+ // (2) Install the Bugsnag handler via public setter if available, else via private field
+ if (trySetErrorHandlerViaMethod(taskScheduler, errorHandler)) {
+ return;
+ }
+ trySetErrorHandlerViaField(taskScheduler, errorHandler);
+ }
+
+ /**
+ * Chain the dedicated bean-level handler if present (useful when we replace the scheduler).
+ */
+ private void chainExistingBeanHandlerIfPresent(BugsnagScheduledTaskExceptionHandler errorHandler) {
+ if (scheduledTaskErrorHandlerBean != null) {
+ errorHandler.setExistingErrorHandler(scheduledTaskErrorHandlerBean);
+ }
+ }
+
+ /**
+ * Prefer a public getter if present; otherwise fall back to private field.
+ */
+ private ErrorHandler extractExistingErrorHandler(TaskScheduler taskScheduler) {
+ // Try public getter (present on ThreadPoolTaskScheduler et al.)
+ try {
+ Method getter = taskScheduler.getClass().getMethod("getErrorHandler");
+ Object val = getter.invoke(taskScheduler);
+ if (val instanceof ErrorHandler) {
+ return (ErrorHandler) val;
+ }
+ } catch (Throwable ignore) {
+ // no-op
+ }
+
+ // Fall back to private field access
+ try {
+ Field fld = taskScheduler.getClass().getDeclaredField("errorHandler");
+ fld.setAccessible(true);
+ Object val = fld.get(taskScheduler);
+ if (val instanceof ErrorHandler) {
+ return (ErrorHandler) val;
+ }
+ } catch (Throwable ignore) {
+ // no-op
+ }
+
+ return null;
+ }
+
+ private boolean trySetErrorHandlerViaMethod(TaskScheduler taskScheduler, ErrorHandler handler) {
+ try {
+ Method setter = taskScheduler.getClass().getMethod("setErrorHandler", ErrorHandler.class);
+ setter.invoke(taskScheduler, handler);
+ return true;
+ } catch (Throwable ex) {
+ return false;
+ }
+ }
+
+ private boolean trySetErrorHandlerViaField(TaskScheduler taskScheduler, ErrorHandler handler) {
+ try {
+ Field fld = taskScheduler.getClass().getDeclaredField("errorHandler");
+ fld.setAccessible(true);
+ fld.set(taskScheduler, handler);
+ return true;
+ } catch (Throwable ex) {
+ return false;
+ }
+ }
+}
diff --git a/bugsnag-spring/src/common/java/com/bugsnag/SpringBootConfiguration.java b/bugsnag-spring/src/main/java/com/bugsnag/SpringBootConfiguration.java
similarity index 100%
rename from bugsnag-spring/src/common/java/com/bugsnag/SpringBootConfiguration.java
rename to bugsnag-spring/src/main/java/com/bugsnag/SpringBootConfiguration.java
diff --git a/bugsnag-spring/src/jakarta/java/com/bugsnag/SpringBootJakartaConfiguration.java b/bugsnag-spring/src/main/java/com/bugsnag/SpringBootJakartaConfiguration.java
similarity index 78%
rename from bugsnag-spring/src/jakarta/java/com/bugsnag/SpringBootJakartaConfiguration.java
rename to bugsnag-spring/src/main/java/com/bugsnag/SpringBootJakartaConfiguration.java
index c9f0d413..06e02edf 100644
--- a/bugsnag-spring/src/jakarta/java/com/bugsnag/SpringBootJakartaConfiguration.java
+++ b/bugsnag-spring/src/main/java/com/bugsnag/SpringBootJakartaConfiguration.java
@@ -16,9 +16,8 @@
class SpringBootJakartaConfiguration extends SpringBootConfiguration {
/**
- * The {@link com.bugsnag.servlet.javax.BugsnagServletContainerInitializer} does not work for Spring Boot, need to
- * register the {@link BugsnagServletRequestListener} using a Spring Boot
- * {@link ServletListenerRegistrationBean} instead. This adds session tracking and
+ * Spring Boot requires manual registration of the {@link BugsnagServletRequestListener} using a Spring Boot
+ * {@link ServletListenerRegistrationBean}. This adds session tracking and
* automatic servlet request metadata collection.
*/
@Bean
diff --git a/bugsnag-spring/src/common/java/com/bugsnag/SpringBootLoadedCondition.java b/bugsnag-spring/src/main/java/com/bugsnag/SpringBootLoadedCondition.java
similarity index 100%
rename from bugsnag-spring/src/common/java/com/bugsnag/SpringBootLoadedCondition.java
rename to bugsnag-spring/src/main/java/com/bugsnag/SpringBootLoadedCondition.java
diff --git a/bugsnag-spring/src/common/java/com/bugsnag/SpringWebMvcLoadedCondition.java b/bugsnag-spring/src/main/java/com/bugsnag/SpringWebMvcLoadedCondition.java
similarity index 100%
rename from bugsnag-spring/src/common/java/com/bugsnag/SpringWebMvcLoadedCondition.java
rename to bugsnag-spring/src/main/java/com/bugsnag/SpringWebMvcLoadedCondition.java
diff --git a/bugsnag-spring/src/jakartaTest/java/com/bugsnag/NotifierTest.java b/bugsnag-spring/src/test/java/com/bugsnag/NotifierTest.java
similarity index 100%
rename from bugsnag-spring/src/jakartaTest/java/com/bugsnag/NotifierTest.java
rename to bugsnag-spring/src/test/java/com/bugsnag/NotifierTest.java
diff --git a/bugsnag-spring/src/jakartaTest/java/com/bugsnag/ScheduledTaskBeanLocatorTest.java b/bugsnag-spring/src/test/java/com/bugsnag/ScheduledTaskBeanLocatorTest.java
similarity index 100%
rename from bugsnag-spring/src/jakartaTest/java/com/bugsnag/ScheduledTaskBeanLocatorTest.java
rename to bugsnag-spring/src/test/java/com/bugsnag/ScheduledTaskBeanLocatorTest.java
diff --git a/bugsnag-spring/src/jakartaTest/java/com/bugsnag/ScheduledTaskConfigurationTest.java b/bugsnag-spring/src/test/java/com/bugsnag/ScheduledTaskConfigurationTest.java
similarity index 100%
rename from bugsnag-spring/src/jakartaTest/java/com/bugsnag/ScheduledTaskConfigurationTest.java
rename to bugsnag-spring/src/test/java/com/bugsnag/ScheduledTaskConfigurationTest.java
diff --git a/bugsnag-spring/src/jakartaTest/java/com/bugsnag/SpringAsyncTest.java b/bugsnag-spring/src/test/java/com/bugsnag/SpringAsyncTest.java
similarity index 100%
rename from bugsnag-spring/src/jakartaTest/java/com/bugsnag/SpringAsyncTest.java
rename to bugsnag-spring/src/test/java/com/bugsnag/SpringAsyncTest.java
diff --git a/bugsnag-spring/src/jakartaTest/java/com/bugsnag/SpringMvcTest.java b/bugsnag-spring/src/test/java/com/bugsnag/SpringMvcTest.java
similarity index 99%
rename from bugsnag-spring/src/jakartaTest/java/com/bugsnag/SpringMvcTest.java
rename to bugsnag-spring/src/test/java/com/bugsnag/SpringMvcTest.java
index ed03f19f..6acb31a1 100644
--- a/bugsnag-spring/src/jakartaTest/java/com/bugsnag/SpringMvcTest.java
+++ b/bugsnag-spring/src/test/java/com/bugsnag/SpringMvcTest.java
@@ -136,7 +136,7 @@ public void requestMetadataSetCorrectly() {
// Check that the request metadata is set as expected
@SuppressWarnings(value = "unchecked") Map requestMetadata =
- (Map) report.getMetaData().get("request");
+ (Map) report.getMetadata().get("request");
assertEquals("http://localhost:" + randomServerPort + "/throw-runtime-exception",
requestMetadata.get("url"));
assertEquals("GET", requestMetadata.get("method"));
diff --git a/bugsnag-spring/src/jakartaTest/java/com/bugsnag/SpringScheduledTaskTest.java b/bugsnag-spring/src/test/java/com/bugsnag/SpringScheduledTaskTest.java
similarity index 100%
rename from bugsnag-spring/src/jakartaTest/java/com/bugsnag/SpringScheduledTaskTest.java
rename to bugsnag-spring/src/test/java/com/bugsnag/SpringScheduledTaskTest.java
diff --git a/bugsnag-spring/src/commonTest/java/com/bugsnag/TestUtils.java b/bugsnag-spring/src/test/java/com/bugsnag/TestUtils.java
similarity index 100%
rename from bugsnag-spring/src/commonTest/java/com/bugsnag/TestUtils.java
rename to bugsnag-spring/src/test/java/com/bugsnag/TestUtils.java
diff --git a/bugsnag-spring/src/jakartaTest/java/com/bugsnag/testapp/springboot/AsyncService.java b/bugsnag-spring/src/test/java/com/bugsnag/testapp/springboot/AsyncService.java
similarity index 100%
rename from bugsnag-spring/src/jakartaTest/java/com/bugsnag/testapp/springboot/AsyncService.java
rename to bugsnag-spring/src/test/java/com/bugsnag/testapp/springboot/AsyncService.java
diff --git a/bugsnag-spring/src/jakartaTest/java/com/bugsnag/testapp/springboot/TestConfiguration.java b/bugsnag-spring/src/test/java/com/bugsnag/testapp/springboot/TestConfiguration.java
similarity index 100%
rename from bugsnag-spring/src/jakartaTest/java/com/bugsnag/testapp/springboot/TestConfiguration.java
rename to bugsnag-spring/src/test/java/com/bugsnag/testapp/springboot/TestConfiguration.java
diff --git a/bugsnag-spring/src/jakartaTest/java/com/bugsnag/testapp/springboot/TestController.java b/bugsnag-spring/src/test/java/com/bugsnag/testapp/springboot/TestController.java
similarity index 100%
rename from bugsnag-spring/src/jakartaTest/java/com/bugsnag/testapp/springboot/TestController.java
rename to bugsnag-spring/src/test/java/com/bugsnag/testapp/springboot/TestController.java
diff --git a/bugsnag-spring/src/jakartaTest/java/com/bugsnag/testapp/springboot/TestSpringBootApplication.java b/bugsnag-spring/src/test/java/com/bugsnag/testapp/springboot/TestSpringBootApplication.java
similarity index 100%
rename from bugsnag-spring/src/jakartaTest/java/com/bugsnag/testapp/springboot/TestSpringBootApplication.java
rename to bugsnag-spring/src/test/java/com/bugsnag/testapp/springboot/TestSpringBootApplication.java
diff --git a/bugsnag/build.gradle b/bugsnag/build.gradle
deleted file mode 100644
index 140e8a75..00000000
--- a/bugsnag/build.gradle
+++ /dev/null
@@ -1,53 +0,0 @@
-plugins {
- id "com.github.hierynomus.license" version "0.16.1"
-}
-
-apply plugin: 'java-library'
-apply from: '../common.gradle'
-
-compileJava {
- sourceCompatibility = '1.7'
- targetCompatibility = '1.7'
-}
-
-compileTestJava {
- sourceCompatibility = '1.7'
- targetCompatibility = '1.7'
-}
-
-repositories {
- mavenCentral()
-}
-
-dependencies {
- api "com.fasterxml.jackson.core:jackson-databind:2.14.1"
- api "org.slf4j:slf4j-api:${slf4jApiVersion}"
- compileOnly "javax.servlet:javax.servlet-api:${javaxServletApiVersion}"
- compileOnly "jakarta.servlet:jakarta.servlet-api:${jakartaServletApiVersion}"
- compileOnly("ch.qos.logback:logback-classic:${logbackVersion}") {
- exclude group: "org.slf4j"
- }
-
- testImplementation "junit:junit:${junitVersion}"
- testImplementation "org.slf4j:log4j-over-slf4j:${slf4jApiVersion}"
- testImplementation "javax.servlet:javax.servlet-api:${javaxServletApiVersion}"
- testImplementation "jakarta.servlet:jakarta.servlet-api:${jakartaServletApiVersion}"
- testImplementation "org.mockito:mockito-core:${mockitoVersion}"
- testImplementation("ch.qos.logback:logback-classic:${logbackVersion}") {
- exclude group: "org.slf4j"
- }
-}
-
-// license checking
-license {
- header rootProject.file('LICENSE')
- ignoreFailures true
-}
-
-downloadLicenses {
- dependencyConfiguration "compile"
-}
-
-java {
- withJavadocJar()
-}
\ No newline at end of file
diff --git a/bugsnag/build.gradle.kts b/bugsnag/build.gradle.kts
new file mode 100644
index 00000000..8b0845c4
--- /dev/null
+++ b/bugsnag/build.gradle.kts
@@ -0,0 +1,57 @@
+plugins {
+ alias(libs.plugins.license)
+ `java-library`
+}
+
+apply(from = "../common.gradle.kts")
+
+java {
+ toolchain {
+ languageVersion.set(JavaLanguageVersion.of(17))
+ }
+}
+
+repositories {
+ mavenCentral()
+}
+
+dependencies {
+ api(libs.jackson.databind)
+ api(libs.slf4j.api)
+ compileOnly(libs.jakarta.servlet.api)
+ compileOnly(libs.logback.classic) {
+ exclude(group = "org.slf4j")
+ }
+
+ testImplementation(libs.junit)
+ testImplementation(libs.log4j.over.slf4j)
+ testImplementation(libs.jakarta.servlet.api)
+ testImplementation(libs.mockito.core)
+ testImplementation(libs.logback.classic) {
+ exclude(group = "org.slf4j")
+ }
+}
+
+// license checking
+configure {
+ header = rootProject.file("LICENSE")
+ isIgnoreFailures = true
+}
+
+tasks.named("downloadLicenses") {
+ // Note: dependencyConfiguration property needs to be set through the plugin's DSL
+ // This may require checking the plugin documentation for Kotlin DSL syntax
+}
+
+java {
+ withJavadocJar()
+}
+
+/** ---- Publishing config ----
+ * Pulls in publishing+signing rules from the shared release.gradle.kts.
+ * This will create tasks like:
+ * :bugsnag:publishMavenJavaPublicationToTestRepository
+ * :bugsnag:publishMavenJavaPublicationToOssrhStagingRepository
+ */
+apply(from = "${rootProject.projectDir}/release.gradle.kts")
+
diff --git a/bugsnag/src/main/java/com/bugsnag/Bugsnag.java b/bugsnag/src/main/java/com/bugsnag/Bugsnag.java
index abfaffd0..635a0439 100644
--- a/bugsnag/src/main/java/com/bugsnag/Bugsnag.java
+++ b/bugsnag/src/main/java/com/bugsnag/Bugsnag.java
@@ -58,10 +58,10 @@ public void rejectedExecution(Runnable runnable, ThreadPoolExecutor executor) {
private Configuration config;
private final SessionTracker sessionTracker;
- private static final ThreadLocal THREAD_METADATA = new ThreadLocal() {
+ private static final ThreadLocal THREAD_METADATA = new ThreadLocal() {
@Override
- public MetaData initialValue() {
- return new MetaData();
+ public Metadata initialValue() {
+ return new Metadata();
}
};
@@ -228,36 +228,40 @@ public void setEndpoint(String endpoint) {
}
/**
- * Set which keys should be filtered when sending metaData to Bugsnag.
+ * Set which keys should be redacted when sending metadata to Bugsnag.
* Use this when you want to ensure sensitive information, such as passwords
- * or credit card information is stripped from metaData you send to Bugsnag.
- * Any keys in metaData which contain these strings will be marked as
- * [FILTERED] when send to Bugsnag.
+ * or credit card information is stripped from metadata you send to Bugsnag.
+ * Any keys in metadata which contain these strings will be marked as
+ * [REDACTED] when send to Bugsnag.
*
- * @param filters a list of String keys to filter from metaData
+ * @param redactedKeys a list of String keys to redact from metadata
*/
- public void setFilters(String... filters) {
- config.filters = filters;
+ public void setRedactedKeys(String... redactedKeys) {
+ config.redactedKeys = redactedKeys;
}
/**
* Set which exception classes should be ignored (not sent) by Bugsnag.
*
- * @param ignoreClasses a list of exception classes to ignore
+ * @param discardClasses a list of exception classes to ignore
*/
- public void setIgnoreClasses(String... ignoreClasses) {
- config.ignoreClasses = ignoreClasses;
+ public void setDiscardClasses(String... discardClasses) {
+ config.discardClasses = discardClasses;
}
/**
* Set for which releaseStages errors should be sent to Bugsnag.
* Use this to stop errors from development builds being sent.
*
- * @param notifyReleaseStages a list of releaseStages to notify for
+ * @param enabledReleaseStages a list of releaseStages to notify for
* @see #setReleaseStage
*/
- public void setNotifyReleaseStages(String... notifyReleaseStages) {
- config.notifyReleaseStages = notifyReleaseStages;
+ public void setEnabledReleaseStages(String... enabledReleaseStages) {
+ if (enabledReleaseStages == null || enabledReleaseStages.length == 0) {
+ config.enabledReleaseStages = Collections.emptySet();
+ } else {
+ config.enabledReleaseStages = Set.of(enabledReleaseStages);
+ }
}
/**
@@ -289,7 +293,7 @@ public void setProxy(Proxy proxy) {
* Set the current "release stage" of your application.
*
* @param releaseStage the release stage of the app
- * @see #setNotifyReleaseStages
+ * @see #setEnabledReleaseStages
*/
public void setReleaseStage(String releaseStage) {
config.releaseStage = releaseStage;
@@ -302,14 +306,15 @@ public void setReleaseStage(String releaseStage) {
* environment.
*
* @param sendThreads should we send thread state with error reports
- * @see #setNotifyReleaseStages
+ * @see #setEnabledReleaseStages
*/
public void setSendThreads(boolean sendThreads) {
config.sendThreads = sendThreads;
}
/**
- * Set a timeout (in ms) to use when delivering Bugsnag error reports and sessions.
+ * Set a timeout (in ms) to use when delivering Bugsnag error reports and
+ * sessions.
* This is a convenient shorthand for bugsnag.getDelivery().setTimeout();
*
* @param timeout the timeout to set (in ms)
@@ -435,14 +440,14 @@ public boolean notify(Report report, Callback reportCallback) {
// Don't notify if this error class should be ignored
if (config.shouldIgnoreClass(report.getExceptionName())) {
- LOGGER.debug("Error not reported to Bugsnag - {} is in 'ignoreClasses'",
+ LOGGER.debug("Error not reported to Bugsnag - {} is in 'discardClasses'",
report.getExceptionName());
return false;
}
- // Don't notify unless releaseStage is in notifyReleaseStages
+ // Don't notify unless releaseStage is in enabledReleaseStages
if (!config.shouldNotifyForReleaseStage()) {
- LOGGER.debug("Error not reported to Bugsnag - {} is not in 'notifyReleaseStages'",
+ LOGGER.debug("Error not reported to Bugsnag - {} is not in 'enabledReleaseStages'",
config.releaseStage);
return false;
}
@@ -465,7 +470,7 @@ public boolean notify(Report report, Callback reportCallback) {
}
// Add thread metadata to the report
- report.mergeMetaData(THREAD_METADATA.get());
+ report.mergeMetadata(THREAD_METADATA.get());
// Run the report-specific beforeNotify callback, if given
if (reportCallback != null) {
@@ -618,14 +623,14 @@ public void close() {
* @param key the key of the metadata to add
* @param value the metadata value to add
*/
- public static void addThreadMetaData(String tabName, String key, Object value) {
+ public static void addThreadMetadata(String tabName, String key, Object value) {
THREAD_METADATA.get().addToTab(tabName, key, value);
}
/**
* Clears all metadata added to the current thread
*/
- public static void clearThreadMetaData() {
+ public static void clearThreadMetadata() {
THREAD_METADATA.get().clear();
}
@@ -634,7 +639,7 @@ public static void clearThreadMetaData() {
*
* @param tabName the name of the tab to remove
*/
- public static void clearThreadMetaData(String tabName) {
+ public static void clearThreadMetadata(String tabName) {
THREAD_METADATA.get().clearTab(tabName);
}
@@ -644,7 +649,7 @@ public static void clearThreadMetaData(String tabName) {
* @param tabName the name of the tab to that the metadata is in
* @param key the key of the metadata to remove
*/
- public static void clearThreadMetaData(String tabName, String key) {
+ public static void clearThreadMetadata(String tabName, String key) {
THREAD_METADATA.get().clearKey(tabName, key);
}
diff --git a/bugsnag/src/main/java/com/bugsnag/BugsnagAppender.java b/bugsnag/src/main/java/com/bugsnag/BugsnagAppender.java
index 5d3cd23d..c65df317 100644
--- a/bugsnag/src/main/java/com/bugsnag/BugsnagAppender.java
+++ b/bugsnag/src/main/java/com/bugsnag/BugsnagAppender.java
@@ -3,9 +3,9 @@
import com.bugsnag.callbacks.Callback;
import com.bugsnag.delivery.Delivery;
import com.bugsnag.logback.BugsnagMarker;
-import com.bugsnag.logback.LogbackMetaData;
-import com.bugsnag.logback.LogbackMetaDataKey;
-import com.bugsnag.logback.LogbackMetaDataTab;
+import com.bugsnag.logback.LogbackMetadata;
+import com.bugsnag.logback.LogbackMetadataKey;
+import com.bugsnag.logback.LogbackMetadataTab;
import com.bugsnag.logback.ProxyConfiguration;
import ch.qos.logback.classic.Level;
@@ -48,14 +48,14 @@ public class BugsnagAppender extends UnsynchronizedAppenderBase {
/** Bugsnag error server endpoint. */
private String endpoint;
- /** Property names that should be filtered out before sending to Bugsnag servers. */
- private Set filteredProperties = new HashSet();
+ /** Property names that should be redacted before sending to Bugsnag servers. */
+ private Set redactedKeys = new HashSet();
/** Exception classes to be ignored. */
- private Set ignoredClasses = new HashSet();
+ private Set discardClasses = new HashSet();
/** Release stages that should be notified. */
- private Set notifyReleaseStages = new HashSet();
+ private Set enabledReleaseStages = new HashSet();
/** Project packages. */
private Set projectPackages = new HashSet();
@@ -75,7 +75,7 @@ public class BugsnagAppender extends UnsynchronizedAppenderBase {
/** Application version. */
private String appVersion;
- private List globalMetaData = new ArrayList();
+ private List globalMetadata = new ArrayList();
/** Bugsnag client. */
private Bugsnag bugsnag = null;
@@ -254,27 +254,27 @@ private Bugsnag createBugsnag() {
bugsnag.setTimeout(timeout);
}
- if (filteredProperties.size() > 0) {
- bugsnag.setFilters(filteredProperties.toArray(new String[0]));
+ if (!redactedKeys.isEmpty()) {
+ bugsnag.setRedactedKeys(redactedKeys.toArray(new String[0]));
}
- bugsnag.setIgnoreClasses(ignoredClasses.toArray(new String[0]));
+ bugsnag.setDiscardClasses(discardClasses.toArray(new String[0]));
- if (notifyReleaseStages.size() > 0) {
- bugsnag.setNotifyReleaseStages(notifyReleaseStages.toArray(new String[0]));
+ if (!enabledReleaseStages.isEmpty()) {
+ bugsnag.setEnabledReleaseStages(enabledReleaseStages.toArray(new String[0]));
}
bugsnag.setProjectPackages(projectPackages.toArray(new String[0]));
bugsnag.setSendThreads(sendThreads);
- // Add a callback to put global meta data on every report
+ // Add a callback to put global metadata on every report
bugsnag.addCallback(new Callback() {
@Override
public void beforeNotify(Report report) {
- for (LogbackMetaData metaData : globalMetaData) {
- for (LogbackMetaDataTab tab : metaData.getTabs()) {
- for (LogbackMetaDataKey key : tab.getKeys()) {
+ for (LogbackMetadata metadata : globalMetadata) {
+ for (LogbackMetadataTab tab : metadata.getTabs()) {
+ for (LogbackMetadataKey key : tab.getKeys()) {
report.addToTab(tab.getName(),
key.getName(),
key.getValue());
@@ -374,68 +374,78 @@ public void setEndpoint(String endpoint) {
}
/**
- * @see Bugsnag#setFilters(String...)
+ * @see Bugsnag#setRedactedKeys(String...)
*/
- public void setFilteredProperty(String filter) {
- this.filteredProperties.add(filter);
+ public void setRedactedKey(String key) {
+ this.redactedKeys.add(key);
if (bugsnag != null) {
- bugsnag.setFilters(this.filteredProperties.toArray(new String[0]));
+ bugsnag.setRedactedKeys(this.redactedKeys.toArray(new String[0]));
}
}
/**
- * @see Bugsnag#setFilters(String...)
+ * @see Bugsnag#setRedactedKeys(String...)
*/
- public void setFilteredProperties(String filters) {
- this.filteredProperties.addAll(split(filters));
+ public void setRedactedKeys(String key) {
+ this.redactedKeys.addAll(split(key));
if (bugsnag != null) {
- bugsnag.setFilters(this.filteredProperties.toArray(new String[0]));
+ bugsnag.setRedactedKeys(this.redactedKeys.toArray(new String[0]));
}
}
+ @Deprecated
+ public void setIgnoredClass(String ignoredClass) {
+ setDiscardClass(ignoredClass);
+ }
+
/**
- * @see Bugsnag#setIgnoreClasses(String...)
+ * @see Bugsnag#setDiscardClasses(String...)
*/
- public void setIgnoredClass(String ignoredClass) {
- this.ignoredClasses.add(ignoredClass);
+ public void setDiscardClass(String discardClass) {
+ this.discardClasses.add(discardClass);
if (bugsnag != null) {
- bugsnag.setIgnoreClasses(this.ignoredClasses.toArray(new String[0]));
+ bugsnag.setDiscardClasses(this.discardClasses.toArray(new String[0]));
}
}
/**
- * @see Bugsnag#setIgnoreClasses(String...)
+ * @see Bugsnag#setDiscardClasses(String...)
*/
- public void setIgnoredClasses(String ignoredClasses) {
- this.ignoredClasses.addAll(split(ignoredClasses));
+ public void setDiscardClasses(String discardClasses) {
+ this.discardClasses.addAll(split(discardClasses));
if (bugsnag != null) {
- bugsnag.setIgnoreClasses(this.ignoredClasses.toArray(new String[0]));
+ bugsnag.setDiscardClasses(this.discardClasses.toArray(new String[0]));
}
}
+ @Deprecated
+ public void setNotifyReleaseStage(String notifyReleaseStage) {
+ setEnabledReleaseStage(notifyReleaseStage);
+ }
+
/**
- * @see Bugsnag#setNotifyReleaseStages(String...)
+ * @see Bugsnag#setEnabledReleaseStages(String...)
*/
- public void setNotifyReleaseStage(String notifyReleaseStage) {
- this.notifyReleaseStages.add(notifyReleaseStage);
+ public void setEnabledReleaseStage(String enabledReleaseStage) {
+ this.enabledReleaseStages.add(enabledReleaseStage);
if (bugsnag != null) {
- bugsnag.setNotifyReleaseStages(this.notifyReleaseStages.toArray(new String[0]));
+ bugsnag.setEnabledReleaseStages(this.enabledReleaseStages.toArray(new String[0]));
}
}
/**
- * @see Bugsnag#setNotifyReleaseStages(String...)
+ * @see Bugsnag#setEnabledReleaseStages(String...)
*/
- public void setNotifyReleaseStages(String notifyReleaseStages) {
- this.notifyReleaseStages.addAll(split(notifyReleaseStages));
+ public void setEnabledReleaseStages(String enabledReleaseStages) {
+ this.enabledReleaseStages.addAll(split(enabledReleaseStages));
if (bugsnag != null) {
- bugsnag.setNotifyReleaseStages(this.notifyReleaseStages.toArray(new String[0]));
+ bugsnag.setEnabledReleaseStages(this.enabledReleaseStages.toArray(new String[0]));
}
}
@@ -524,10 +534,15 @@ public void setAppVersion(String appVersion) {
* Internal use only
* Should only be used via the logback.xml file
*
- * @param metaData Adds meta data to every report
+ * @param metadata Adds metadata to every report
*/
- public void setMetaData(LogbackMetaData metaData) {
- this.globalMetaData.add(metaData);
+ public void setMetadata(LogbackMetadata metadata) {
+ this.globalMetadata.add(metadata);
+ }
+
+ @Deprecated
+ public void setMetaData(LogbackMetadata metadata) {
+ setMetadata(metadata);
}
/**
diff --git a/bugsnag/src/main/java/com/bugsnag/Configuration.java b/bugsnag/src/main/java/com/bugsnag/Configuration.java
index 351c1c0d..d7652574 100644
--- a/bugsnag/src/main/java/com/bugsnag/Configuration.java
+++ b/bugsnag/src/main/java/com/bugsnag/Configuration.java
@@ -4,7 +4,6 @@
import com.bugsnag.callbacks.Callback;
import com.bugsnag.callbacks.DeviceCallback;
import com.bugsnag.callbacks.JakartaServletCallback;
-import com.bugsnag.callbacks.JavaxServletCallback;
import com.bugsnag.delivery.AsyncHttpDelivery;
import com.bugsnag.delivery.Delivery;
import com.bugsnag.delivery.HttpDelivery;
@@ -20,6 +19,7 @@
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import java.util.Set;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.atomic.AtomicBoolean;
@@ -37,9 +37,9 @@ public class Configuration {
public Delivery delivery;
public EndpointConfiguration endpointConfiguration;
public Delivery sessionDelivery;
- public String[] filters = new String[]{"password", "secret", "Authorization", "Cookie"};
- public String[] ignoreClasses;
- public String[] notifyReleaseStages = null;
+ public String[] redactedKeys = new String[] {"password", "secret", "Authorization", "Cookie"};
+ public String[] discardClasses;
+ public Set enabledReleaseStages = null;
public String[] projectPackages;
public String releaseStage;
public boolean sendThreads = false;
@@ -61,30 +61,24 @@ public class Configuration {
this.delivery = new AsyncHttpDelivery(endpointConfiguration.getNotifyEndpoint());
this.sessionDelivery = new AsyncHttpDelivery(endpointConfiguration.getSessionEndpoint());
- if (JavaxServletCallback.isAvailable()) {
- addCallback(new JavaxServletCallback());
- }
-
if (JakartaServletCallback.isAvailable()) {
addCallback(new JakartaServletCallback());
}
}
boolean shouldNotifyForReleaseStage() {
- if (notifyReleaseStages == null) {
+ if (enabledReleaseStages == null) {
return true;
}
-
- List stages = Arrays.asList(notifyReleaseStages);
- return stages.contains(releaseStage);
+ return enabledReleaseStages.contains(releaseStage);
}
boolean shouldIgnoreClass(String className) {
- if (ignoreClasses == null) {
+ if (discardClasses == null) {
return false;
}
- List classes = Arrays.asList(ignoreClasses);
+ List classes = Arrays.asList(discardClasses);
return classes.contains(className);
}
diff --git a/bugsnag/src/main/java/com/bugsnag/Diagnostics.java b/bugsnag/src/main/java/com/bugsnag/Diagnostics.java
index d6aa3e1a..671e3fed 100644
--- a/bugsnag/src/main/java/com/bugsnag/Diagnostics.java
+++ b/bugsnag/src/main/java/com/bugsnag/Diagnostics.java
@@ -11,7 +11,7 @@ class Diagnostics {
Map app;
Map device;
Map user = new HashMap();
- MetaData metaData = new MetaData();
+ Metadata metadata = new Metadata();
Diagnostics(Configuration configuration) {
app = getDefaultAppInfo(configuration);
diff --git a/bugsnag/src/main/java/com/bugsnag/MetaData.java b/bugsnag/src/main/java/com/bugsnag/Metadata.java
similarity index 80%
rename from bugsnag/src/main/java/com/bugsnag/MetaData.java
rename to bugsnag/src/main/java/com/bugsnag/Metadata.java
index 33d215fd..92fd4ea6 100644
--- a/bugsnag/src/main/java/com/bugsnag/MetaData.java
+++ b/bugsnag/src/main/java/com/bugsnag/Metadata.java
@@ -3,7 +3,7 @@
import java.util.HashMap;
import java.util.Map;
-class MetaData extends HashMap {
+class Metadata extends HashMap {
private static final long serialVersionUID = 2530038179702722770L;
public void addToTab(String tabName, String key, Object value) {
@@ -20,9 +20,9 @@ void clearKey(String tabName, String key) {
tab.remove(key);
}
- void merge(MetaData metaData) {
- for (String tabName : metaData.keySet()) {
- getTab(tabName).putAll(metaData.getTab(tabName));
+ void merge(Metadata metadata) {
+ for (String tabName : metadata.keySet()) {
+ getTab(tabName).putAll(metadata.getTab(tabName));
}
}
diff --git a/bugsnag/src/main/java/com/bugsnag/util/FilteredMap.java b/bugsnag/src/main/java/com/bugsnag/RedactedMap.java
similarity index 55%
rename from bugsnag/src/main/java/com/bugsnag/util/FilteredMap.java
rename to bugsnag/src/main/java/com/bugsnag/RedactedMap.java
index 1adc9269..b417137d 100644
--- a/bugsnag/src/main/java/com/bugsnag/util/FilteredMap.java
+++ b/bugsnag/src/main/java/com/bugsnag/RedactedMap.java
@@ -1,24 +1,24 @@
-package com.bugsnag.util;
+package com.bugsnag;
-import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.Map;
import java.util.Set;
/**
- * Decorates a map by replacing values of filtered keys.
+ * Decorates a map by replacing values of redacted keys.
*/
-public class FilteredMap implements Map {
+class RedactedMap implements Map {
- private static final String FILTERED_PLACEHOLDER = "[FILTERED]";
+ private static final String REDACTED_PLACEHOLDER = "[REDACTED]";
- private final Map filteredCopy;
- private final Collection keyFilters = new ArrayList();
+ private final Map redactedCopy;
+ private final Collection redactedKeys = new HashSet<>();
- public FilteredMap(Map map, Collection keyFilters) {
- this.keyFilters.addAll(keyFilters);
- this.filteredCopy = createCopy(map);
+ RedactedMap(Map map, Collection redactedKeys) {
+ this.redactedKeys.addAll(redactedKeys);
+ this.redactedCopy = createCopy(map);
}
private Map createCopy(Map extends String, ?> map) {
@@ -36,84 +36,87 @@ private Map createCopy(Map extends String, ?> map) {
@Override
public int size() {
- return filteredCopy.size();
+ return redactedCopy.size();
}
@Override
public boolean isEmpty() {
- return filteredCopy.isEmpty();
+ return redactedCopy.isEmpty();
}
@Override
public boolean containsKey(Object key) {
- return filteredCopy.containsKey(key);
+ return redactedCopy.containsKey(key);
}
@Override
public boolean containsValue(Object value) {
- return filteredCopy.containsValue(value);
+ return redactedCopy.containsValue(value);
}
@Override
public Object get(Object key) {
- return filteredCopy.get(key);
+ return redactedCopy.get(key);
}
@Override
public Object put(String key, Object value) {
if (value == null) {
- return filteredCopy.put(key, null);
+ return redactedCopy.put(key, null);
}
Object transformedValue = transformEntry(key, value);
- return filteredCopy.put(key, transformedValue);
+ return redactedCopy.put(key, transformedValue);
}
@Override
public Object remove(Object key) {
- return filteredCopy.remove(key);
+ return redactedCopy.remove(key);
}
@Override
public void putAll(Map extends String, ?> mapValues) {
Map copy = createCopy(mapValues);
- filteredCopy.putAll(copy);
+ redactedCopy.putAll(copy);
}
@Override
public void clear() {
- filteredCopy.clear();
+ redactedCopy.clear();
}
@Override
public Set keySet() {
- return filteredCopy.keySet();
+ return redactedCopy.keySet();
}
@Override
public Collection