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

hansva pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/hop.git


The following commit(s) were added to refs/heads/main by this push:
     new fec02d0685 Fix load Janino codeExclusions from classpath in embedded 
deployments (#7481)
fec02d0685 is described below

commit fec02d06859438756bf110a9946d12c1ab7d1bbd
Author: Lance <[email protected]>
AuthorDate: Mon Jul 13 22:17:24 2026 +0800

    Fix load Janino codeExclusions from classpath in embedded deployments 
(#7481)
    
    Signed-off-by: lance <[email protected]>
---
 .../transforms/util/JaninoCheckerUtil.java         | 89 ++++++++++++++++++----
 .../transforms/util/JaninoCheckerUtilTest.java     | 10 ++-
 2 files changed, 81 insertions(+), 18 deletions(-)

diff --git 
a/plugins/transforms/janino/src/main/java/org/apache/hop/pipeline/transforms/util/JaninoCheckerUtil.java
 
b/plugins/transforms/janino/src/main/java/org/apache/hop/pipeline/transforms/util/JaninoCheckerUtil.java
index 3ced991b15..4b314116af 100644
--- 
a/plugins/transforms/janino/src/main/java/org/apache/hop/pipeline/transforms/util/JaninoCheckerUtil.java
+++ 
b/plugins/transforms/janino/src/main/java/org/apache/hop/pipeline/transforms/util/JaninoCheckerUtil.java
@@ -17,34 +17,38 @@
 package org.apache.hop.pipeline.transforms.util;
 
 import java.io.File;
+import java.io.InputStream;
 import java.util.ArrayList;
 import java.util.List;
+import org.apache.hop.core.exception.HopXmlException;
 import org.apache.hop.core.logging.LogChannel;
 import org.apache.hop.core.xml.XmlHandler;
 import org.w3c.dom.Document;
 import org.w3c.dom.Node;
 
+/** Utility that checks Janino user code against a configurable deny-list of 
substrings. */
 public class JaninoCheckerUtil {
+  private static final String EXCLUSIONS_FILE_NAME = "codeExclusions.xml";
+
   List<String> matches = new ArrayList<>();
 
+  /**
+   * Loads exclusion patterns from {@value #EXCLUSIONS_FILE_NAME}. An external 
file next to the
+   * plugin jar is tried first (Hop installation layout); if absent, the 
bundled classpath resource
+   * is used (Maven, Spring Boot, and nested jar deployments).
+   */
   public JaninoCheckerUtil() {
-    String path = "";
-
-    try {
-      path = getJarPath() + File.separator + "codeExclusions.xml";
-
-      Document document = XmlHandler.loadXmlFile(path);
-      Node exclusionsNode = XmlHandler.getSubNode(document, "exclusions");
-      List<Node> exclusionNodes = XmlHandler.getNodes(exclusionsNode, 
"exclusion");
-
-      for (Node exclusionNode : exclusionNodes) {
-        matches.add(exclusionNode.getTextContent());
-      }
-    } catch (Exception e) {
-      LogChannel.GENERAL.logError("Unable to load exclusions from: '" + path + 
"'", e);
+    if (!loadExclusionsFromExternalFile()) {
+      loadExclusionsFromClasspath();
     }
   }
 
+  /**
+   * Returns exclusion patterns found as substrings in the given source code.
+   *
+   * @param code Janino expression or Java class source to validate
+   * @return matched exclusion strings, empty when none apply
+   */
   public List<String> checkCode(String code) {
     List<String> foundBlocks = new ArrayList<>();
     for (String s : matches) {
@@ -55,9 +59,66 @@ public class JaninoCheckerUtil {
     return foundBlocks;
   }
 
+  /**
+   * Returns the directory containing the janino plugin jar (parent of the 
code source location).
+   *
+   * @return absolute path to the jar directory
+   */
   public String getJarPath() {
     return new File(
             
JaninoCheckerUtil.class.getProtectionDomain().getCodeSource().getLocation().getPath())
         .getParent();
   }
+
+  /** Path to an optional external {@value #EXCLUSIONS_FILE_NAME} beside the 
plugin jar. */
+  private String getExternalExclusionsPath() {
+    return getJarPath() + File.separator + EXCLUSIONS_FILE_NAME;
+  }
+
+  /**
+   * Loads exclusions from a filesystem file when present (custom Hop 
installation config).
+   *
+   * @return {@code true} when a regular file was found and parsed successfully
+   */
+  private boolean loadExclusionsFromExternalFile() {
+    String path = getExternalExclusionsPath();
+    File file = new File(path);
+    if (!file.isFile()) {
+      return false;
+    }
+    try {
+      parseExclusions(XmlHandler.loadXmlFile(path));
+      return true;
+    } catch (Exception e) {
+      LogChannel.GENERAL.logError("Unable to load exclusions from: '" + path + 
"'", e);
+      return false;
+    }
+  }
+
+  /** Loads the bundled {@value #EXCLUSIONS_FILE_NAME} from the plugin 
classpath. */
+  private void loadExclusionsFromClasspath() {
+    try (InputStream is = JaninoCheckerUtil.class.getResourceAsStream("/" + 
EXCLUSIONS_FILE_NAME)) {
+      if (is == null) {
+        return;
+      }
+
+      parseExclusions(XmlHandler.loadXmlFile(is));
+    } catch (Exception e) {
+      LogChannel.GENERAL.logDebug(
+          "Unable to load default exclusions from classpath: " + 
EXCLUSIONS_FILE_NAME, e);
+    }
+  }
+
+  /** Parses {@code <exclusion>} entries from a loaded exclusions document 
into {@link #matches}. */
+  private void parseExclusions(Document document) throws HopXmlException {
+    if (document == null) {
+      throw new HopXmlException("Exclusions document is null");
+    }
+
+    Node exclusionsNode = XmlHandler.getSubNode(document, "exclusions");
+    List<Node> exclusionNodes = XmlHandler.getNodes(exclusionsNode, 
"exclusion");
+    for (Node exclusionNode : exclusionNodes) {
+      matches.add(exclusionNode.getTextContent());
+    }
+  }
 }
diff --git 
a/plugins/transforms/janino/src/test/java/org/apache/hop/pipeline/transforms/util/JaninoCheckerUtilTest.java
 
b/plugins/transforms/janino/src/test/java/org/apache/hop/pipeline/transforms/util/JaninoCheckerUtilTest.java
index fff1da2d38..c984988b3b 100644
--- 
a/plugins/transforms/janino/src/test/java/org/apache/hop/pipeline/transforms/util/JaninoCheckerUtilTest.java
+++ 
b/plugins/transforms/janino/src/test/java/org/apache/hop/pipeline/transforms/util/JaninoCheckerUtilTest.java
@@ -24,9 +24,12 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
 
 import java.util.List;
 import org.apache.hop.core.logging.HopLogStore;
+import org.apache.hop.junit.rules.RestoreHopEngineEnvironmentExtension;
 import org.junit.jupiter.api.BeforeAll;
 import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
 
+@ExtendWith(RestoreHopEngineEnvironmentExtension.class)
 class JaninoCheckerUtilTest {
 
   @BeforeAll
@@ -37,15 +40,14 @@ class JaninoCheckerUtilTest {
   // ------------------------------------------------------------------ 
constructor
 
   @Test
-  void constructor_noExclusionsFile_doesNotThrow() {
-    // The codeExclusions.xml file will not be found in test env → logged, no 
throw
+  void constructor_loadsDefaultExclusionsWithoutError() {
     assertDoesNotThrow(JaninoCheckerUtil::new);
   }
 
   @Test
-  void constructor_whenFileNotFound_matchesListIsEmpty() {
+  void 
constructor_loadsFromClasspathWhenNoExternalFile_matchesListIsEmptyByDefault() {
     JaninoCheckerUtil util = new JaninoCheckerUtil();
-    // No exclusions file in test classpath → empty matches
+    // Default bundled codeExclusions.xml has an empty <exclusions> list
     assertTrue(util.matches.isEmpty());
   }
 

Reply via email to