[java-shib-shared] 04/09: JSSH-71 Remove the impact of the DestructableComponent Interface

Codeberg noreply at shibboleth.net
Mon Jul 6 15:29:31 UTC 2026


This is an automated email from the git hooks/post-receive script.

codeberg pushed a commit to branch dev/JSSH-71-old
in repository java-shib-shared.

View the commit online:
https://codeberg.org/Shibboleth/java-shib-shared/commit/fa8c89727ca5ccdba1fc4ebfcea7c24e923cd473

commit fa8c89727ca5ccdba1fc4ebfcea7c24e923cd473
Author: Rod Widdowson <rdw at steadingsoftware.com>
AuthorDate: Sat May 23 14:35:12 2026 +0100

    JSSH-71 Remove the impact of the DestructableComponent Interface
    
    https://shibboleth.atlassian.net/browse/JSSH-71
    
    Add explicit code in our Service model to call @PreDestroy method if
    the ServiceableComponent has one.
---
 shib-security/pom.xml                              | 10 +--
 shib-service/pom.xml                               | 10 +--
 .../service/AbstractServiceableComponent.java      | 10 ++-
 .../service/ReloadableSpringServiceTest.java       |  9 +--
 shib-support/pom.xml                               |  4 ++
 .../shared/primitive/AnnotationsSupport.java       | 78 +++++++++++++++++++++
 .../shared/primitive/AnnotationsSupportTest.java   | 79 ++++++++++++++++++++++
 7 files changed, 183 insertions(+), 17 deletions(-)

diff --git a/shib-security/pom.xml b/shib-security/pom.xml
index a39eb298..9614705c 100644
--- a/shib-security/pom.xml
+++ b/shib-security/pom.xml
@@ -59,17 +59,17 @@
             <optional>true</optional>
         </dependency>
 
+        <dependency>
+            <groupId>jakarta.annotation</groupId>
+            <artifactId>jakarta.annotation-api</artifactId>
+        </dependency>
+
         <!-- Provided Dependencies -->
         <dependency>
             <groupId>jakarta.servlet</groupId>
             <artifactId>jakarta.servlet-api</artifactId>
             <scope>provided</scope>
         </dependency>
-        <dependency>
-            <groupId>jakarta.annotation</groupId>
-            <artifactId>jakarta.annotation-api</artifactId>
-            <scope>provided</scope>
-        </dependency>
 
         <!-- Runtime Dependencies -->
 
diff --git a/shib-service/pom.xml b/shib-service/pom.xml
index 741ab07e..1a74ab72 100644
--- a/shib-service/pom.xml
+++ b/shib-service/pom.xml
@@ -73,17 +73,17 @@
             <artifactId>spring-core</artifactId>
         </dependency>
 
+        <dependency>
+            <groupId>jakarta.annotation</groupId>
+            <artifactId>jakarta.annotation-api</artifactId>
+        </dependency>
+
         <!-- Provided Dependencies -->
         <dependency>
             <groupId>jakarta.servlet</groupId>
             <artifactId>jakarta.servlet-api</artifactId>
             <scope>provided</scope>
         </dependency>
-        <dependency>
-            <groupId>jakarta.annotation</groupId>
-            <artifactId>jakarta.annotation-api</artifactId>
-            <scope>provided</scope>
-        </dependency>
 
         <!-- Runtime Dependencies -->
 
diff --git a/shib-service/src/main/java/net/shibboleth/shared/spring/service/AbstractServiceableComponent.java b/shib-service/src/main/java/net/shibboleth/shared/spring/service/AbstractServiceableComponent.java
index 8f4cd929..a4e8780b 100644
--- a/shib-service/src/main/java/net/shibboleth/shared/spring/service/AbstractServiceableComponent.java
+++ b/shib-service/src/main/java/net/shibboleth/shared/spring/service/AbstractServiceableComponent.java
@@ -20,15 +20,14 @@ import javax.annotation.Nonnull;
 import javax.annotation.Nullable;
 
 import org.slf4j.Logger;
-
 import org.springframework.context.ApplicationContext;
 import org.springframework.context.ApplicationContextAware;
 import org.springframework.context.ConfigurableApplicationContext;
 
 import jakarta.annotation.PreDestroy;
-
 import net.shibboleth.shared.component.AbstractIdentifiableInitializableComponent;
 import net.shibboleth.shared.component.ComponentInitializationException;
