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

wusheng pushed a commit to branch groovy-replace
in repository https://gitbox.apache.org/repos/asf/skywalking.git

commit 0b8ec1cdb5cba3443e5e72c729a332fc6ffbf987
Author: Wu Sheng <[email protected]>
AuthorDate: Sat Feb 28 16:13:57 2026 +0800

    Refactor hierarchy rules: replace Groovy Closure with Java BiFunction 
(Phase 5)
    
    Extract hierarchy matching rules from HierarchyDefinitionService into
    pluggable HierarchyRuleProvider interface. Remove Groovy imports from
    server-core by replacing Closure<Boolean> with 
BiFunction<Service,Service,Boolean>.
    
    - hierarchy-v1: GroovyHierarchyRuleProvider (for CI checker only)
    - hierarchy-v2: JavaHierarchyRuleProvider with 4 built-in rules + 12 tests
    - HierarchyDefinitionService: add HierarchyRuleProvider interface, 
DefaultJavaRuleProvider
    - HierarchyService: .getClosure().call() → .match()
    
    Co-Authored-By: Claude Opus 4.6 <[email protected]>
---
 oap-server/analyzer/{ => hierarchy-v1}/pom.xml     |  29 +---
 .../core/config/GroovyHierarchyRuleProvider.java   |  49 +++++++
 oap-server/analyzer/{ => hierarchy-v2}/pom.xml     |  29 +---
 .../core/config/JavaHierarchyRuleProvider.java     |  86 ++++++++++++
 .../core/config/JavaHierarchyRuleProviderTest.java | 155 +++++++++++++++++++++
 oap-server/analyzer/pom.xml                        |   2 +
 .../core/config/HierarchyDefinitionService.java    | 109 ++++++++++++---
 .../server/core/hierarchy/HierarchyService.java    |  10 +-
 8 files changed, 394 insertions(+), 75 deletions(-)

diff --git a/oap-server/analyzer/pom.xml 
b/oap-server/analyzer/hierarchy-v1/pom.xml
similarity index 62%
copy from oap-server/analyzer/pom.xml
copy to oap-server/analyzer/hierarchy-v1/pom.xml
index 5053d0e628..0e2cf572f2 100644
--- a/oap-server/analyzer/pom.xml
+++ b/oap-server/analyzer/hierarchy-v1/pom.xml
@@ -19,41 +19,24 @@
 
 <project xmlns="http://maven.apache.org/POM/4.0.0"; 
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"; 
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
http://maven.apache.org/xsd/maven-4.0.0.xsd";>
     <parent>
-        <artifactId>oap-server</artifactId>
+        <artifactId>analyzer</artifactId>
         <groupId>org.apache.skywalking</groupId>
         <version>${revision}</version>
     </parent>
     <modelVersion>4.0.0</modelVersion>
 
-    <artifactId>analyzer</artifactId>
-    <packaging>pom</packaging>
-
-    <modules>
-        <module>agent-analyzer</module>
-        <module>log-analyzer</module>
-        <module>meter-analyzer</module>
-        <module>event-analyzer</module>
-        <module>mal-transpiler</module>
-        <module>lal-transpiler</module>
-        <module>meter-analyzer-v2</module>
-        <module>log-analyzer-v2</module>
-    </modules>
+    <artifactId>hierarchy-v1</artifactId>
+    <description>Groovy-based hierarchy rule provider (for checker module 
only, not runtime)</description>
 
     <dependencies>
         <dependency>
             <groupId>org.apache.skywalking</groupId>
-            <artifactId>apm-network</artifactId>
+            <artifactId>server-core</artifactId>
             <version>${project.version}</version>
         </dependency>
         <dependency>
-            <groupId>org.apache.skywalking</groupId>
-            <artifactId>library-module</artifactId>
-            <version>${project.version}</version>
-        </dependency>
-        <dependency>
-            <groupId>org.apache.skywalking</groupId>
-            <artifactId>library-util</artifactId>
-            <version>${project.version}</version>
+            <groupId>org.apache.groovy</groupId>
+            <artifactId>groovy</artifactId>
         </dependency>
     </dependencies>
 </project>
diff --git 
a/oap-server/analyzer/hierarchy-v1/src/main/java/org/apache/skywalking/oap/server/core/config/GroovyHierarchyRuleProvider.java
 
