This is an automated email from the ASF dual-hosted git repository.

asf-gitbox-commits pushed a commit to branch geoapi-4.0
in repository https://gitbox.apache.org/repos/asf/sis.git

commit f51380286efcdcdae9b7a5a905819c9e74935d10
Author: Martin Desruisseaux <[email protected]>
AuthorDate: Sat Nov 7 15:37:55 2026 +0100

    Add Enterprise JavaBeans (EJB) support in `org.apache.sis.web` module.
    https://issues.apache.org/jira/browse/SIS-640
---
 endorsed/build.gradle.kts                          | 10 ++--
 .../main/org/apache/sis/setup/Configuration.java   | 12 ++---
 .../main/org/apache/sis/system/DaemonThread.java   |  7 +--
 .../main/org/apache/sis/system/Shutdown.java       | 31 ++++++++---
 .../src/org.apache.sis.web/main/module-info.java   | 25 ++++++---
 .../org/apache/sis/web/jakarta/JavaBeanHook.java   | 60 ++++++++++++++++++++++
 .../org/apache/sis/web/jakarta/package-info.java   |  5 +-
 .../org/apache/sis/web/javaee/JavaBeanHook.java    | 60 ++++++++++++++++++++++
 .../org/apache/sis/web/javaee/package-info.java    |  5 +-
 netbeans-project/ivy.xml                           |  4 ++
 10 files changed, 189 insertions(+), 30 deletions(-)