+import net.shibboleth.shared.primitive.AnnotationsSupport;
 import net.shibboleth.shared.primitive.LoggerFactory;
 import net.shibboleth.shared.service.ServiceableComponent;
 
@@ -125,7 +124,12 @@ public abstract class AbstractServiceableComponent<T> extends AbstractIdentifiab
         }
         // If we were not created by spring we need to do the destroy of ourself.
         // Note that we will end up being called here but will fall out at the top.
-        destroy();
+        //
+        // We do this by looking for the preDetroy annotation on one of our methods).
+        // Worth of note - as opposed to the spring case this will only tearn down us
+        // (not everyone child that needs torn down).  Use spring or add the one off
+        //  yourself.
+        AnnotationsSupport.callPreDestroyAnnotation(this, getId(), log);
     }
 
     /** {@inheritDoc} */
diff --git a/shib-service/src/test/java/net/shibboleth/shared/spring/service/ReloadableSpringServiceTest.java b/shib-service/src/test/java/net/shibboleth/shared/spring/service/ReloadableSpringServiceTest.java
index bba41854..4a825222 100644
--- a/shib-service/src/test/java/net/shibboleth/shared/spring/service/ReloadableSpringServiceTest.java
+++ b/shib-service/src/test/java/net/shibboleth/shared/spring/service/ReloadableSpringServiceTest.java
@@ -26,6 +26,7 @@ import javax.annotation.Nullable;
 
 import net.shibboleth.shared.collection.CollectionSupport;
 import net.shibboleth.shared.component.ComponentInitializationException;
+import net.shibboleth.shared.primitive.AnnotationsSupport;
 import net.shibboleth.shared.service.ServiceException;
 import net.shibboleth.shared.service.ServiceableComponent;
 import net.shibboleth.shared.spring.util.ApplicationContextBuilder;
@@ -117,7 +118,7 @@ public class ReloadableSpringServiceTest {
 
         Assert.assertEquals(serviceableComponent.getComponent().getTheValue(), "Two");
         serviceableComponent.unpinComponent();
-        service.destroy();
+        AnnotationsSupport.callPreDestroyAnnotation(service, null, null);
         
         deleteFile();
     }
@@ -177,8 +178,8 @@ public class ReloadableSpringServiceTest {
             Thread.sleep(RELOAD_DELAY.toMillis());
             count--;
         }
+        AnnotationsSupport.callPreDestroyAnnotation(service, null, null);
         Assert.assertTrue(component.destroyed, "After 7 second initial component has still not be destroyed");
-        service.destroy();
         deleteFile();
     }
 
@@ -218,7 +219,7 @@ public class ReloadableSpringServiceTest {
             // OK
         }
 
