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());
}