[java-shib-shared] branch main updated: JSSH-71 Remove the impact of the DestructableComponent Interface
Codeberg
noreply at shibboleth.net
Thu Jul 23 08:57:59 UTC 2026
This is an automated email from the git hooks/post-receive script.
codeberg pushed a commit to branch main
in repository java-shib-shared.
View the commit online:
https://codeberg.org/Shibboleth/java-shib-shared/commit/5037f03d14d7f5afe0fef8555d4a4b4afb77a417
The following commit(s) were added to refs/heads/main by this push:
new 5037f03d JSSH-71 Remove the impact of the DestructableComponent Interface
5037f03d is described below
commit 5037f03d14d7f5afe0fef8555d4a4b4afb77a417
Author: Rod Widdowson <rdw at steadingsoftware.com>
AuthorDate: Wed Jul 22 20:03:39 2026 +0100
JSSH-71 Remove the impact of the DestructableComponent Interface
https://shibboleth.atlassian.net/browse/JSSH-71
Add a bean which will tear down all its ApplicationContext peers
at our annotation point.
---
.../spring/config/BeanTearDownProcessor.java | 74 +++++++++++++++++
.../spring/config/BeanTearDownProcessorTest.java | 93 ++++++++++++++++++++++
.../config/beanTearDownPostProcessorTest.xml | 19 +++++
.../shared/primitive/AnnotationsSupport.java | 40 +++++++---
4 files changed, 213 insertions(+), 13 deletions(-)
diff --git a/shib-spring/src/main/java/net/shibboleth/shared/spring/config/BeanTearDownProcessor.java b/shib-spring/src/main/java/net/shibboleth/shared/spring/config/BeanTearDownProcessor.java
new file mode 100644
index 00000000..b8028819
--- /dev/null
+++ b/shib-spring/src/main/java/net/shibboleth/shared/spring/config/BeanTearDownProcessor.java
@@ -0,0 +1,74 @@
+/*
+ * 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.spring.config;
+
+import javax.annotation.Nullable;
+
+import org.slf4j.Logger;
+import org.springframework.beans.BeansException;
+import org.springframework.beans.factory.DisposableBean;
+import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.ApplicationContextAware;
+import org.springframework.context.ConfigurableApplicationContext;
+
+import net.shibboleth.shared.annotation.OnTeardown;
+import net.shibboleth.shared.primitive.AnnotationsSupport;
+import net.shibboleth.shared.primitive.LoggerFactory;
+
+/**
+ * A bean to teardown all beans in the application Context that have
+ * the {@link OnTeardown} annotation.
+ *
+ * @since 9.3.0
+ */
+public class BeanTearDownProcessor implements ApplicationContextAware, DisposableBean {
+
+ /** Logger. */
+ final private Logger log = LoggerFactory.getLogger(BeanTearDownProcessor.class);
+
+ /**
+ * If non null, the {@link ApplicationContext} that we were created inside.
+ */
+ @Nullable private ApplicationContext applicationContext = null;
+
+ /** {@inheritDoc}
+ * <p>If there is an {@link ApplicationContext} then iterate over all beans and call all
+ * the annotated tear down methods that each one implements (if any).</p>
+ */
+ @Override
+ public void destroy() throws Exception {
+
+ if (applicationContext == null) {
+ return;
+ }
+
+ if (applicationContext instanceof ConfigurableApplicationContext) {
+ final ConfigurableListableBeanFactory clbf = ((ConfigurableApplicationContext) applicationContext).getBeanFactory();
+ for (final String beanName : applicationContext.getBeanDefinitionNames()) {
+ final Object bean = clbf.getSingleton(beanName);
+ AnnotationsSupport.callOnTeardownAnnotations(bean, beanName, log);
+ }
+ }
+ }
+
+ /** {@inheritDoc}
+ * <p>We save the {@link ApplicationContext} for use in teardown.</p>
+ */
+ @Override
+ public void setApplicationContext( @Nullable final ApplicationContext ctx) throws BeansException {
+ applicationContext = ctx;
+ }
+}
\ No newline at end of file
diff --git a/shib-spring/src/test/java/net/shibboleth/shared/spring/config/BeanTearDownProcessorTest.java b/shib-spring/src/test/java/net/shibboleth/shared/spring/config/BeanTearDownProcessorTest.java
new file mode 100644
index 00000000..7723a18c
--- /dev/null
+++ b/shib-spring/src/test/java/net/shibboleth/shared/spring/config/BeanTearDownProcessorTest.java
@@ -0,0 +1,93 @@
+/*
+ * 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.spring.config;
+
+import static org.testng.Assert.assertFalse;
+import static org.testng.Assert.assertTrue;
+
+import org.springframework.beans.factory.DisposableBean;
+import org.springframework.context.support.GenericApplicationContext;
+import org.springframework.core.io.ClassPathResource;
+import org.testng.annotations.Test;
+
+import net.shibboleth.shared.annotation.OnTeardown;
+import net.shibboleth.shared.spring.util.ApplicationContextBuilder;
+
+/**
+ * Test for {@link BeanTearDownProcessor}.
+ *
+ * Summon up some beans, destroy the ApplicationContext. look at the side effects.
+ */
+ at SuppressWarnings("javadoc")
+public class BeanTearDownProcessorTest {
+
+ @Test
+ public void test() {
+ //
+ // We need to own the Context lifetime
+ //
+ final ApplicationContextBuilder builder = new ApplicationContextBuilder();
+ final ClassPathResource res = new ClassPathResource("net/shibboleth/shared/spring/config/beanTearDownPostProcessorTest.xml");
+ builder.setServiceConfiguration(res);
+ final GenericApplicationContext applicationContext = builder.build();
+ final WithAnnotationChild childWith = (WithAnnotationChild) applicationContext.getBean("ChildWith");
+ final NoAnnotationParent noneParent = (NoAnnotationParent) applicationContext.getBean("ParentWithout");
+ final NoAnnotationParent noneChild =(NoAnnotationParent) applicationContext.getBean("ChildWithout");
+
+ assertFalse(childWith.tornDownChild);
+ assertFalse(childWith.tornDownParent);
+ assertFalse(noneParent.tornDown);
+ assertFalse(noneChild.tornDown);
+
+ applicationContext.close();
+ assertTrue(childWith.tornDownChild); //annotated
+ assertTrue(childWith.tornDownParent); // annotated, different method
+ assertFalse(noneParent.tornDown); // Not a Disposable Bean
+ assertTrue(noneChild.tornDown);
+ }
+
+ /** Test class to check both tear downs are called */
+ public static class WithAnnotationParent {
+
+ public boolean tornDownParent = false;
+
+ @OnTeardown public final void tearDownWithAnnotationParent() {
+ tornDownParent = true;
+ }
+ }
+
+ /** Test class to check both tear downs are called */
+ public static class WithAnnotationChild extends WithAnnotationParent{
+
+ public boolean tornDownChild= false;
+
+ @OnTeardown public final void tearDownWithAnnotationChild() {
+ tornDownChild = true;
+ }
+ }
+
+ public static class NoAnnotationParent {
+
+ public boolean tornDown = false;
+
+ public final void destroy() {
+ tornDown = true;
+ }
+ }
+
+ public static class NoAnnotationChild extends NoAnnotationParent implements DisposableBean {
+
+ }
+}
diff --git a/shib-spring/src/test/resources/net/shibboleth/shared/spring/config/beanTearDownPostProcessorTest.xml b/shib-spring/src/test/resources/net/shibboleth/shared/spring/config/beanTearDownPostProcessorTest.xml
new file mode 100644
index 00000000..73681908
--- /dev/null
+++ b/shib-spring/src/test/resources/net/shibboleth/shared/spring/config/beanTearDownPostProcessorTest.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<beans xmlns="http://www.springframework.org/schema/beans"
+ xmlns:p="http://www.springframework.org/schema/p" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"
+ default-init-method="initialize" >
+
+ <bean class="net.shibboleth.shared.spring.config.BeanTearDownProcessor" />
+
+ <bean id="ChildWith"
+ class="net.shibboleth.shared.spring.config.BeanTearDownProcessorTest$WithAnnotationChild" />
+
+ <bean id="ParentWithout"
+ class="net.shibboleth.shared.spring.config.BeanTearDownProcessorTest$NoAnnotationParent" />
+
+ <bean id="ChildWithout"
+ class="net.shibboleth.shared.spring.config.BeanTearDownProcessorTest$NoAnnotationChild" />
+
+
+</beans>
\ No newline at end of file
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
index a09599e8..605629f4 100644
--- a/shib-support/src/main/java/net/shibboleth/shared/primitive/AnnotationsSupport.java
+++ b/shib-support/src/main/java/net/shibboleth/shared/primitive/AnnotationsSupport.java
@@ -16,6 +16,7 @@ package net.shibboleth.shared.primitive;
import java.lang.reflect.Method;
+import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import org.slf4j.Logger;
@@ -39,35 +40,48 @@ public final class AnnotationsSupport {
static public void callOnTeardownAnnotations(@Nullable final Object obj, @Nullable final String id, @Nullable final Logger log) {
Logger theLog = log;
- String theName = id;
if (obj == null) {
return;
}
for (final Method m: obj.getClass().getMethods()) {
if (m.getParameterCount() == 0 && m.getAnnotation(OnTeardown.class) != null) {
- if (theName == null) {
- if (obj instanceof IdentifiedComponent) {
- theName = ((IdentifiedComponent)obj).getId();
- }
- if (theName == null) {
- theName = "Unidentified bean of type " + obj.getClass().getName();
- }
- }
- if (log != null) {
- log.debug("{}: found an OnTeardown method {}", id, m);
+ if (log != null && log.isDebugEnabled()) {
+ log.debug("{}: found an OnTeardown method {}", getNameFor(obj, id));
}
try {
m.invoke(obj);
- } catch (final Exception e) {
+ }
+ catch (final Exception e) {
if (theLog == null) {
theLog = LoggerFactory.getLogger(AnnotationsSupport.class);
}
- theLog.error("{}: Failed invoking {}", id, m, e);
+ theLog.error("{}: Failed invoking {}", getNameFor(obj, id), m, e);
}
}
}
}
+ /**
+ * Helper method to find a log-friendly name for an object.
+ * <p>Return the supplied name if non null, otherwise interrogate for
+ * its identity (if it has one), otherwise generate something.</p>
+ * @param obj The object we are interested it
+ * @param suppliedName the name, if we know it already
+ * @return the best name we could
+ */
+ static @Nonnull String getNameFor(@Nonnull final Object obj, @Nullable String suppliedName) {
+ String result = suppliedName;
+ if (suppliedName == null) {
+ if (obj instanceof IdentifiedComponent) {
+ suppliedName = ((IdentifiedComponent)obj).getId();
+ }
+ if (suppliedName == null) {
+ suppliedName = "Unidentified bean of type " + obj.getClass().getName();
+ }
+ }
+ return result;
+ }
+
/**
* Call the {@link OnTeardown} methods.
* @param obj The object to introspect
--
To stop receiving notification emails like this one, please contact
the administrator of this repository.
More information about the commits
mailing list