-        service.destroy();
+        AnnotationsSupport.callPreDestroyAnnotation(service, null, null);
         deleteFile();
     }
     @Test public void testNotFailFast() throws IOException, InterruptedException, ComponentInitializationException {
@@ -260,7 +261,7 @@ public class ReloadableSpringServiceTest {
 
         Assert.assertFalse(component.destroyed);
         serviceableComponent.unpinComponent();
-        service.destroy();
+        AnnotationsSupport.callPreDestroyAnnotation(service, null, null);
 
         count = 70;
         while (count > 0 && !component.destroyed) {
diff --git a/shib-support/pom.xml b/shib-support/pom.xml
index 231213ec..6444645e 100644
--- a/shib-support/pom.xml
+++ b/shib-support/pom.xml
@@ -29,6 +29,10 @@
             <groupId>com.google.guava</groupId>
             <artifactId>guava</artifactId>
         </dependency>
+        <dependency>
+            <groupId>jakarta.annotation</groupId>
+            <artifactId>jakarta.annotation-api</artifactId>
+        </dependency>
 
         <!-- Provided Dependencies -->
 
diff --git a/shib-support/src/main/java/net/shibboleth/shared/primitive/AnnotationsSupport.java b/shib-support/src/main/java/net/shibboleth/shared/primitive/AnnotationsSupport.java
new file mode 100644
index 00000000..ba21be49
--- /dev/null
+++ b/shib-support/src/main/java/net/shibboleth/shared/primitive/AnnotationsSupport.java
@@ -0,0 +1,78 @@
+/*
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.shibboleth.shared.primitive;
+
+import java.lang.reflect.Method;
+
+import org.slf4j.Logger;
+
+import jakarta.annotation.Nullable;
+import jakarta.annotation.PreDestroy;
+
+/**
+ * Static methods to work with annotations.
+ */
+public final class AnnotationsSupport {
+
+    /** Call the {@link @PreDestroy} methods.
+     * @param obj The object to introspect
+     * @param id The id of the object
+     * @param log the logger to use
+     */
+    static public void callPreDestroyAnnotation(@Nullable final Object obj, @Nullable final String id, @Nullable final Logger log) {
+
+        if (obj == null) {
+            return;
+        }
+        for (final Method m: obj.getClass().getMethods()) {
+            if (m.getParameterCount() == 0 && m.getAnnotation(PreDestroy.class) != null) {
+                if (log != null) {
+                    log.debug("{}: found a PreDestroyMethod {}", id, m);
+                }
+                try {
+                    m.invoke(obj);
+                } catch (final Exception e) {
+                    Logger theLog = log;
+                    if (theLog == null) {
+                        theLog = LoggerFactory.getLogger(AnnotationsSupport.class);
+                    }
+                    theLog.error("{}: Failed invoking {}", id, m, e);
+                }
+            }
+        }
+    }
+    /** Call the {@link @PreDestroy} methods.
+     * @param obj The object to introspect
+     */
+    static public void callPreDestroyAnnotation(@Nullable final Object obj) {
+        callPreDestroyAnnotation(obj, null, null);
+    }
+    /** Mostly for testing - does this object carry a {@link @PreDestroy} annotated
+     * method?
+     * @param obj The object to introspect
+     * @return whether it is tear-downable.
+     */
+    static public boolean hasPreDestroyAnnotation(@Nullable final Object obj) {
+        if (obj == null) {
+            return false;
+        }
+        for (final Method m: obj.getClass().getMethods()) {
+            if (m.getParameterCount() == 0 && m.getAnnotation(PreDestroy.class) != null) {
+                return true;
+            }
+        }
+        return false;
+     }
+}
diff --git a/shib-support/src/test/java/net/shibboleth/shared/primitive/AnnotationsSupportTest.java b/shib-support/src/test/java/net/shibboleth/shared/primitive/AnnotationsSupportTest.java
new file mode 100644
index 00000000..daac8f76
--- /dev/null
+++ b/shib-support/src/test/java/net/shibboleth/shared/primitive/AnnotationsSupportTest.java
@@ -0,0 +1,79 @@
+/*
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.shibboleth.shared.primitive;
+
+import static org.testng.Assert.assertFalse;
+import static org.testng.Assert.assertTrue;
+
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import jakarta.annotation.PreDestroy;
+
+/** Tests for {@link AnnotationsSupport}.  */
+public class AnnotationsSupportTest {
+
+    private boolean withCalled;
+    private boolean withParamCalled;
+
+    @BeforeMethod public void reset() {
+        withCalled = false;
+        withParamCalled = false;
+    }
+
+    @Test public void withTest() {
+        final var what = new With();
+        assertTrue(AnnotationsSupport.hasPreDestroyAnnotation(what));
+        AnnotationsSupport.callPreDestroyAnnotation(what);
+        assertTrue(withCalled);
+        assertFalse(withParamCalled);
+    }
+
+    @Test public void withParamTest() {
+        final var what = new WithParam();
+        assertFalse(AnnotationsSupport.hasPreDestroyAnnotation(what));
+        AnnotationsSupport.callPreDestroyAnnotation(what);
+        assertFalse(withCalled);
+        assertFalse(withParamCalled);
+    }
+
+    @Test public void withOutTest() {
+        final var what = new Without();
+        assertFalse(AnnotationsSupport.hasPreDestroyAnnotation(what));
+        AnnotationsSupport.callPreDestroyAnnotation(what);
+        assertFalse(withCalled);
+        assertFalse(withParamCalled);
+    }
+
+    private class With {
+
+        @PreDestroy public void teardown() {
+            withCalled = true;
+        }
+    }
+
+    private class WithParam {
+
+        @PreDestroy public void teardown(final int param) {
+            withParamCalled = true;
+        }
+    }
+
+    private class Without {
+
+        public void teardown() {
+        }
+    }
+}

-- 
To stop receiving notification emails like this one, please contact
the administrator of this repository.


More information about the commits mailing list