b/oap-server/analyzer/hierarchy-v1/src/main/java/org/apache/skywalking/oap/server/core/config/GroovyHierarchyRuleProvider.java
new file mode 100644
index 0000000000..c8e8af4305
--- /dev/null
+++ 
b/oap-server/analyzer/hierarchy-v1/src/main/java/org/apache/skywalking/oap/server/core/config/GroovyHierarchyRuleProvider.java
@@ -0,0 +1,49 @@
+/*
+ * 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.skywalking.oap.server.core.config;
+
+import groovy.lang.Closure;
+import groovy.lang.GroovyShell;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.function.BiFunction;
+import org.apache.skywalking.oap.server.core.query.type.Service;
+
+/**
+ * Groovy-based hierarchy rule provider. Uses GroovyShell.evaluate() to compile
+ * hierarchy matching rule closures from YAML expressions.
+ *
+ * <p>This provider is NOT included in the runtime classpath. It is only used
+ * by the hierarchy-v1-v2-checker module for CI validation against the pure 
Java
+ * provider (hierarchy-v2).
+ */
+public final class GroovyHierarchyRuleProvider implements 
HierarchyDefinitionService.HierarchyRuleProvider {
+
+    @Override
+    @SuppressWarnings("unchecked")
+    public Map<String, BiFunction<Service, Service, Boolean>> buildRules(
+            final Map<String, String> ruleExpressions) {
+        final Map<String, BiFunction<Service, Service, Boolean>> rules = new 
HashMap<>();
+        final GroovyShell sh = new GroovyShell();
+        ruleExpressions.forEach((name, expression) -> {
+            final Closure<Boolean> closure = (Closure<Boolean>) 
sh.evaluate(expression);
+            rules.put(name, (u, l) -> closure.call(u, l));
+        });
+        return rules;
+    }
+}
diff --git a/oap-server/analyzer/pom.xml 
b/oap-server/analyzer/hierarchy-v2/pom.xml
similarity index 61%
copy from oap-server/analyzer/pom.xml
copy to oap-server/analyzer/hierarchy-v2/pom.xml
index 5053d0e628..42e49678b2 100644
--- a/oap-server/analyzer/pom.xml
+++ b/oap-server/analyzer/hierarchy-v2/pom.xml
@@ -19,40 +19,19 @@
 
 <project xmlns="http://maven.apache.org/POM/4.0.0"; 
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"; 
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
http://maven.apache.org/xsd/maven-4.0.0.xsd";>
     <parent>
-        <artifactId>oap-server</artifactId>
+        <artifactId>analyzer</artifactId>
         <groupId>org.apache.skywalking</groupId>
         <version>${revision}</version>
     </parent>
     <modelVersion>4.0.0</modelVersion>
 
-    <artifactId>analyzer</artifactId>
-    <packaging>pom</packaging>
-
-    <modules>
-        <module>agent-analyzer</module>
-        <module>log-analyzer</module>
-        <module>meter-analyzer</module>
-        <module>event-analyzer</module>
-        <module>mal-transpiler</module>
-        <module>lal-transpiler</module>
-        <module>meter-analyzer-v2</module>
-        <module>log-analyzer-v2</module>
-    </modules>
+    <artifactId>hierarchy-v2</artifactId>
+    <description>Pure Java hierarchy rule provider with static rule registry 
(no Groovy)</description>
 
     <dependencies>
         <dependency>
             <groupId>org.apache.skywalking</groupId>
-            <artifactId>apm-network</artifactId>
-            <version>${project.version}</version>
-        </dependency>
-        <dependency>
-            <groupId>org.apache.skywalking</groupId>
-            <artifactId>library-module</artifactId>
-            <version>${project.version}</version>
-        </dependency>
-        <dependency>
-            <groupId>org.apache.skywalking</groupId>
-            <artifactId>library-util</artifactId>
+            <artifactId>server-core</artifactId>
             <version>${project.version}</version>
         </dependency>
     </dependencies>
diff --git 
a/oap-server/analyzer/hierarchy-v2/src/main/java/org/apache/skywalking/oap/server/core/config/JavaHierarchyRuleProvider.java
 
