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

critas pushed a commit to branch wx_fix_external-service_loader
in repository https://gitbox.apache.org/repos/asf/iotdb.git

commit c693f14560019ac0064205fd41bbd76f523f2547
Author: CritasWang <[email protected]>
AuthorDate: Thu Feb 12 16:32:21 2026 +0800

    Fix external service ClassLoader context handling and dependency scope
    
    - Store and restore thread context ClassLoader when starting/stopping 
external services to ensure proper class loading
    - Remove `provided` scope from jakarta.ws.rs-api dependency in rest module
    - Remove unneed external-service-impl assembly descriptor from distribution
    - Add serviceClassLoader field to ServiceInfo for tracking service-specific 
ClassLoaders
---
 distribution/pom.xml                               |  1 -
 external-service-impl/rest/pom.xml                 |  1 -
 .../ExternalServiceManagementService.java          | 35 ++++++++++++++++------
 .../iotdb/commons/externalservice/ServiceInfo.java |  9 ++++++
 4 files changed, 35 insertions(+), 11 deletions(-)

diff --git a/distribution/pom.xml b/distribution/pom.xml
index 65a3572fd3a..f4773f6afad 100644
--- a/distribution/pom.xml
+++ b/distribution/pom.xml
@@ -86,7 +86,6 @@
                                 
<descriptor>src/assembly/confignode.xml</descriptor>
                                 <descriptor>src/assembly/cli.xml</descriptor>
                                 
<descriptor>src/assembly/library-udf.xml</descriptor>
-                                
<descriptor>src/assembly/external-service-impl.xml</descriptor>
                             </descriptors>
                             
<finalName>apache-iotdb-${project.version}</finalName>
                         </configuration>
diff --git a/external-service-impl/rest/pom.xml 
b/external-service-impl/rest/pom.xml
index edb8aa3fc33..a5a1d76a1b2 100644
--- a/external-service-impl/rest/pom.xml
+++ b/external-service-impl/rest/pom.xml
@@ -153,7 +153,6 @@
         <dependency>
             <groupId>jakarta.ws.rs</groupId>
             <artifactId>jakarta.ws.rs-api</artifactId>
-            <scope>provided</scope>
         </dependency>
         <dependency>
             <groupId>org.apache.iotdb</groupId>
diff --git 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/service/externalservice/ExternalServiceManagementService.java
 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/service/externalservice/ExternalServiceManagementService.java
index 98f231db5b8..1d108bc941a 100644
--- 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/service/externalservice/ExternalServiceManagementService.java
+++ 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/service/externalservice/ExternalServiceManagementService.java
@@ -159,9 +159,9 @@ public class ExternalServiceManagementService {
         if (serviceInfo.getServiceInstance() == null) {
           // lazy create Instance
           serviceInfo.setServiceInstance(
-              createExternalServiceInstance(serviceName, 
serviceInfo.getClassName()));
+              createExternalServiceInstance(serviceName, 
serviceInfo.getClassName(), serviceInfo));
         }
-        serviceInfo.getServiceInstance().start();
+        runWithServiceClassLoader(serviceInfo, () -> 
serviceInfo.getServiceInstance().start());
       }
 
       // 3. persist on CN if service is user-defined, rollback if failed
@@ -170,7 +170,7 @@ public class ExternalServiceManagementService {
             
ConfigNodeClientManager.getInstance().borrowClient(ConfigNodeInfo.CONFIG_REGION_ID))
 {
           TSStatus status = 
client.startExternalService(QueryId.getDataNodeId(), serviceName);
           if (status.getCode() != TSStatusCode.SUCCESS_STATUS.getStatusCode()) 
{
-            serviceInfo.getServiceInstance().stop();
+            runWithServiceClassLoader(serviceInfo, () -> 
serviceInfo.getServiceInstance().stop());
             throw new IoTDBRuntimeException(status.message, status.code);
           }
         }
@@ -183,12 +183,14 @@ public class ExternalServiceManagementService {
     }
   }
 
