Custom schedulers to execute @PreDestroy methods (#4716)

Signed-off-by: Jorge Bescos Gascon <jorge.bescos.gascon@oracle.com>
diff --git a/core-client/src/main/java/org/glassfish/jersey/client/ClientExecutorProvidersConfigurator.java b/core-client/src/main/java/org/glassfish/jersey/client/ClientExecutorProvidersConfigurator.java
index 220f4f7..df26b22 100644
--- a/core-client/src/main/java/org/glassfish/jersey/client/ClientExecutorProvidersConfigurator.java
+++ b/core-client/src/main/java/org/glassfish/jersey/client/ClientExecutorProvidersConfigurator.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2017, 2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2017, 2021 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v. 2.0, which is available at
@@ -65,7 +65,6 @@
     @Override
     public void init(InjectionManager injectionManager, BootstrapBag bootstrapBag) {
         Map<String, Object> runtimeProperties = bootstrapBag.getConfiguration().getProperties();
-        ManagedObjectsFinalizer finalizer = bootstrapBag.getManagedObjectsFinalizer();
 
         ExecutorServiceProvider defaultAsyncExecutorProvider;
         ScheduledExecutorServiceProvider defaultScheduledExecutorProvider;
@@ -110,7 +109,6 @@
                 .to(ExecutorServiceProvider.class);
 
         injectionManager.register(executorBinding);
-        finalizer.registerForPreDestroyCall(defaultAsyncExecutorProvider);
 
         final ScheduledExecutorService clientScheduledExecutorService = client.getScheduledExecutorService() == null
                 // scheduled executor service set from {@link ClientConfig}.
@@ -134,9 +132,9 @@
                 .service(defaultScheduledExecutorProvider)
                 .to(ScheduledExecutorServiceProvider.class);
         injectionManager.register(schedulerBinding);
-        finalizer.registerForPreDestroyCall(defaultScheduledExecutorProvider);
 
-        registerExecutors(injectionManager, componentBag, defaultAsyncExecutorProvider, defaultScheduledExecutorProvider);
+        registerExecutors(injectionManager, componentBag, defaultAsyncExecutorProvider,
+                defaultScheduledExecutorProvider, bootstrapBag.getManagedObjectsFinalizer());
     }
 
     private static ExecutorService lookupManagedExecutorService() {
diff --git a/core-common/src/main/java/org/glassfish/jersey/process/internal/AbstractExecutorProvidersConfigurator.java b/core-common/src/main/java/org/glassfish/jersey/process/internal/AbstractExecutorProvidersConfigurator.java
index 7ad252e..2a9c8b4 100644
--- a/core-common/src/main/java/org/glassfish/jersey/process/internal/AbstractExecutorProvidersConfigurator.java
+++ b/core-common/src/main/java/org/glassfish/jersey/process/internal/AbstractExecutorProvidersConfigurator.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2017, 2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2017, 2021 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v. 2.0, which is available at
@@ -24,6 +24,7 @@
 import org.glassfish.jersey.internal.BootstrapConfigurator;
 import org.glassfish.jersey.internal.inject.InjectionManager;
 import org.glassfish.jersey.model.internal.ComponentBag;
+import org.glassfish.jersey.model.internal.ManagedObjectsFinalizer;
 import org.glassfish.jersey.spi.ExecutorServiceProvider;
 import org.glassfish.jersey.spi.ScheduledExecutorServiceProvider;
 
@@ -49,12 +50,14 @@
      * @param componentBag                     provides executor service providers registered by an application.
      * @param defaultAsyncExecutorProvider     default implementation of {@link ExecutorServiceProvider}.
      * @param defaultScheduledExecutorProvider default implementation of {@link ScheduledExecutorServiceProvider}.
+     * @param finalizer                        register finalizers.
      */
     protected void registerExecutors(
             InjectionManager injectionManager,
             ComponentBag componentBag,
             ExecutorServiceProvider defaultAsyncExecutorProvider,
-            ScheduledExecutorServiceProvider defaultScheduledExecutorProvider) {
+            ScheduledExecutorServiceProvider defaultScheduledExecutorProvider,
+            ManagedObjectsFinalizer finalizer) {
 
         List<ExecutorServiceProvider> customExecutors =
                 Stream.concat(
@@ -64,6 +67,7 @@
                         .map(CAST_TO_EXECUTOR_PROVIDER)
                         .collect(Collectors.toList());
         customExecutors.add(defaultAsyncExecutorProvider);
+        customExecutors.stream().forEach(e -> finalizer.registerForPreDestroyCall(e));
 
         List<ScheduledExecutorServiceProvider> customScheduledExecutors =
                 Stream.concat(
@@ -73,6 +77,7 @@
                         .map(CAST_TO_SCHEDULED_EXECUTOR_PROVIDER)
                         .collect(Collectors.toList());
         customScheduledExecutors.add(defaultScheduledExecutorProvider);
+        customScheduledExecutors.stream().forEach(e -> finalizer.registerForPreDestroyCall(e));
 
         ExecutorProviders.registerExecutorBindings(injectionManager, customExecutors, customScheduledExecutors);
     }
diff --git a/core-server/src/main/java/org/glassfish/jersey/server/ServerExecutorProvidersConfigurator.java b/core-server/src/main/java/org/glassfish/jersey/server/ServerExecutorProvidersConfigurator.java
index 5a18a3e..77974f1 100644
--- a/core-server/src/main/java/org/glassfish/jersey/server/ServerExecutorProvidersConfigurator.java
+++ b/core-server/src/main/java/org/glassfish/jersey/server/ServerExecutorProvidersConfigurator.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2017, 2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2017, 2021 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v. 2.0, which is available at
@@ -41,7 +41,6 @@
         ServerBootstrapBag serverBag = (ServerBootstrapBag) bootstrapBag;
         ResourceConfig runtimeConfig = serverBag.getRuntimeConfig();
         ComponentBag componentBag = runtimeConfig.getComponentBag();
-        ManagedObjectsFinalizer finalizer = serverBag.getManagedObjectsFinalizer();
 
         // TODO: Do we need to register DEFAULT Executor and ScheduledExecutor to InjectionManager?
         ScheduledExecutorServiceProvider defaultScheduledExecutorProvider = new DefaultBackgroundSchedulerProvider();
@@ -50,16 +49,15 @@
                 .to(ScheduledExecutorServiceProvider.class)
                 .qualifiedBy(BackgroundSchedulerLiteral.INSTANCE);
         injectionManager.register(schedulerBinding);
-        finalizer.registerForPreDestroyCall(defaultScheduledExecutorProvider);
 
         ExecutorServiceProvider defaultAsyncExecutorProvider = new DefaultManagedAsyncExecutorProvider();
         InstanceBinding<ExecutorServiceProvider> executorBinding = Bindings
                 .service(defaultAsyncExecutorProvider)
                 .to(ExecutorServiceProvider.class);
         injectionManager.register(executorBinding);
-        finalizer.registerForPreDestroyCall(defaultAsyncExecutorProvider);
 
-        registerExecutors(injectionManager, componentBag, defaultAsyncExecutorProvider, defaultScheduledExecutorProvider);
+        registerExecutors(injectionManager, componentBag, defaultAsyncExecutorProvider,
+                defaultScheduledExecutorProvider, serverBag.getManagedObjectsFinalizer());
     }
 
     /**
diff --git a/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/ScheduledThreadPoolExecutorProviderTest.java b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/ScheduledThreadPoolExecutorProviderTest.java
new file mode 100644
index 0000000..4a1a130
--- /dev/null
+++ b/tests/e2e-server/src/test/java/org/glassfish/jersey/tests/e2e/server/ScheduledThreadPoolExecutorProviderTest.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (c) 2021 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package org.glassfish.jersey.tests.e2e.server;
+
+import java.util.concurrent.RejectedExecutionHandler;
+import java.util.concurrent.ScheduledThreadPoolExecutor;
+import java.util.concurrent.ThreadFactory;
+
+import javax.inject.Singleton;
+import javax.ws.rs.core.Application;
+
+import org.glassfish.jersey.server.BackgroundScheduler;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.spi.ScheduledThreadPoolExecutorProvider;
+import org.glassfish.jersey.test.JerseyTest;
+import org.junit.Test;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+public class ScheduledThreadPoolExecutorProviderTest {
+
+    @Test
+    public void restartShutsdownScheduler() throws Exception {
+        WebContainer container = new WebContainer();
+        container.setUp();
+        container.tearDown();
+        assertNotNull(CustomScheduledThreadPoolExecutorProvider.executor);
+        assertTrue(CustomScheduledThreadPoolExecutorProvider.selfRef.isClosed());
+        assertTrue(CustomScheduledThreadPoolExecutorProvider.executor.isShutdown());
+    }
+
+    private static class WebContainer extends JerseyTest {
+        @Override
+        protected Application configure() {
+            return new ResourceConfig(CustomScheduledThreadPoolExecutorProvider.class);
+        }
+    }
+
+    @BackgroundScheduler
+    @Singleton
+    public static class CustomScheduledThreadPoolExecutorProvider extends ScheduledThreadPoolExecutorProvider {
+
+        private static CustomScheduledThreadPoolExecutorProvider selfRef;
+        private static ScheduledThreadPoolExecutor executor;
+
+        public CustomScheduledThreadPoolExecutorProvider() {
+            this("CustomScheduledThreadPoolExecutorProvider");
+        }
+
+        public CustomScheduledThreadPoolExecutorProvider(String name) {
+            super(name);
+            CustomScheduledThreadPoolExecutorProvider.selfRef = this;
+        }
+
+        @Override
+        protected ScheduledThreadPoolExecutor createExecutor(int corePoolSize, ThreadFactory threadFactory,
+                RejectedExecutionHandler handler) {
+            executor = new ScheduledThreadPoolExecutor(5, threadFactory, handler);
+            return executor;
+        }
+    }
+}