b/oap-server/analyzer/hierarchy-v2/src/main/java/org/apache/skywalking/oap/server/core/config/JavaHierarchyRuleProvider.java
new file mode 100644
index 0000000000..d85bdb99c7
--- /dev/null
+++ 
b/oap-server/analyzer/hierarchy-v2/src/main/java/org/apache/skywalking/oap/server/core/config/JavaHierarchyRuleProvider.java
@@ -0,0 +1,86 @@
+/*
+ * 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.skywalking.oap.server.core.config;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Objects;
+import java.util.function.BiFunction;
+import org.apache.skywalking.oap.server.core.query.type.Service;
+
+/**
+ * Pure Java hierarchy rule provider. Contains a static registry of all known
+ * hierarchy matching rules as Java lambdas. Zero Groovy dependency.
+ *
+ * <p>Rule names must match those in hierarchy-definition.yml 
auto-matching-rules section.
+ * Unknown rule names fail fast at startup with IllegalArgumentException.
+ */
+public final class JavaHierarchyRuleProvider implements 
HierarchyDefinitionService.HierarchyRuleProvider {
+
+    private static final Map<String, BiFunction<Service, Service, Boolean>> 
RULE_REGISTRY;
+
+    static {
+        RULE_REGISTRY = new HashMap<>();
+
+        // name: { (u, l) -> u.name == l.name }
+        RULE_REGISTRY.put("name",
+            (u, l) -> Objects.equals(u.getName(), l.getName()));
+
+        // short-name: { (u, l) -> u.shortName == l.shortName }
+        RULE_REGISTRY.put("short-name",
+            (u, l) -> Objects.equals(u.getShortName(), l.getShortName()));
+
+        // lower-short-name-remove-ns:
+        // { (u, l) -> { if(l.shortName.lastIndexOf('.') > 0)
+        //     return u.shortName == l.shortName.substring(0, 
l.shortName.lastIndexOf('.'));
+        //     return false; } }
+        RULE_REGISTRY.put("lower-short-name-remove-ns", (u, l) -> {
+            final String sn = l.getShortName();
+            final int dot = sn.lastIndexOf('.');
+            return dot > 0 && Objects.equals(u.getShortName(), sn.substring(0, 
dot));
+        });
+
+        // lower-short-name-with-fqdn:
+        // { (u, l) -> { if(u.shortName.lastIndexOf(':') > 0)
+        //     return u.shortName.substring(0, u.shortName.lastIndexOf(':')) 
== l.shortName.concat('.svc.cluster.local');
+        //     return false; } }
+        RULE_REGISTRY.put("lower-short-name-with-fqdn", (u, l) -> {
+            final String sn = u.getShortName();
+            final int colon = sn.lastIndexOf(':');
+            return colon > 0 && Objects.equals(
+                sn.substring(0, colon),
+                l.getShortName() + ".svc.cluster.local");
+        });
+    }
+
+    @Override
+    public Map<String, BiFunction<Service, Service, Boolean>> buildRules(
+            final Map<String, String> ruleExpressions) {
+        final Map<String, BiFunction<Service, Service, Boolean>> rules = new 
HashMap<>();
+        ruleExpressions.forEach((name, expression) -> {
+            final BiFunction<Service, Service, Boolean> fn = 
RULE_REGISTRY.get(name);
+            if (fn == null) {
+                throw new IllegalArgumentException(
+                    "Unknown hierarchy matching rule: " + name
+                        + ". Known rules: " + RULE_REGISTRY.keySet());
+            }
+            rules.put(name, fn);
+        });
+        return rules;
+    }
+}
diff --git 
a/oap-server/analyzer/hierarchy-v2/src/test/java/org/apache/skywalking/oap/server/core/config/JavaHierarchyRuleProviderTest.java
 