-  private IExternalService createExternalServiceInstance(String serviceName, 
String className) {
+  private IExternalService createExternalServiceInstance(
+      String serviceName, String className, ServiceInfo serviceInfo) {
     // close ClassLoader automatically to release the file handle
     try {
       // Remind: this classLoader should be closed when service is dropped 
after user-defined
       // service supported
       ExternalServiceClassLoader classLoader = new 
ExternalServiceClassLoader(libRoot);
+      serviceInfo.setServiceClassLoader(classLoader);
       return (IExternalService)
           Class.forName(className, true, 
classLoader).getDeclaredConstructor().newInstance();
     } catch (Throwable t) {
@@ -203,6 +205,21 @@ public class ExternalServiceManagementService {
     }
   }
 
+  private static void runWithServiceClassLoader(ServiceInfo serviceInfo, 
Runnable action) {
+    ClassLoader serviceClassLoader = serviceInfo.getServiceClassLoader();
+    if (serviceClassLoader == null) {
+      action.run();
+      return;
+    }
+    ClassLoader originalClassLoader = 
Thread.currentThread().getContextClassLoader();
+    try {
+      Thread.currentThread().setContextClassLoader(serviceClassLoader);
+      action.run();
+    } finally {
+      Thread.currentThread().setContextClassLoader(originalClassLoader);
+    }
+  }
+
   public void stopService(String serviceName) throws ClientManagerException, 
TException {
     try {
       lock.writeLock().lock();
@@ -231,7 +248,7 @@ public class ExternalServiceManagementService {
             
ConfigNodeClientManager.getInstance().borrowClient(ConfigNodeInfo.CONFIG_REGION_ID);
 ) {
           TSStatus status = 
client.stopExternalService(QueryId.getDataNodeId(), serviceName);
           if (status.getCode() != TSStatusCode.SUCCESS_STATUS.getStatusCode()) 
{
-            serviceInfo.getServiceInstance().start();
+            runWithServiceClassLoader(serviceInfo, () -> 
serviceInfo.getServiceInstance().start());
             throw new IoTDBRuntimeException(status.message, status.code);
           }
         }
@@ -249,7 +266,7 @@ public class ExternalServiceManagementService {
         serviceInfo.getServiceInstance() != null,
         INSTANCE_NULL_ERROR_MSG,
         serviceInfo.getServiceName());
-    serviceInfo.getServiceInstance().stop();
+    runWithServiceClassLoader(serviceInfo, () -> 
serviceInfo.getServiceInstance().stop());
   }
 
   public void dropService(String serviceName, boolean forcedly)
@@ -325,11 +342,11 @@ public class ExternalServiceManagementService {
               if (serviceInfo.getState() == RUNNING) {
                 IExternalService serviceInstance =
                     createExternalServiceInstance(
-                        serviceInfo.getServiceName(), 
serviceInfo.getClassName());
+                        serviceInfo.getServiceName(), 
serviceInfo.getClassName(), serviceInfo);
                 checkState(
                     serviceInstance != null, INSTANCE_NULL_ERROR_MSG, 
serviceInfo.getServiceName());
                 serviceInfo.setServiceInstance(serviceInstance);
-                serviceInstance.start();
+                runWithServiceClassLoader(serviceInfo, serviceInstance::start);
               }
             });
   }
@@ -346,7 +363,7 @@ public class ExternalServiceManagementService {
                 // service in restoreRunningServiceInstance method
                 if (serviceInstance != null) {
                   // only stop the instance successfully started
-                  serviceInstance.stop();
+                  runWithServiceClassLoader(serviceInfo, 
serviceInstance::stop);
                 }
               }
             });
diff --git 
a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/externalservice/ServiceInfo.java
 
b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/externalservice/ServiceInfo.java
index f9ef9a73c02..7a933812fb4 100644
--- 
a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/externalservice/ServiceInfo.java
+++ 
b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/externalservice/ServiceInfo.java
@@ -36,6 +36,7 @@ public class ServiceInfo {
   private State state;
 
   private transient IExternalService serviceInstance;
+  private transient ClassLoader serviceClassLoader;
 
   public ServiceInfo(String serviceName, String className, ServiceType 
serviceType) {
     this.serviceName = serviceName;
@@ -79,6 +80,14 @@ public class ServiceInfo {
     this.serviceInstance = serviceInstance;
   }
 
+  public ClassLoader getServiceClassLoader() {
+    return serviceClassLoader;
+  }
+
+  public void setServiceClassLoader(ClassLoader serviceClassLoader) {
+    this.serviceClassLoader = serviceClassLoader;
+  }
+
   public void serialize(OutputStream stream) throws IOException {
     ReadWriteIOUtils.write(serviceName, stream);
     ReadWriteIOUtils.write(className, stream);

Reply via email to