diff --git a/endorsed/build.gradle.kts b/endorsed/build.gradle.kts
index 9cb53dd0ee..a7b4a7ca4c 100644
--- a/endorsed/build.gradle.kts
+++ b/endorsed/build.gradle.kts
@@ -70,9 +70,13 @@ dependencies {
     compileOnly(libs.aws.s3)
     compileOnly(drivers.postgres)
     runtimeOnly(drivers.hsql)
-    compileOnly(group = "jakarta.servlet", name = "jakarta.servlet-api", 
version = "6.1.0")
-    compileOnly(group = "javax.servlet",   name = "javax.servlet-api",   
version = "4.0.1")
-    compileOnly(group = "org.osgi",        name = "osgi.core",           
version = "8.0.0")
+    compileOnly(group = "jakarta.annotation", name = "jakarta.annotation-api", 
version = "3.0.0")
+    compileOnly(group = "jakarta.servlet",    name = "jakarta.servlet-api",    
version = "6.1.0")
+    compileOnly(group = "jakarta.ejb",        name = "jakarta.ejb-api",        
version = "4.0.1")
+    compileOnly(group = "javax.annotation",   name = "javax.annotation-api",   
version = "1.3.2")
+    compileOnly(group = "javax.servlet",      name = "javax.servlet-api",      
version = "4.0.1")
+    compileOnly(group = "javax.ejb",          name = "javax.ejb-api",          
version = "3.2.2")
+    compileOnly(group = "org.osgi",           name = "osgi.core",              
version = "8.0.0")
 
     // Test dependencies
     testImplementation(tests.junit5)
diff --git 
a/endorsed/src/org.apache.sis.util/main/org/apache/sis/setup/Configuration.java 
b/endorsed/src/org.apache.sis.util/main/org/apache/sis/setup/Configuration.java
index 32291aafae..18a401d62b 100644
--- 
a/endorsed/src/org.apache.sis.util/main/org/apache/sis/setup/Configuration.java
+++ 
b/endorsed/src/org.apache.sis.util/main/org/apache/sis/setup/Configuration.java
@@ -25,8 +25,6 @@ import javax.sql.DataSource;
 import java.sql.SQLException;
 import org.apache.sis.system.Shutdown;
 import org.apache.sis.system.Environment;
-import org.apache.sis.system.SystemListener;
-import org.apache.sis.util.logging.Logging;
 import org.apache.sis.util.internal.shared.MetadataServices;
 
 
@@ -156,7 +154,7 @@ public final class Configuration {
      *
      *     class MyClass {
      *         private static DataSource createDataSource() {
-     *             PGSimpleDataSource ds = new PGSimpleDataSource();
+     *             var ds = new PGSimpleDataSource();
      *             ds.setDatabaseName("SpatialMetadata");
      *             // Server default to "localhost".
      *             return ds;
@@ -182,7 +180,7 @@ public final class Configuration {
      *         The supplier may return {@code null}, in which case it will be 
ignored.
      * @throws IllegalStateException if {@link DataSource} has already be 
obtained before this method call.
      *
-     * @see <a href="https://sis.apache.org/epsg.html#jndi";>How to use EPSG 
geodetic dataset</a>
+     * @see <a href="https://sis.apache.org/epsg.html#jndi";>How to use 
<abbr>EPSG</abbr> geodetic dataset</a>
      */
     public void setDatabase(final Supplier<DataSource> source) {
         
MetadataServices.getInstance().setDataSource(Objects.requireNonNull(source));
@@ -195,13 +193,13 @@ public final class Configuration {
      * Any use of Apache <abbr>SIS</abbr> after this method call may have 
unexpected effects.
      * In particular, it may cause memory leaks.
      *
-     * <h4>When to use</h4>
+     * <h4>When to shutdown Apache <abbr>SIS</abbr></h4>
      * This method should generally <strong>not</strong> be invoked, because 
Apache <abbr>SIS</abbr> registers
      * itself a {@linkplain Runtime#addShutdownHook(Thread) shutdown hook} to 
the Java Virtual Machine.
      * This method may be useful in embedded environments that do not allow 
the use of shutdown hooks,
      * or when waiting for the <abbr>JVM</abbr> shutdown is overly 
conservative.
      *
-     * <h4>Complete shutdown of Derby</h4>
+     * <h4>Complete shutdown of Derby database</h4>
      * This method shutdowns only the databases used by Apache 
<abbr>SIS</abbr>.
      * If Apache Derby is used for the <abbr>EPSG</abbr> database, some Derby 
daemon threads may still be running.
      * Those daemons can be ignored in standalone applications, but may need 
to be stopped in embedded environments.
@@ -224,7 +222,7 @@ public final class Configuration {
         try {
             Shutdown.stop(Configuration.class);
         } catch (Exception e) {
-            Logging.unexpectedException(SystemListener.LOGGER, 
Configuration.class, "stop", e);
+            Shutdown.log(Configuration.class, e);
         }
     }
 }
diff --git 
a/endorsed/src/org.apache.sis.util/main/org/apache/sis/system/DaemonThread.java 
b/endorsed/src/org.apache.sis.util/main/org/apache/sis/system/DaemonThread.java
index 47360d304c..3dbf7585a9 100644
--- 
a/endorsed/src/org.apache.sis.util/main/org/apache/sis/system/DaemonThread.java
+++ 
b/endorsed/src/org.apache.sis.util/main/org/apache/sis/system/DaemonThread.java
@@ -18,6 +18,7 @@ package org.apache.sis.system;
 
 import java.util.List;
 import java.util.ArrayList;
+import static 
org.apache.sis.util.internal.shared.Constants.NANOS_PER_MILLISECOND;
 
 
 /**
@@ -141,9 +142,9 @@ abstract class DaemonThread extends Thread {
             t.interrupt();
         }
         while (thread != null) {
-            final long delay = stopWaitingAt - System.nanoTime();
-            if (delay <= 0) break;
-            thread.join(delay / 1000000);                   // Convert 
nanoseconds to milliseconds.
+            final long delay = (stopWaitingAt - System.nanoTime()) / 
NANOS_PER_MILLISECOND;
+            if (delay <= 0) break;      // A value of 0 cause `join(…)` to 
wait forever.
+            thread.join(delay);
             thread = thread.previous;
         }
     }
diff --git 
a/endorsed/src/org.apache.sis.util/main/org/apache/sis/system/Shutdown.java 
b/endorsed/src/org.apache.sis.util/main/org/apache/sis/system/Shutdown.java
index d2ce0f08a3..70fabf7124 100644
--- a/endorsed/src/org.apache.sis.util/main/org/apache/sis/system/Shutdown.java
+++ b/endorsed/src/org.apache.sis.util/main/org/apache/sis/system/Shutdown.java
@@ -21,6 +21,7 @@ import java.util.ArrayDeque;
 import java.util.Objects;
 import java.util.concurrent.Callable;
 import org.apache.sis.util.logging.Logging;
+import static org.apache.sis.util.internal.shared.Constants.NANOS_PER_SECOND;
 
 
 /**
@@ -33,6 +34,8 @@ public final class Shutdown extends Thread {
     /**
      * Non-null if a shutdown hook is already registered. That shutdown hook 
is not necessarily {@link #hook}.
      * It may be an OSGi or Servlet shutdown hook instead, as notified by 
{@link #setContainer(String)}.
+     *
+     * @see #setContainer(String)
      */
     private static String container;
 
@@ -60,7 +63,7 @@ public final class Shutdown extends Thread {
     @SuppressWarnings("CallToPrintStackTrace")
     public void run() {
         try {
-            Shutdown.stop((Class<?>) null);
+            stop((Class<?>) null);
         } catch (Exception e) {
             /*
              * Too late for logging since we are in process of shutting down 
the Java Virtual Machine.
@@ -84,15 +87,18 @@ public final class Shutdown extends Thread {
     /**
      * Invoked if the Apache SIS library is executed from an environment that 
provides its own shutdown hook.
      * Example of such environments are OSGi and servlet containers. In such 
case, the shutdown hook will not
-     * be registered to the JVM {@link Runtime}.
+     * be registered to the <abbr>JVM</abbr> {@link Runtime}.
      *
      * @param  env  a description of the container. Should contain version 
information if possible.
      *              Example: {@code "OSGi"} or {@code "JavaServer Web Dev 
Kit/1.0"}.
      */
-    public static void setContainer(final String env) {
+    public static void setContainer(String env) {
         Objects.requireNonNull(env);
         synchronized (resources) {
             removeShutdownHook();       // Should not be needed but we are 
paranoiac.
+            if (container != null) {
+                env = container + " & " + env;
+            }
             container = env;
         }
     }
@@ -145,7 +151,7 @@ public final class Shutdown extends Thread {
 
     /**
      * Unregisters the supervisor MBean, executes the disposal tasks and 
shutdowns the {@code org.apache.sis.util} threads.
-     * This method may be invoked at JVM shutdown, or if a container like OSGi 
is unloaded the SIS library.
+     * This method may be invoked at JVM shutdown, or if a container like OSGi 
is unloading the <abbr>SIS</abbr> library.
      *
      * @param  caller  the class invoking this method (used for logging 
purpose), or {@code null} if this method
      *         is invoked at JVM shutdown time (in which case the logging 
system is not available anymore).
@@ -163,7 +169,7 @@ public final class Shutdown extends Thread {
         }
         /*
          * Unregister the MBean before to stop the threads, in order to avoid 
false alerts
-         * in the superviror 'warnings()' method. Failure to unregister the 
MBean is worth
+         * in the superviror `warnings()` method. Failure to unregister the 
MBean is worth
          * to report, but we will do that only after we completed the other 
shutdown steps.
          */
         Exception exception = null;
@@ -194,14 +200,25 @@ public final class Shutdown extends Thread {
          * but continue the shutdown process anyway.
          */
         try {
-            Threads.shutdown(System.nanoTime() + 4000);
+            Threads.shutdown(System.nanoTime() + 3*NANOS_PER_SECOND);
         } catch (InterruptedException e) {
             if (caller != null) {
-                Logging.unexpectedException(SystemListener.LOGGER, caller, 
"stop", e);
+                log(caller, e);
             }
         }
         if (exception != null) {
             throw exception;
         }
     }
+
+    /**
+     * Convenience method for logging an unexpected exception at shutdown time.
+     * This method assumes that the caller is a method named {@code 
"shutdown"}.
+     *
+     * @param caller  the class which is doing the shutdown.
+     * @param e       the exception that occurred during shutdown.
+     */
+    public static void log(final Class<?> caller, final Exception e) {
+        Logging.unexpectedException(SystemListener.LOGGER, caller, "shutdown", 
e);
+    }
 }
diff --git a/endorsed/src/org.apache.sis.web/main/module-info.java 
b/endorsed/src/org.apache.sis.web/main/module-info.java
index e10df4ddf0..edd24615e0 100644
--- a/endorsed/src/org.apache.sis.web/main/module-info.java
+++ b/endorsed/src/org.apache.sis.web/main/module-info.java
@@ -16,18 +16,21 @@
  */
 
 /**
- * Support for Apache <abbr>SIS</abbr> execution in the context of web 
containers.
+ * Support for Apache <abbr>SIS</abbr> execution in the context of JavaEE or 
web containers.
  * This module registers listeners for stopping Apache <abbr>SIS</abbr> 
background threads
- * when a web application using <abbr>SIS</abbr> is undeployed from a web 
container.
+ * when a web application or an Enterprise JavaBean (<abbr>EJB</abbr>) using 
<abbr>SIS</abbr>
+ * is undeployed from a container.
  * The listeners in this module are compatible with the following frameworks:
  *
  * <ul>
- *   <li>JavaEE Servlet 4.0 ({@code javax.servlet} package)</li>
- *   <li>Jakarta Servlet 6.1 ({@code jakarta.servlet} package)</li>
- *   <li>OGSGi 8.0 ({@code org.osgi.framework} package)</li>
+ *   <li>Jakarta Servlet ({@code jakarta.servlet} package)</li>
+ *   <li>JavaEE Servlet ({@code javax.servlet} package)</li>
+ *   <li>Jakarta Enterprise JavaBeans (<abbr>EJB</abbr>) ({@code jakarta.ejb} 
package)</li>
+ *   <li>JavaEE <abbr>EJB</abbr> ({@code javax.ejb} package)</li>
+ *   <li>OGSGi ({@code org.osgi.framework} package)</li>
  * </ul>
  *
- * Web applications should consider adding this module in their dependencies
+ * Web applications or Enterprise JavaBeans should consider adding this module 
in their dependencies
  * if they use Apache <abbr>SIS</abbr> and need clean undeploy while keeping 
the rest of the container running.
  *
  * @author  Martin Desruisseaux (Geomatys)
@@ -36,8 +39,18 @@
  */
 module org.apache.sis.web {
     requires transitive org.apache.sis.util;
+
+    // Servlet (new and old)
     requires static jakarta.servlet;
     requires static javax.servlet.api;
+
+    // JavaEE (new and old)
+    requires static jakarta.annotation;
+    requires static jakarta.ejb;
+    requires static java.annotation;
+    requires static javax.ejb.api;
+
+    // OSGi bundle
     requires static osgi.core;
 
     provides jakarta.servlet.ServletContainerInitializer
diff --git 
a/endorsed/src/org.apache.sis.web/main/org/apache/sis/web/jakarta/JavaBeanHook.java
 
b/endorsed/src/org.apache.sis.web/main/org/apache/sis/web/jakarta/JavaBeanHook.java
new file mode 100644
index 0000000000..15b37812dc
--- /dev/null
+++ 
b/endorsed/src/org.apache.sis.web/main/org/apache/sis/web/jakarta/JavaBeanHook.java
@@ -0,0 +1,60 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.sis.web.jakarta;
+
+import jakarta.ejb.Startup;
+import jakarta.ejb.Singleton;
+import jakarta.annotation.PostConstruct;
+import jakarta.annotation.PreDestroy;
+import org.apache.sis.system.Shutdown;
+
+
+/**
+ * Listener for Enterprise JavaBeans, used as a shutdown hook when the 
<abbr>EJB</abbr> is undeployed.
+ * This class should not be used directly.
+ *
+ * @author  Martin Desruisseaux (Geomatys)
+ */
+@Startup
+@Singleton
+public final class JavaBeanHook {
+    /**
+     * Invoked by reflection.
+     */
+    public JavaBeanHook() {
+    }
+
+    /**
+     * Notifies Apache <abbr>SIS</abbr> that the application is initializing.
+     */
+    @PostConstruct
+    public void initialize() {
+        Shutdown.setContainer("EJB (Jakarta)");
+    }
+
+    /**
+     * Receives notification that the application is about to be shutdown.
+     */
+    @PreDestroy
+    public void shutdown() {
+        try {
+            Shutdown.stop(getClass());
+        } catch (Exception e) {
+            Shutdown.log(getClass(), e);
+        }
+    }
+}
diff --git 
a/endorsed/src/org.apache.sis.web/main/org/apache/sis/web/jakarta/package-info.java
 
b/endorsed/src/org.apache.sis.web/main/org/apache/sis/web/jakarta/package-info.java
index 8c3d25f7d4..5c99bcb14c 100644
--- 
a/endorsed/src/org.apache.sis.web/main/org/apache/sis/web/jakarta/package-info.java
+++ 
b/endorsed/src/org.apache.sis.web/main/org/apache/sis/web/jakarta/package-info.java
@@ -17,8 +17,9 @@
 
 
 /**
- * All classes having a dependencies to Jakarta ({@code jakarta.servlet} 
packages).
- * This is the replacement of JavaEE ({@code javax.servlet} packages).
+ * All classes having a dependencies to Jakarta ({@code jakarta.servlet} and 
{@code jakarta.ejb} packages).
+ * Jakarta contains Enterprise JavaBeans (<abbr>EJB</abbr>) for business logic 
and servlet for the web.
+ * This is the replacement of JavaEE ({@code javax.*} packages).
  *
  * @author  Martin Desruisseaux (Geomatys)
  */
diff --git 
a/endorsed/src/org.apache.sis.web/main/org/apache/sis/web/javaee/JavaBeanHook.java
 
b/endorsed/src/org.apache.sis.web/main/org/apache/sis/web/javaee/JavaBeanHook.java
new file mode 100644
index 0000000000..0b97ff8c2c
--- /dev/null
+++ 
b/endorsed/src/org.apache.sis.web/main/org/apache/sis/web/javaee/JavaBeanHook.java
@@ -0,0 +1,60 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.sis.web.javaee;
+
+import javax.ejb.Startup;
+import javax.ejb.Singleton;
+import javax.annotation.PostConstruct;
+import javax.annotation.PreDestroy;
+import org.apache.sis.system.Shutdown;
+
+
+/**
+ * Listener for Enterprise JavaBeans, used as a shutdown hook when the 
<abbr>EJB</abbr> is undeployed.
+ * This class should not be used directly.
+ *
+ * @author  Martin Desruisseaux (Geomatys)
+ */
+@Startup
+@Singleton
+public final class JavaBeanHook {
+    /**
+     * Invoked by reflection.
+     */
+    public JavaBeanHook() {
+    }
+
+    /**
+     * Notifies Apache <abbr>SIS</abbr> that the application is initializing.
+     */
+    @PostConstruct
+    public void initialize() {
+        Shutdown.setContainer("EJB (JavaEE)");
+    }
+
+    /**
+     * Receives notification that the application is about to be shutdown.
+     */
+    @PreDestroy
+    public void shutdown() {
+        try {
+            Shutdown.stop(getClass());
+        } catch (Exception e) {
+            Shutdown.log(getClass(), e);
+        }
+    }
+}
diff --git 
a/endorsed/src/org.apache.sis.web/main/org/apache/sis/web/javaee/package-info.java
 
b/endorsed/src/org.apache.sis.web/main/org/apache/sis/web/javaee/package-info.java
index 739613c974..9137f99f9e 100644
--- 
a/endorsed/src/org.apache.sis.web/main/org/apache/sis/web/javaee/package-info.java
+++ 
b/endorsed/src/org.apache.sis.web/main/org/apache/sis/web/javaee/package-info.java
@@ -17,8 +17,9 @@
 
 
 /**
- * All classes having a dependencies to JavaEE ({@code javax.servlet} 
packages).
- * JavaEE is replaced by Jakarta ({@code jakarta.servlet} packages), but not 
all applications have migrated.
+ * All classes having a dependencies to JavaEE ({@code javax.servlet} and 
{@code javax.ejb} packages).
+ * JavaEE contains Enterprise JavaBeans (<abbr>EJB</abbr>) for business logic 
and servlet for the web.
+ * JavaEE is replaced by Jakarta ({@code jakarta.*} packages), but not all 
applications have migrated.
  * This package is provided for web applications that are still using JavaEE.
  *
  * @author  Martin Desruisseaux (Geomatys)
diff --git a/netbeans-project/ivy.xml b/netbeans-project/ivy.xml
index c998d5b478..8e329a390f 100644
--- a/netbeans-project/ivy.xml
+++ b/netbeans-project/ivy.xml
@@ -28,6 +28,10 @@
         <dependency org="org.antlr"              name="antlr4-maven-plugin"    
 rev="4.13.2"/>
         <dependency org="javax.servlet"          name="javax.servlet-api"      
 rev="4.0.1"/>
         <dependency org="jakarta.servlet"        name="jakarta.servlet-api"    
 rev="6.1.0"/>
+        <dependency org="javax.ejb"              name="javax.ejb-api"          
 rev="3.2.2"/>
+        <dependency org="jakarta.ejb"            name="jakarta.ejb-api"        
 rev="4.0.1"/>
+        <dependency org="jakarta.annotation"     name="jakarta.annotation-api" 
 rev="3.0.0"/>
+        <dependency org="javax.annotation"       name="javax.annotation-api"   
 rev="1.3.2"/>
         <dependency org="org.osgi"               name="osgi.core"              
 rev="8.0.0"/>
         <dependency org="org.junit.platform"     
name="junit-platform-launcher" rev="1.10.3"/>
         <dependency org="org.junit.jupiter"      name="junit-jupiter-api"      
 rev="5.10.3"/>

Reply via email to