b/oap-server/analyzer/hierarchy-v2/src/test/java/org/apache/skywalking/oap/server/core/config/JavaHierarchyRuleProviderTest.java
new file mode 100644
index 0000000000..699d3af4aa
--- /dev/null
+++ 
b/oap-server/analyzer/hierarchy-v2/src/test/java/org/apache/skywalking/oap/server/core/config/JavaHierarchyRuleProviderTest.java
@@ -0,0 +1,155 @@
+/*
+ * 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.skywalking.oap.server.core.config;
+
+import java.util.Map;
+import java.util.function.BiFunction;
+import org.apache.skywalking.oap.server.core.query.type.Service;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+class JavaHierarchyRuleProviderTest {
+
+    private JavaHierarchyRuleProvider provider;
+
+    @BeforeEach
+    void setUp() {
+        provider = new JavaHierarchyRuleProvider();
+    }
+
+    private Service svc(final String name, final String shortName) {
+        final Service s = new Service();
+        s.setName(name);
+        s.setShortName(shortName);
+        return s;
+    }
+
+    // ---- name rule ----
+
+    @Test
+    void nameRuleMatches() {
+        final Map<String, BiFunction<Service, Service, Boolean>> rules =
+            provider.buildRules(Map.of("name", "{ (u, l) -> u.name == l.name 
}"));
+        assertTrue(rules.get("name").apply(svc("svc", "svc"), svc("svc", 
"svc")));
+    }
+
+    @Test
+    void nameRuleDoesNotMatch() {
+        final Map<String, BiFunction<Service, Service, Boolean>> rules =
+            provider.buildRules(Map.of("name", "ignored"));
+        assertFalse(rules.get("name").apply(svc("svc", "svc"), svc("other", 
"other")));
+    }
+
+    // ---- short-name rule ----
+
+    @Test
+    void shortNameRuleMatches() {
+        final Map<String, BiFunction<Service, Service, Boolean>> rules =
+            provider.buildRules(Map.of("short-name", "ignored"));
+        assertTrue(rules.get("short-name").apply(svc("a", "svc"), svc("b", 
"svc")));
+    }
+
+    @Test
+    void shortNameRuleDoesNotMatch() {
+        final Map<String, BiFunction<Service, Service, Boolean>> rules =
+            provider.buildRules(Map.of("short-name", "ignored"));
+        assertFalse(rules.get("short-name").apply(svc("a", "svc1"), svc("b", 
"svc2")));
+    }
+
+    // ---- lower-short-name-remove-ns rule ----
+
+    @Test
+    void lowerShortNameRemoveNsMatches() {
+        final Map<String, BiFunction<Service, Service, Boolean>> rules =
+            provider.buildRules(Map.of("lower-short-name-remove-ns", 
"ignored"));
+        // l.shortName = "svc.namespace", u.shortName = "svc"
+        assertTrue(rules.get("lower-short-name-remove-ns")
+            .apply(svc("a", "svc"), svc("b", "svc.namespace")));
+    }
+
+    @Test
+    void lowerShortNameRemoveNsNoDot() {
+        final Map<String, BiFunction<Service, Service, Boolean>> rules =
+            provider.buildRules(Map.of("lower-short-name-remove-ns", 
"ignored"));
+        assertFalse(rules.get("lower-short-name-remove-ns")
+            .apply(svc("a", "svc"), svc("b", "svc")));
+    }
+
+    @Test
+    void lowerShortNameRemoveNsMismatch() {
+        final Map<String, BiFunction<Service, Service, Boolean>> rules =
+            provider.buildRules(Map.of("lower-short-name-remove-ns", 
"ignored"));
+        assertFalse(rules.get("lower-short-name-remove-ns")
+            .apply(svc("a", "other"), svc("b", "svc.namespace")));
+    }
+
+    // ---- lower-short-name-with-fqdn rule ----
+
+    @Test
+    void lowerShortNameWithFqdnMatches() {
+        final Map<String, BiFunction<Service, Service, Boolean>> rules =
+            provider.buildRules(Map.of("lower-short-name-with-fqdn", 
"ignored"));
+        // u.shortName = "db:3306", l.shortName = "db" -> "db" == 
"db.svc.cluster.local"? no
+        // u.shortName = "db:3306", l.shortName should match: u prefix = "db", 
l + fqdn = "db.svc.cluster.local"
+        assertTrue(rules.get("lower-short-name-with-fqdn")
+            .apply(svc("a", "db.svc.cluster.local:3306"), svc("b", "db")));
+    }
+
+    @Test
+    void lowerShortNameWithFqdnNoColon() {
+        final Map<String, BiFunction<Service, Service, Boolean>> rules =
+            provider.buildRules(Map.of("lower-short-name-with-fqdn", 
"ignored"));
+        assertFalse(rules.get("lower-short-name-with-fqdn")
+            .apply(svc("a", "db"), svc("b", "db")));
+    }
+
+    @Test
+    void lowerShortNameWithFqdnWrongSuffix() {
+        final Map<String, BiFunction<Service, Service, Boolean>> rules =
+            provider.buildRules(Map.of("lower-short-name-with-fqdn", 
"ignored"));
+        assertFalse(rules.get("lower-short-name-with-fqdn")
+            .apply(svc("a", "db:3306"), svc("b", "other")));
+    }
+
+    // ---- unknown rule ----
+
+    @Test
+    void unknownRuleThrows() {
+        assertThrows(IllegalArgumentException.class,
+            () -> provider.buildRules(Map.of("unknown-rule", "ignored")));
+    }
+
+    // ---- builds all 4 rules ----
+
+    @Test
+    void buildsAllFourRules() {
+        final Map<String, BiFunction<Service, Service, Boolean>> rules =
+            provider.buildRules(Map.of(
+                "name", "ignored",
+                "short-name", "ignored",
+                "lower-short-name-remove-ns", "ignored",
+                "lower-short-name-with-fqdn", "ignored"
+            ));
+        assertEquals(4, rules.size());
+    }
+}
diff --git a/oap-server/analyzer/pom.xml b/oap-server/analyzer/pom.xml
index 5053d0e628..b6b4fb531d 100644
--- a/oap-server/analyzer/pom.xml
+++ b/oap-server/analyzer/pom.xml
@@ -37,6 +37,8 @@
         <module>lal-transpiler</module>
         <module>meter-analyzer-v2</module>
         <module>log-analyzer-v2</module>
+        <module>hierarchy-v1</module>
+        <module>hierarchy-v2</module>
     </modules>
 
     <dependencies>
diff --git 
a/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/config/HierarchyDefinitionService.java
 
b/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/config/HierarchyDefinitionService.java
index fbec34cf6c..737e50ebed 100644
--- 
a/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/config/HierarchyDefinitionService.java
+++ 
b/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/config/HierarchyDefinitionService.java
@@ -18,17 +18,18 @@
 
 package org.apache.skywalking.oap.server.core.config;
 
-import groovy.lang.Closure;
-import groovy.lang.GroovyShell;
 import java.io.FileNotFoundException;
 import java.io.Reader;
 import java.util.HashMap;
 import java.util.Map;
+import java.util.Objects;
+import java.util.function.BiFunction;
 import lombok.Getter;
 import lombok.extern.slf4j.Slf4j;
 import org.apache.skywalking.oap.server.core.CoreModuleConfig;
 import org.apache.skywalking.oap.server.core.UnexpectedException;
 import org.apache.skywalking.oap.server.core.analysis.Layer;
+import org.apache.skywalking.oap.server.core.query.type.Service;
 import org.apache.skywalking.oap.server.library.util.ResourceUtils;
 import org.yaml.snakeyaml.Yaml;
 
@@ -37,36 +38,99 @@ import static java.util.stream.Collectors.toMap;
 @Slf4j
 public class HierarchyDefinitionService implements 
org.apache.skywalking.oap.server.library.module.Service {
 
+    /**
+     * Functional interface for building hierarchy matching rules.
+     * Implementations are provided by hierarchy-v1 (Groovy) or hierarchy-v2 
(pure Java).
+     */
+    @FunctionalInterface
+    public interface HierarchyRuleProvider {
+        Map<String, BiFunction<Service, Service, Boolean>> 
buildRules(Map<String, String> ruleExpressions);
+    }
+
     @Getter
     private final Map<String, Map<String, MatchingRule>> hierarchyDefinition;
     @Getter
     private Map<String, Integer> layerLevels;
     private Map<String, MatchingRule> matchingRules;
 
