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

jackietien pushed a commit to branch rc/2.0.8
in repository https://gitbox.apache.org/repos/asf/iotdb.git

commit 6951a0c3c127f163c6a2b4fd926eb54efbad21a2
Author: CritasWang <[email protected]>
AuthorDate: Thu Feb 12 18:24:27 2026 +0800

    Fix external service ClassLoader context handling and dependency scope 
(#17202)
---
 distribution/pom.xml                               |  1 -
 external-service-impl/rest/pom.xml                 |  1 -
 iotdb-core/datanode/pom.xml                        |  4 ---
 .../view/visitor/TransformToExpressionVisitor.java | 21 ++++++-------
 .../ExternalServiceManagementService.java          | 35 ++++++++++++++++------
 .../iotdb/commons/externalservice/ServiceInfo.java |  9 ++++++
 6 files changed, 44 insertions(+), 27 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/pom.xml b/iotdb-core/datanode/pom.xml
index 2ccae580549..8b530204896 100644
--- a/iotdb-core/datanode/pom.xml
+++ b/iotdb-core/datanode/pom.xml
@@ -223,10 +223,6 @@
             <groupId>jakarta.validation</groupId>
             <artifactId>jakarta.validation-api</artifactId>
         </dependency>
-        <dependency>
-            <groupId>jakarta.ws.rs</groupId>
-            <artifactId>jakarta.ws.rs-api</artifactId>
-        </dependency>
         <dependency>
             <groupId>org.reflections</groupId>
             <artifactId>reflections</artifactId>
diff --git 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/schemaengine/schemaregion/view/visitor/TransformToExpressionVisitor.java
 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/schemaengine/schemaregion/view/visitor/TransformToExpressionVisitor.java
index 8bedc236c27..0b3bce049c2 100644
--- 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/schemaengine/schemaregion/view/visitor/TransformToExpressionVisitor.java
+++ 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/schemaengine/schemaregion/view/visitor/TransformToExpressionVisitor.java
@@ -76,8 +76,6 @@ import 
org.apache.iotdb.db.queryengine.plan.expression.leaf.TimestampOperand;
 
 import org.apache.tsfile.utils.Pair;
 
-import javax.ws.rs.NotSupportedException;
-
 import java.util.ArrayList;
 import java.util.List;
 
@@ -90,15 +88,14 @@ public class TransformToExpressionVisitor extends 
ViewExpressionVisitor<Expressi
 
   @Override
   public Expression visitExpression(ViewExpression expression, Void context) {
-    throw new RuntimeException(
-        new NotSupportedException(
-            "visitExpression in TransformToExpressionVisitor is not 
supported."));
+    throw new UnsupportedOperationException(
+        "visitExpression in TransformToExpressionVisitor is not supported.");
   }
 
   // region leaf operand
   @Override
   public Expression visitLeafOperand(LeafViewOperand leafViewOperand, Void 
context) {
-    throw new RuntimeException(new NotSupportedException("Can not construct 
abstract class."));
+    throw new UnsupportedOperationException("Can not construct abstract 
class.");
   }
 
   @Override
@@ -131,7 +128,7 @@ public class TransformToExpressionVisitor extends 
ViewExpressionVisitor<Expressi
   // region Unary Expressions
   @Override
   public Expression visitUnaryExpression(UnaryViewExpression 
unaryViewExpression, Void context) {
-    throw new RuntimeException(new NotSupportedException("Can not construct 
abstract class."));
+    throw new UnsupportedOperationException("Can not construct abstract 
class.");
   }
 
   @Override
@@ -184,7 +181,7 @@ public class TransformToExpressionVisitor extends 
ViewExpressionVisitor<Expressi
   @Override
   // region Binary Expressions
   public Expression visitBinaryExpression(BinaryViewExpression 
binaryViewExpression, Void context) {
-    throw new RuntimeException(new NotSupportedException("Can not construct 
abstract class."));
+    throw new UnsupportedOperationException("Can not construct abstract 
class.");
   }
 
   private Pair<Expression, Expression> getExpressionsForBinaryExpression(
@@ -197,7 +194,7 @@ public class TransformToExpressionVisitor extends 
ViewExpressionVisitor<Expressi
   // region Binary : Arithmetic Binary Expression
   public Expression visitArithmeticBinaryExpression(
       ArithmeticBinaryViewExpression arithmeticBinaryExpression, Void context) 
{
-    throw new RuntimeException(new NotSupportedException("Can not construct 
abstract class."));
+    throw new UnsupportedOperationException("Can not construct abstract 
class.");
   }
 
   public Expression visitAdditionExpression(
@@ -236,7 +233,7 @@ public class TransformToExpressionVisitor extends 
ViewExpressionVisitor<Expressi
   // region Binary: Compare Binary Expression
   public Expression visitCompareBinaryExpression(
       CompareBinaryViewExpression compareBinaryExpression, Void context) {
-    throw new RuntimeException(new NotSupportedException("Can not construct 
abstract class."));
+    throw new UnsupportedOperationException("Can not construct abstract 
class.");
   }
 
   public Expression visitEqualToExpression(EqualToViewExpression 
equalToExpression, Void context) {
@@ -281,7 +278,7 @@ public class TransformToExpressionVisitor extends 
ViewExpressionVisitor<Expressi
   // region Binary : Logic Binary Expression
   public Expression visitLogicBinaryExpression(
       LogicBinaryViewExpression logicBinaryExpression, Void context) {
-    throw new RuntimeException(new NotSupportedException("Can not construct 
abstract class."));
+    throw new UnsupportedOperationException("Can not construct abstract 
class.");
   }
 
   public Expression visitLogicAndExpression(
@@ -302,7 +299,7 @@ public class TransformToExpressionVisitor extends 
ViewExpressionVisitor<Expressi
   // region Ternary Expressions
   public Expression visitTernaryExpression(
       TernaryViewExpression ternaryViewExpression, Void context) {
-    throw new RuntimeException(new NotSupportedException("Can not construct 
abstract class."));
+    throw new UnsupportedOperationException("Can not construct abstract 
class.");
   }
 
   public Expression visitBetweenExpression(
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