-    public HierarchyDefinitionService(CoreModuleConfig moduleConfig) {
+    public HierarchyDefinitionService(final CoreModuleConfig moduleConfig,
+                                      final HierarchyRuleProvider 
ruleProvider) {
         this.hierarchyDefinition = new HashMap<>();
         this.layerLevels = new HashMap<>();
         if (moduleConfig.isEnableHierarchy()) {
-            this.init();
+            this.init(ruleProvider);
             this.checkLayers();
         }
     }
 
+    /**
+     * Convenience constructor that uses the default Java rule provider.
+     */
+    public HierarchyDefinitionService(final CoreModuleConfig moduleConfig) {
+        this(moduleConfig, new DefaultJavaRuleProvider());
+    }
+
+    /**
+     * Default pure Java rule provider with 4 built-in hierarchy matching 
rules.
+     * No Groovy dependency.
+     */
+    private static class DefaultJavaRuleProvider implements 
HierarchyRuleProvider {
+        @Override
+        public Map<String, BiFunction<Service, Service, Boolean>> buildRules(
+                final Map<String, String> ruleExpressions) {
+            final Map<String, BiFunction<Service, Service, Boolean>> registry 
= new HashMap<>();
+            registry.put("name", (u, l) -> Objects.equals(u.getName(), 
l.getName()));
+            registry.put("short-name", (u, l) -> 
Objects.equals(u.getShortName(), l.getShortName()));
+            registry.put("lower-short-name-remove-ns", (u, l) -> {
+                final String sn = l.getShortName();
+                final int dot = sn.lastIndexOf('.');
+                return dot > 0 && Objects.equals(u.getShortName(), 
sn.substring(0, dot));
+            });
+            registry.put("lower-short-name-with-fqdn", (u, l) -> {
+                final String sn = u.getShortName();
+                final int colon = sn.lastIndexOf(':');
+                return colon > 0 && Objects.equals(
+                    sn.substring(0, colon),
+                    l.getShortName() + ".svc.cluster.local");
+            });
+
+            final Map<String, BiFunction<Service, Service, Boolean>> rules = 
new HashMap<>();
+            ruleExpressions.forEach((name, expression) -> {
+                final BiFunction<Service, Service, Boolean> fn = 
registry.get(name);
+                if (fn == null) {
+                    throw new IllegalArgumentException(
+                        "Unknown hierarchy matching rule: " + name
+                            + ". Known rules: " + registry.keySet());
+                }
+                rules.put(name, fn);
+            });
+            return rules;
+        }
+    }
+
     @SuppressWarnings("unchecked")
-    private void init() {
+    private void init(final HierarchyRuleProvider ruleProvider) {
         try {
-            Reader applicationReader = 
ResourceUtils.read("hierarchy-definition.yml");
-            Yaml yaml = new Yaml();
-            Map<String, Map> config = yaml.loadAs(applicationReader, 
Map.class);
-            Map<String, Map<String, String>> hierarchy = (Map<String, 
Map<String, String>>) config.get("hierarchy");
-            Map<String, String> matchingRules = (Map<String, String>) 
config.get("auto-matching-rules");
+            final Reader applicationReader = 
ResourceUtils.read("hierarchy-definition.yml");
+            final Yaml yaml = new Yaml();
+            final Map<String, Map> config = yaml.loadAs(applicationReader, 
Map.class);
+            final Map<String, Map<String, String>> hierarchy = (Map<String, 
Map<String, String>>) config.get("hierarchy");
+            final Map<String, String> ruleExpressions = (Map<String, String>) 
config.get("auto-matching-rules");
             this.layerLevels = (Map<String, Integer>) 
config.get("layer-levels");
-            this.matchingRules = matchingRules.entrySet().stream().map(entry 
-> {
-                MatchingRule matchingRule = new MatchingRule(entry.getKey(), 
entry.getValue());
+
+            final Map<String, BiFunction<Service, Service, Boolean>> 
builtRules = ruleProvider.buildRules(ruleExpressions);
+
+            this.matchingRules = ruleExpressions.entrySet().stream().map(entry 
-> {
+                final BiFunction<Service, Service, Boolean> matcher = 
builtRules.get(entry.getKey());
+                if (matcher == null) {
+                    throw new IllegalStateException(
+                        "HierarchyRuleProvider did not produce a matcher for 
rule: " + entry.getKey());
+                }
+                final MatchingRule matchingRule = new 
MatchingRule(entry.getKey(), entry.getValue(), matcher);
                 return Map.entry(entry.getKey(), matchingRule);
             }).collect(toMap(Map.Entry::getKey, Map.Entry::getValue));
             hierarchy.forEach((layer, lowerLayers) -> {
-                Map<String, MatchingRule> rules = new HashMap<>();
+                final Map<String, MatchingRule> rules = new HashMap<>();
                 lowerLayers.forEach((lowerLayer, ruleName) -> {
                     rules.put(lowerLayer, this.matchingRules.get(ruleName));
                 });
@@ -85,14 +149,14 @@ public class HierarchyDefinitionService implements 
org.apache.skywalking.oap.ser
             }
         });
         this.hierarchyDefinition.forEach((layer, lowerLayers) -> {
-            Integer layerLevel = this.layerLevels.get(layer);
+            final Integer layerLevel = this.layerLevels.get(layer);
             if (this.layerLevels.get(layer) == null) {
                 throw new IllegalArgumentException(
                     "hierarchy-definition.yml  layer-levels: " + layer + " is 
not defined");
             }
 
-            for (String lowerLayer : lowerLayers.keySet()) {
-                Integer lowerLayerLevel = this.layerLevels.get(lowerLayer);
+            for (final String lowerLayer : lowerLayers.keySet()) {
+                final Integer lowerLayerLevel = 
this.layerLevels.get(lowerLayer);
                 if (lowerLayerLevel == null) {
                     throw new IllegalArgumentException(
                         "hierarchy-definition.yml  layer-levels: " + 
lowerLayer + " is not defined.");
@@ -109,14 +173,17 @@ public class HierarchyDefinitionService implements 
org.apache.skywalking.oap.ser
     public static class MatchingRule {
         private final String name;
         private final String expression;
-        private final Closure<Boolean> closure;
+        private final BiFunction<Service, Service, Boolean> matcher;
 
-        @SuppressWarnings("unchecked")
-        public MatchingRule(final String name, final String expression) {
+        public MatchingRule(final String name, final String expression,
+                            final BiFunction<Service, Service, Boolean> 
matcher) {
             this.name = name;
             this.expression = expression;
-            GroovyShell sh = new GroovyShell();
-            closure = (Closure<Boolean>) sh.evaluate(expression);
+            this.matcher = matcher;
+        }
+
+        public boolean match(final Service upper, final Service lower) {
+            return matcher.apply(upper, lower);
         }
     }
 }
diff --git 
a/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/hierarchy/HierarchyService.java
 
b/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/hierarchy/HierarchyService.java
index 02e4014229..5eaa22752e 100644
--- 
a/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/hierarchy/HierarchyService.java
+++ 
b/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/hierarchy/HierarchyService.java
@@ -199,8 +199,7 @@ public class HierarchyService implements 
org.apache.skywalking.oap.server.librar
                     if (lowerLayers != null && 
lowerLayers.get(comparedServiceLayer) != null) {
                         try {
                             if (lowerLayers.get(comparedServiceLayer)
-                                           .getClosure()
-                                           .call(service, comparedService)) {
+                                           .match(service, comparedService)) {
                                 autoMatchingServiceRelation(service.getName(), 
Layer.nameOf(serviceLayer),
                                                             
comparedService.getName(),
                                                             
Layer.nameOf(comparedServiceLayer)
@@ -208,7 +207,7 @@ public class HierarchyService implements 
org.apache.skywalking.oap.server.librar
                             }
                         } catch (Throwable e) {
                             log.error(
-                                "Auto matching service hierarchy from service 
traffic failure. Upper layer {}, lower layer {}, closure{}",
+                                "Auto matching service hierarchy from service 
traffic failure. Upper layer {}, lower layer {}, rule {}",
                                 serviceLayer,
                                 comparedServiceLayer,
                                 
lowerLayers.get(comparedServiceLayer).getExpression(), e
@@ -218,8 +217,7 @@ public class HierarchyService implements 
org.apache.skywalking.oap.server.librar
                     } else if (comparedLowerLayers != null && 
comparedLowerLayers.get(serviceLayer) != null) {
                         try {
                             if (comparedLowerLayers.get(serviceLayer)
-                                                   .getClosure()
-                                                   .call(comparedService, 
service)) {
+                                                   .match(comparedService, 
service)) {
                                 autoMatchingServiceRelation(
                                     comparedService.getName(),
                                     Layer.nameOf(comparedServiceLayer),
@@ -229,7 +227,7 @@ public class HierarchyService implements 
org.apache.skywalking.oap.server.librar
                             }
                         } catch (Throwable e) {
                             log.error(
-                                "Auto matching service hierarchy from service 
traffic failure. Upper layer {}, lower layer {}, closure{}",
+                                "Auto matching service hierarchy from service 
traffic failure. Upper layer {}, lower layer {}, rule {}",
                                 comparedServiceLayer,
                                 serviceLayer,
                                 
comparedLowerLayers.get(serviceLayer).getExpression(), e

Reply via email to