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

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

commit 9a4c809699d35130f33ea0714cab253c601cf8ad
Author: Wu Sheng <[email protected]>
AuthorDate: Wed Feb 18 22:03:57 2026 +0800

    Add OAL immigration: same-FQCN replacement classes + annotation manifests
    
    Replace 3 upstream classes via Maven classpath precedence to eliminate
    runtime Javassist code generation and Guava ClassPath scanning:
    
    - OALEngineLoaderService: loads pre-compiled OAL classes from build-time
      manifests instead of running the OAL engine at startup
    - AnnotationScan: reads annotation manifests from META-INF/annotation-scan/
      instead of Guava ClassPath.from() scanning
    - SourceReceiverImpl: reads dispatcher/decorator manifests instead of
      Guava classpath scanning
    
    OALClassExporter enhanced to scan 6 annotation/interface types at build
    time (ScopeDeclaration, Stream, Disable, MultipleDisable, SourceDispatcher,
    ISourceDecorator) and write manifests.
    
    Verification: OALClassExporterTest (3 tests) + PrecompiledRegistrationTest
    (12 tests) covering manifest-vs-classpath comparison, class loading, scope
    registration, and source→dispatcher→metrics chain consistency.
    
    Co-Authored-By: Claude Opus 4.6 <[email protected]>
---
 .../server/buildtools/oal/OALClassExporter.java    |  87 ++++-
 .../buildtools/oal/OALClassExporterTest.java       |  26 ++
 oap-graalvm-server/pom.xml                         |  15 +
 .../oap/server/core/annotation/AnnotationScan.java | 125 +++++++
 .../server/core/oal/rt/OALEngineLoaderService.java | 129 +++++++
 .../oap/server/core/source/SourceReceiverImpl.java | 112 ++++++
 .../graalvm/PrecompiledRegistrationTest.java       | 392 +++++++++++++++++++++
 7 files changed, 881 insertions(+), 5 deletions(-)

diff --git 
a/build-tools/oal-exporter/src/main/java/org/apache/skywalking/oap/server/buildtools/oal/OALClassExporter.java
 
b/build-tools/oal-exporter/src/main/java/org/apache/skywalking/oap/server/buildtools/oal/OALClassExporter.java
index ded57e7..614763d 100644
--- 
a/build-tools/oal-exporter/src/main/java/org/apache/skywalking/oap/server/buildtools/oal/OALClassExporter.java
+++ 
b/build-tools/oal-exporter/src/main/java/org/apache/skywalking/oap/server/buildtools/oal/OALClassExporter.java
@@ -17,8 +17,12 @@
 
 package org.apache.skywalking.oap.server.buildtools.oal;
 
+import com.google.common.collect.ImmutableSet;
+import com.google.common.reflect.ClassPath;
 import java.io.IOException;
+import java.lang.annotation.Annotation;
 import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
 import java.nio.charset.StandardCharsets;
 import java.nio.file.Files;
 import java.nio.file.Path;
@@ -31,12 +35,17 @@ import lombok.extern.slf4j.Slf4j;
 import org.apache.skywalking.aop.server.receiver.mesh.MeshOALDefine;
 import org.apache.skywalking.oal.v2.OALEngineV2;
 import org.apache.skywalking.oal.v2.generator.OALClassGeneratorV2;
+import org.apache.skywalking.oap.server.core.analysis.Disable;
 import org.apache.skywalking.oap.server.core.analysis.DisableRegister;
+import org.apache.skywalking.oap.server.core.analysis.ISourceDecorator;
+import org.apache.skywalking.oap.server.core.analysis.MultipleDisable;
+import org.apache.skywalking.oap.server.core.analysis.SourceDispatcher;
 import org.apache.skywalking.oap.server.core.annotation.AnnotationScan;
 import org.apache.skywalking.oap.server.core.oal.rt.CoreOALDefine;
 import org.apache.skywalking.oap.server.core.oal.rt.DisableOALDefine;
 import org.apache.skywalking.oap.server.core.oal.rt.OALDefine;
 import org.apache.skywalking.oap.server.core.source.DefaultScopeDefine;
+import org.apache.skywalking.oap.server.core.source.ScopeDeclaration;
 import org.apache.skywalking.oap.server.core.storage.StorageBuilderFactory;
 import org.apache.skywalking.oap.server.fetcher.cilium.CiliumOALDefine;
 import 
org.apache.skywalking.oap.server.receiver.browser.provider.BrowserOALDefine;
@@ -47,13 +56,11 @@ import 
org.apache.skywalking.oap.server.receiver.jvm.provider.JVMOALDefine;
 
 /**
  * Build-time tool that runs the OAL engine for all 9 OALDefine configurations,
- * exports generated .class files (metrics, builders, dispatchers), and writes
- * manifest files listing all generated class names.
+ * exports generated .class files and manifest files, and scans the classpath 
for
+ * hardcoded annotated classes and interface implementations used at runtime.
  *
  * OAL script files are loaded from the skywalking submodule directly via
  * additionalClasspathElements in the exec-maven-plugin configuration.
- *
- * Uses the existing SW_OAL_ENGINE_DEBUG export mechanism in 
OALClassGeneratorV2.
  */
 @Slf4j
 public class OALClassExporter {
@@ -129,8 +136,31 @@ public class OALClassExporter {
         writeManifest(metaInf.resolve("oal-dispatcher-classes.txt"), 
dispatcherClasses);
         writeManifest(metaInf.resolve("oal-disabled-sources.txt"), 
disabledSources);
 
-        log.info("OAL Class Exporter: done - {} metrics, {} dispatchers, {} 
disabled sources",
+        log.info("OAL Class Exporter: {} metrics, {} dispatchers, {} disabled 
sources",
             metricsClasses.size(), dispatcherClasses.size(), 
disabledSources.size());
+
+        // ---- Annotation & interface scanning for hardcoded classes ----
+        Path annotationScanDir = metaInf.resolve("annotation-scan");
+        Files.createDirectories(annotationScanDir);
+
+        ImmutableSet<ClassPath.ClassInfo> allClasses = ClassPath
+            .from(OALClassExporter.class.getClassLoader())
+            .getTopLevelClassesRecursive("org.apache.skywalking");
+
+        writeManifest(annotationScanDir.resolve("ScopeDeclaration.txt"),
+            scanAnnotation(allClasses, ScopeDeclaration.class));
+        writeManifest(annotationScanDir.resolve("Stream.txt"),
+            scanAnnotation(allClasses, 
org.apache.skywalking.oap.server.core.analysis.Stream.class));
+        writeManifest(annotationScanDir.resolve("Disable.txt"),
+            scanAnnotation(allClasses, Disable.class));
+        writeManifest(annotationScanDir.resolve("MultipleDisable.txt"),
+            scanAnnotation(allClasses, MultipleDisable.class));
+        writeManifest(annotationScanDir.resolve("SourceDispatcher.txt"),
+            scanInterface(allClasses, SourceDispatcher.class));
+        writeManifest(annotationScanDir.resolve("ISourceDecorator.txt"),
+            scanInterface(allClasses, ISourceDecorator.class));
+
+        log.info("OAL Class Exporter: done");
     }
 
     /**
@@ -195,6 +225,53 @@ public class OALClassExporter {
         return result;
     }
 
+    /**
+     * Scan for classes annotated with the given annotation type.
+     */
+    private static List<String> scanAnnotation(
+        ImmutableSet<ClassPath.ClassInfo> allClasses,
+        Class<? extends Annotation> annotationType) {
+
+        List<String> result = new ArrayList<>();
+        for (ClassPath.ClassInfo classInfo : allClasses) {
+            try {
+                Class<?> aClass = classInfo.load();
+                if (aClass.isAnnotationPresent(annotationType)) {
+                    result.add(aClass.getName());
+                }
+            } catch (NoClassDefFoundError | Exception ignored) {
+                // Some classes may fail to load due to missing optional 
dependencies
+            }
+        }
+        Collections.sort(result);
+        log.info("Scanned @{}: {} classes", annotationType.getSimpleName(), 
result.size());
+        return result;
+    }
+
+    /**
+     * Scan for concrete classes implementing the given interface.
+     */
+    private static List<String> scanInterface(
+        ImmutableSet<ClassPath.ClassInfo> allClasses, Class<?> interfaceType) {
+
+        List<String> result = new ArrayList<>();
+        for (ClassPath.ClassInfo classInfo : allClasses) {
+            try {
+                Class<?> aClass = classInfo.load();
+                if (!aClass.isInterface()
+                    && !Modifier.isAbstract(aClass.getModifiers())
+                    && interfaceType.isAssignableFrom(aClass)) {
+                    result.add(aClass.getName());
+                }
+            } catch (NoClassDefFoundError | Exception ignored) {
+                // Some classes may fail to load due to missing optional 
dependencies
+            }
+        }
+        Collections.sort(result);
+        log.info("Scanned {}: {} classes", interfaceType.getSimpleName(), 
result.size());
+        return result;
+    }
+
     private static void writeManifest(Path path, List<String> lines) throws 
IOException {
         Files.write(path, lines, StandardCharsets.UTF_8);
     }
diff --git 
a/build-tools/oal-exporter/src/test/java/org/apache/skywalking/oap/server/buildtools/oal/OALClassExporterTest.java
 
b/build-tools/oal-exporter/src/test/java/org/apache/skywalking/oap/server/buildtools/oal/OALClassExporterTest.java
index 27a1866..8384d51 100644
--- 
a/build-tools/oal-exporter/src/test/java/org/apache/skywalking/oap/server/buildtools/oal/OALClassExporterTest.java
+++ 
b/build-tools/oal-exporter/src/test/java/org/apache/skywalking/oap/server/buildtools/oal/OALClassExporterTest.java
@@ -126,5 +126,31 @@ class OALClassExporterTest {
         // Verify disabled sources manifest exists (may be empty when all 
disable() calls are commented out)
         Path disabledManifest = 
tempDir.resolve("META-INF/oal-disabled-sources.txt");
         assertTrue(Files.exists(disabledManifest), "disabled sources manifest 
should exist");
+
+        // Verify annotation scan manifests
+        Path annotationScanDir = tempDir.resolve("META-INF/annotation-scan");
+        assertTrue(Files.isDirectory(annotationScanDir), "annotation-scan 
directory should exist");
+
+        assertManifestNotEmpty(annotationScanDir, "ScopeDeclaration.txt");
+        assertManifestNotEmpty(annotationScanDir, "Stream.txt");
+        assertManifestNotEmpty(annotationScanDir, "SourceDispatcher.txt");
+        assertManifestNotEmpty(annotationScanDir, "ISourceDecorator.txt");
+
+        // Disable/MultipleDisable manifests exist but may be empty (no 
classes use these annotations currently)
+        assertTrue(
+            Files.exists(annotationScanDir.resolve("Disable.txt")),
+            "Disable manifest should exist"
+        );
+        assertTrue(
+            Files.exists(annotationScanDir.resolve("MultipleDisable.txt")),
+            "MultipleDisable manifest should exist"
+        );
+    }
+
+    private void assertManifestNotEmpty(Path dir, String fileName) throws 
Exception {
+        Path manifest = dir.resolve(fileName);
+        assertTrue(Files.exists(manifest), fileName + " should exist");
+        List<String> lines = Files.readAllLines(manifest);
+        assertFalse(lines.isEmpty(), fileName + " should not be empty");
     }
 }
diff --git a/oap-graalvm-server/pom.xml b/oap-graalvm-server/pom.xml
index dfc7881..37dec83 100644
--- a/oap-graalvm-server/pom.xml
+++ b/oap-graalvm-server/pom.xml
@@ -241,10 +241,25 @@
             <artifactId>server-starter</artifactId>
         </dependency>
 
+        <!-- Pre-compiled OAL classes + annotation manifests from build-time 
export -->
+        <dependency>
+            <groupId>org.apache.skywalking</groupId>
+            <artifactId>oal-exporter</artifactId>
+            <version>${project.version}</version>
+            <classifier>generated</classifier>
+        </dependency>
+
         <!-- Lombok -->
         <dependency>
             <groupId>org.projectlombok</groupId>
             <artifactId>lombok</artifactId>
         </dependency>
+
+        <!-- Test -->
+        <dependency>
+            <groupId>org.junit.jupiter</groupId>
+            <artifactId>junit-jupiter</artifactId>
+            <scope>test</scope>
+        </dependency>
     </dependencies>
 </project>
\ No newline at end of file
diff --git 
a/oap-graalvm-server/src/main/java/org/apache/skywalking/oap/server/core/annotation/AnnotationScan.java
 
b/oap-graalvm-server/src/main/java/org/apache/skywalking/oap/server/core/annotation/AnnotationScan.java
new file mode 100644
index 0000000..9ec96fa
--- /dev/null
+++ 
b/oap-graalvm-server/src/main/java/org/apache/skywalking/oap/server/core/annotation/AnnotationScan.java
@@ -0,0 +1,125 @@
+/*
+ * 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.annotation;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.lang.annotation.Annotation;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.LinkedList;
+import java.util.List;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.skywalking.oap.server.core.storage.StorageException;
+
+/**
+ * GraalVM replacement for upstream AnnotationScan.
+ * Same FQCN — shadows the upstream class via Maven classpath precedence.
+ *
+ * Instead of Guava ClassPath scanning, reads pre-built manifest files
+ * produced by OALClassExporter at build time.
+ * Manifest path: META-INF/annotation-scan/{AnnotationSimpleName}.txt
+ */
+@Slf4j
+public class AnnotationScan {
+
+    private static final String MANIFEST_PREFIX = "META-INF/annotation-scan/";
+
+    private final List<AnnotationListenerCache> listeners;
+
+    public AnnotationScan() {
+        this.listeners = new LinkedList<>();
+    }
+
+    public void registerListener(AnnotationListener listener) {
+        listeners.add(new AnnotationListenerCache(listener));
+    }
+
+    public void scan() throws IOException, StorageException {
+        for (AnnotationListenerCache listener : listeners) {
+            String annotationName = listener.annotation().getSimpleName();
+            String manifestPath = MANIFEST_PREFIX + annotationName + ".txt";
+
+            List<String> classNames = readManifest(manifestPath);
+            for (String className : classNames) {
+                try {
+                    Class<?> aClass = Class.forName(className);
+                    if (aClass.isAnnotationPresent(listener.annotation())) {
+                        listener.addMatch(aClass);
+                    }
+                } catch (ClassNotFoundException e) {
+                    log.warn("Class not found from manifest {}: {}", 
manifestPath, className);
+                }
+            }
+        }
+
+        for (AnnotationListenerCache listener : listeners) {
+            listener.complete();
+        }
+    }
+
+    private static List<String> readManifest(String resourcePath) throws 
IOException {
+        List<String> lines = new ArrayList<>();
+        ClassLoader cl = AnnotationScan.class.getClassLoader();
+        try (InputStream is = cl.getResourceAsStream(resourcePath)) {
+            if (is == null) {
+                log.warn("Annotation manifest not found: {}", resourcePath);
+                return lines;
+            }
+            try (BufferedReader reader = new BufferedReader(
+                new InputStreamReader(is, StandardCharsets.UTF_8))) {
+                String line;
+                while ((line = reader.readLine()) != null) {
+                    line = line.trim();
+                    if (!line.isEmpty()) {
+                        lines.add(line);
+                    }
+                }
+            }
+        }
+        return lines;
+    }
+
+    private static class AnnotationListenerCache {
+        private final AnnotationListener listener;
+        private final List<Class<?>> matchedClass;
+
+        private AnnotationListenerCache(AnnotationListener listener) {
+            this.listener = listener;
+            this.matchedClass = new LinkedList<>();
+        }
+
+        private Class<? extends Annotation> annotation() {
+            return this.listener.annotation();
+        }
+
+        private void addMatch(Class<?> aClass) {
+            matchedClass.add(aClass);
+        }
+
+        private void complete() throws StorageException {
+            matchedClass.sort(Comparator.comparing(Class::getName));
+            for (Class<?> aClass : matchedClass) {
+                listener.notify(aClass);
+            }
+        }
+    }
+}
diff --git 
a/oap-graalvm-server/src/main/java/org/apache/skywalking/oap/server/core/oal/rt/OALEngineLoaderService.java
 
b/oap-graalvm-server/src/main/java/org/apache/skywalking/oap/server/core/oal/rt/OALEngineLoaderService.java
new file mode 100644
index 0000000..fa40c7b
--- /dev/null
+++ 
b/oap-graalvm-server/src/main/java/org/apache/skywalking/oap/server/core/oal/rt/OALEngineLoaderService.java
@@ -0,0 +1,129 @@
+/*
+ * 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.oal.rt;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.skywalking.oap.server.core.CoreModule;
+import org.apache.skywalking.oap.server.core.analysis.DisableRegister;
+import org.apache.skywalking.oap.server.core.analysis.StreamAnnotationListener;
+import org.apache.skywalking.oap.server.core.source.SourceReceiver;
+import org.apache.skywalking.oap.server.library.module.ModuleManager;
+import org.apache.skywalking.oap.server.library.module.ModuleStartException;
+import org.apache.skywalking.oap.server.library.module.Service;
+
+/**
+ * GraalVM replacement for upstream OALEngineLoaderService.
+ * Same FQCN — shadows the upstream class via Maven classpath precedence.
+ *
+ * Instead of running the OAL engine (Javassist code generation) at runtime,
+ * loads pre-compiled classes from build-time manifests produced by 
OALClassExporter.
+ */
+@Slf4j
+public class OALEngineLoaderService implements Service {
+
+    private static final String METRICS_MANIFEST = 
"META-INF/oal-metrics-classes.txt";
+    private static final String DISPATCHER_MANIFEST = 
"META-INF/oal-dispatcher-classes.txt";
+    private static final String DISABLED_MANIFEST = 
"META-INF/oal-disabled-sources.txt";
+
+    private final Set<OALDefine> oalDefineSet = new HashSet<>();
+    private final ModuleManager moduleManager;
+    private boolean loaded;
+
+    public OALEngineLoaderService(ModuleManager moduleManager) {
+        this.moduleManager = moduleManager;
+    }
+
+    public void load(OALDefine define) throws ModuleStartException {
+        if (oalDefineSet.contains(define)) {
+            return;
+        }
+
+        if (!loaded) {
+            loadAllPrecompiledClasses();
+            loaded = true;
+        }
+
+        oalDefineSet.add(define);
+    }
+
+    private void loadAllPrecompiledClasses() throws ModuleStartException {
+        try {
+            // 1. Register disabled sources before processing metrics
+            List<String> disabledSources = readManifest(DISABLED_MANIFEST);
+            for (String name : disabledSources) {
+                DisableRegister.INSTANCE.add(name);
+            }
+            log.info("Loaded {} disabled sources from manifest", 
disabledSources.size());
+
+            // 2. Load and register pre-compiled metrics classes
+            StreamAnnotationListener streamListener = new 
StreamAnnotationListener(moduleManager);
+            List<String> metricsClassNames = readManifest(METRICS_MANIFEST);
+            for (String className : metricsClassNames) {
+                Class<?> metricsClass = Class.forName(className);
+                streamListener.notify(metricsClass);
+            }
+            log.info("Registered {} pre-compiled OAL metrics classes", 
metricsClassNames.size());
+
+            // 3. Load and register pre-compiled dispatcher classes
+            var dispatcherListener = moduleManager.find(CoreModule.NAME)
+                                                  .provider()
+                                                  
.getService(SourceReceiver.class)
+                                                  
.getDispatcherDetectorListener();
+            List<String> dispatcherClassNames = 
readManifest(DISPATCHER_MANIFEST);
+            for (String className : dispatcherClassNames) {
+                Class<?> dispatcherClass = Class.forName(className);
+                dispatcherListener.addIfAsSourceDispatcher(dispatcherClass);
+            }
+            log.info("Registered {} pre-compiled OAL dispatcher classes", 
dispatcherClassNames.size());
+
+        } catch (Exception e) {
+            throw new ModuleStartException("Failed to load pre-compiled OAL 
classes", e);
+        }
+    }
+
+    private static List<String> readManifest(String resourcePath) throws 
IOException {
+        List<String> lines = new ArrayList<>();
+        ClassLoader cl = OALEngineLoaderService.class.getClassLoader();
+        try (InputStream is = cl.getResourceAsStream(resourcePath)) {
+            if (is == null) {
+                log.warn("Manifest not found: {}", resourcePath);
+                return lines;
+            }
+            try (BufferedReader reader = new BufferedReader(
+                new InputStreamReader(is, StandardCharsets.UTF_8))) {
+                String line;
+                while ((line = reader.readLine()) != null) {
+                    line = line.trim();
+                    if (!line.isEmpty()) {
+                        lines.add(line);
+                    }
+                }
+            }
+        }
+        return lines;
+    }
+}
diff --git 
a/oap-graalvm-server/src/main/java/org/apache/skywalking/oap/server/core/source/SourceReceiverImpl.java
 
b/oap-graalvm-server/src/main/java/org/apache/skywalking/oap/server/core/source/SourceReceiverImpl.java
new file mode 100644
index 0000000..08c7b02
--- /dev/null
+++ 
b/oap-graalvm-server/src/main/java/org/apache/skywalking/oap/server/core/source/SourceReceiverImpl.java
@@ -0,0 +1,112 @@
+/*
+ * 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.source;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.List;
+import lombok.Getter;
+import lombok.extern.slf4j.Slf4j;
+import 
org.apache.skywalking.oap.server.core.analysis.DispatcherDetectorListener;
+import org.apache.skywalking.oap.server.core.analysis.DispatcherManager;
+import org.apache.skywalking.oap.server.core.analysis.SourceDecoratorManager;
+
+/**
+ * GraalVM replacement for upstream SourceReceiverImpl.
+ * Same FQCN — shadows the upstream class via Maven classpath precedence.
+ *
+ * Instead of Guava ClassPath scanning, reads pre-built manifest files
+ * produced by OALClassExporter at build time.
+ */
+@Slf4j
+public class SourceReceiverImpl implements SourceReceiver {
+
+    private static final String DISPATCHER_MANIFEST = 
"META-INF/annotation-scan/SourceDispatcher.txt";
+    private static final String DECORATOR_MANIFEST = 
"META-INF/annotation-scan/ISourceDecorator.txt";
+
+    @Getter
+    private final DispatcherManager dispatcherManager;
+
+    @Getter
+    private final SourceDecoratorManager sourceDecoratorManager;
+
+    public SourceReceiverImpl() {
+        this.dispatcherManager = new DispatcherManager();
+        this.sourceDecoratorManager = new SourceDecoratorManager();
+    }
+
+    @Override
+    public void receive(ISource source) {
+        dispatcherManager.forward(source);
+    }
+
+    @Override
+    public DispatcherDetectorListener getDispatcherDetectorListener() {
+        return getDispatcherManager();
+    }
+
+    public void scan() throws IOException, InstantiationException, 
IllegalAccessException {
+        List<String> dispatcherNames = readManifest(DISPATCHER_MANIFEST);
+        for (String className : dispatcherNames) {
+            try {
+                Class<?> aClass = Class.forName(className);
+                dispatcherManager.addIfAsSourceDispatcher(aClass);
+            } catch (ClassNotFoundException e) {
+                log.warn("Dispatcher class not found: {}", className);
+            }
+        }
+        log.info("Registered {} source dispatchers from manifest", 
dispatcherNames.size());
+
+        List<String> decoratorNames = readManifest(DECORATOR_MANIFEST);
+        for (String className : decoratorNames) {
+            try {
+                Class<?> aClass = Class.forName(className);
+                sourceDecoratorManager.addIfAsSourceDecorator(aClass);
+            } catch (ClassNotFoundException e) {
+                log.warn("Decorator class not found: {}", className);
+            }
+        }
+        log.info("Registered {} source decorators from manifest", 
decoratorNames.size());
+    }
+
+    private static List<String> readManifest(String resourcePath) throws 
IOException {
+        List<String> lines = new ArrayList<>();
+        ClassLoader cl = SourceReceiverImpl.class.getClassLoader();
+        try (InputStream is = cl.getResourceAsStream(resourcePath)) {
+            if (is == null) {
+                log.warn("Manifest not found: {}", resourcePath);
+                return lines;
+            }
+            try (BufferedReader reader = new BufferedReader(
+                new InputStreamReader(is, StandardCharsets.UTF_8))) {
+                String line;
+                while ((line = reader.readLine()) != null) {
+                    line = line.trim();
+                    if (!line.isEmpty()) {
+                        lines.add(line);
+                    }
+                }
+            }
+        }
+        return lines;
+    }
+}
diff --git 
a/oap-graalvm-server/src/test/java/org/apache/skywalking/oap/server/graalvm/PrecompiledRegistrationTest.java
 
b/oap-graalvm-server/src/test/java/org/apache/skywalking/oap/server/graalvm/PrecompiledRegistrationTest.java
new file mode 100644
index 0000000..eafa1c0
--- /dev/null
+++ 
b/oap-graalvm-server/src/test/java/org/apache/skywalking/oap/server/graalvm/PrecompiledRegistrationTest.java
@@ -0,0 +1,392 @@
+/*
+ * 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.graalvm;
+
+import com.google.common.collect.ImmutableSet;
+import com.google.common.reflect.ClassPath;
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.lang.reflect.Modifier;
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeSet;
+import org.apache.skywalking.oap.server.core.analysis.ISourceDecorator;
+import org.apache.skywalking.oap.server.core.analysis.SourceDispatcher;
+import org.apache.skywalking.oap.server.core.analysis.Stream;
+import org.apache.skywalking.oap.server.core.annotation.AnnotationScan;
+import org.apache.skywalking.oap.server.core.source.DefaultScopeDefine;
+import org.apache.skywalking.oap.server.core.source.ISource;
+import org.apache.skywalking.oap.server.core.source.ScopeDeclaration;
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.BeforeAll;
+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.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.junit.jupiter.api.Assertions.fail;
+
+/**
+ * Verifies that pre-compiled manifests produced by OALClassExporter are 
complete
+ * and consistent. Checks:
+ * 1. Manifest contents match live Guava ClassPath scan (for hardcoded classes)
+ * 2. All OAL-generated classes are loadable and properly annotated
+ * 3. DefaultScopeDefine initializes correctly from manifests
+ * 4. Source -> Dispatcher -> Metrics chain is consistent
+ */
+class PrecompiledRegistrationTest {
+
+    private static final String OAL_RT_PACKAGE =
+        "org.apache.skywalking.oap.server.core.source.oal.rt.";
+
+    @BeforeAll
+    static void initScopeDefine() throws Exception {
+        DefaultScopeDefine.reset();
+        AnnotationScan scopeScan = new AnnotationScan();
+        scopeScan.registerListener(new DefaultScopeDefine.Listener());
+        scopeScan.scan();
+    }
+
+    @AfterAll
+    static void cleanUp() {
+        DefaultScopeDefine.reset();
+    }
+
+    // 
=========================================================================
+    // 1. Manifest vs Guava ClassPath comparison (hardcoded classes only)
+    // 
=========================================================================
+
+    @Test
+    void scopeDeclarationManifestMatchesClasspath() throws Exception {
+        Set<String> manifest = 
readManifestAsSet("META-INF/annotation-scan/ScopeDeclaration.txt");
+        Set<String> scanned = guavaScanAnnotation(ScopeDeclaration.class);
+        assertFalse(manifest.isEmpty(), "ScopeDeclaration manifest is empty");
+        assertEquals(scanned, manifest,
+            "ScopeDeclaration manifest does not match classpath scan");
+    }
+
+    @Test
+    void hardcodedStreamManifestMatchesClasspath() throws Exception {
+        Set<String> manifest = 
readManifestAsSet("META-INF/annotation-scan/Stream.txt");
+        Set<String> scanned = guavaScanAnnotation(Stream.class);
+        // Filter out OAL-generated classes from Guava scan
+        scanned.removeIf(name -> name.startsWith(OAL_RT_PACKAGE));
+        assertFalse(manifest.isEmpty(), "Stream manifest is empty");
+        assertEquals(scanned, manifest,
+            "Hardcoded @Stream manifest does not match classpath scan");
+    }
+
+    @Test
+    void sourceDispatcherManifestMatchesClasspath() throws Exception {
+        Set<String> manifest = 
readManifestAsSet("META-INF/annotation-scan/SourceDispatcher.txt");
+        Set<String> scanned = guavaScanInterface(SourceDispatcher.class);
+        // Filter out OAL-generated dispatchers from Guava scan
+        scanned.removeIf(name -> name.startsWith(OAL_RT_PACKAGE));
+        assertFalse(manifest.isEmpty(), "SourceDispatcher manifest is empty");
+        assertEquals(scanned, manifest,
+            "SourceDispatcher manifest does not match classpath scan");
+    }
+
+    @Test
+    void sourceDecoratorManifestMatchesClasspath() throws Exception {
+        Set<String> manifest = 
readManifestAsSet("META-INF/annotation-scan/ISourceDecorator.txt");
+        Set<String> scanned = guavaScanInterface(ISourceDecorator.class);
+        assertFalse(manifest.isEmpty(), "ISourceDecorator manifest is empty");
+        assertEquals(scanned, manifest,
+            "ISourceDecorator manifest does not match classpath scan");
+    }
+
+    // 
=========================================================================
+    // 2. OAL-generated classes: loadable and properly annotated
+    // 
=========================================================================
+
+    @Test
+    void allOalMetricsClassesLoadAndHaveStreamAnnotation() throws Exception {
+        List<String> classes = 
readManifest("META-INF/oal-metrics-classes.txt");
+        assertFalse(classes.isEmpty(), "OAL metrics manifest is empty");
+        for (String className : classes) {
+            Class<?> clazz = Class.forName(className);
+            assertTrue(clazz.isAnnotationPresent(Stream.class),
+                "OAL metrics class missing @Stream: " + className);
+        }
+    }
+
+    @Test
+    void allOalDispatcherClassesLoadAndImplementInterface() throws Exception {
+        List<String> classes = 
readManifest("META-INF/oal-dispatcher-classes.txt");
+        assertFalse(classes.isEmpty(), "OAL dispatcher manifest is empty");
+        for (String className : classes) {
+            Class<?> clazz = Class.forName(className);
+            assertTrue(SourceDispatcher.class.isAssignableFrom(clazz),
+                "OAL dispatcher doesn't implement SourceDispatcher: " + 
className);
+        }
+    }
+
+    // 
=========================================================================
+    // 3. Scope registration verification
+    // 
=========================================================================
+
+    @Test
+    void allDeclaredScopesRegisteredInDefaultScopeDefine() throws Exception {
+        List<String> classes = 
readManifest("META-INF/annotation-scan/ScopeDeclaration.txt");
+        for (String className : classes) {
+            Class<?> clazz = Class.forName(className);
+            ScopeDeclaration decl = 
clazz.getAnnotation(ScopeDeclaration.class);
+            assertNotNull(decl, "Missing @ScopeDeclaration on " + className);
+
+            String registeredName = DefaultScopeDefine.nameOf(decl.id());
+            assertEquals(decl.name(), registeredName,
+                "Scope name mismatch for id=" + decl.id() + " on " + 
className);
+        }
+    }
+
+    @Test
+    void wellKnownScopesAreRegistered() {
+        // Verify essential scopes exist after manifest-based initialization
+        assertNotNull(DefaultScopeDefine.nameOf(DefaultScopeDefine.SERVICE));
+        
assertNotNull(DefaultScopeDefine.nameOf(DefaultScopeDefine.SERVICE_INSTANCE));
+        assertNotNull(DefaultScopeDefine.nameOf(DefaultScopeDefine.ENDPOINT));
+    }
+
+    // 
=========================================================================
+    // 4. Source -> Dispatcher -> Metrics chain
+    // 
=========================================================================
+
+    @Test
+    void allStreamAnnotationsReferenceValidScopes() throws Exception {
+        // Check hardcoded @Stream classes
+        List<String> hardcoded = 
readManifest("META-INF/annotation-scan/Stream.txt");
+        for (String className : hardcoded) {
+            Class<?> clazz = Class.forName(className);
+            Stream stream = clazz.getAnnotation(Stream.class);
+            assertNotNull(stream, "Missing @Stream on " + className);
+            assertScopeExists(stream.scopeId(), className);
+        }
+
+        // Check OAL-generated @Stream classes
+        List<String> oalMetrics = 
readManifest("META-INF/oal-metrics-classes.txt");
+        for (String className : oalMetrics) {
+            Class<?> clazz = Class.forName(className);
+            Stream stream = clazz.getAnnotation(Stream.class);
+            assertNotNull(stream, "Missing @Stream on OAL class " + className);
+            assertScopeExists(stream.scopeId(), className);
+        }
+    }
+
+    @Test
+    @SuppressWarnings("deprecation")
+    void hardcodedDispatchersHaveDeclaredSourceScopes() throws Exception {
+        List<String> dispatchers = 
readManifest("META-INF/annotation-scan/SourceDispatcher.txt");
+        for (String className : dispatchers) {
+            Class<?> clazz = Class.forName(className);
+            Class<?> sourceType = extractSourceType(clazz);
+            if (sourceType == null) {
+                fail("Cannot extract source type from " + className);
+            }
+            ISource source = (ISource) sourceType.newInstance();
+            int scopeId = source.scope();
+            assertScopeExists(scopeId,
+                className + " (source=" + sourceType.getSimpleName() + ")");
+        }
+    }
+
+    @Test
+    @SuppressWarnings("deprecation")
+    void oalDispatchersHaveDeclaredSourceScopes() throws Exception {
+        List<String> dispatchers = 
readManifest("META-INF/oal-dispatcher-classes.txt");
+        for (String className : dispatchers) {
+            Class<?> clazz = Class.forName(className);
+            Class<?> sourceType = extractSourceType(clazz);
+            if (sourceType == null) {
+                fail("Cannot extract source type from " + className);
+            }
+            ISource source = (ISource) sourceType.newInstance();
+            int scopeId = source.scope();
+            assertScopeExists(scopeId,
+                className + " (source=" + sourceType.getSimpleName() + ")");
+        }
+    }
+
+    @Test
+    void knownScopesHaveDispatchersAndStreamMetrics() throws Exception {
+        // Build scope -> dispatchers map from both hardcoded and OAL manifests
+        Map<Integer, List<String>> scopeDispatchers = 
buildScopeDispatcherMap();
+
+        // Build scope -> @Stream metrics map from both hardcoded and OAL 
manifests
+        Map<Integer, List<String>> scopeStreams = buildScopeStreamMap();
+
+        // Verify key scopes have both dispatchers and stream metrics
+        int[] keyScopeIds = {
+            DefaultScopeDefine.SERVICE,
+            DefaultScopeDefine.ENDPOINT,
+            DefaultScopeDefine.SERVICE_INSTANCE,
+            DefaultScopeDefine.SERVICE_RELATION,
+        };
+
+        for (int scopeId : keyScopeIds) {
+            String scopeName = DefaultScopeDefine.nameOf(scopeId);
+
+            assertTrue(
+                scopeDispatchers.containsKey(scopeId)
+                    && !scopeDispatchers.get(scopeId).isEmpty(),
+                "Scope " + scopeName + " (id=" + scopeId + ") has no 
dispatchers"
+            );
+
+            assertTrue(
+                scopeStreams.containsKey(scopeId)
+                    && !scopeStreams.get(scopeId).isEmpty(),
+                "Scope " + scopeName + " (id=" + scopeId + ") has no @Stream 
metrics"
+            );
+        }
+    }
+
+    // 
=========================================================================
+    // Helpers
+    // 
=========================================================================
+
+    private static void assertScopeExists(int scopeId, String context) {
+        try {
+            DefaultScopeDefine.nameOf(scopeId);
+        } catch (Exception e) {
+            fail("Invalid scope id=" + scopeId + " referenced by " + context);
+        }
+    }
+
+    @SuppressWarnings("deprecation")
+    private Map<Integer, List<String>> buildScopeDispatcherMap() throws 
Exception {
+        Map<Integer, List<String>> map = new HashMap<>();
+
+        List<String> allDispatchers = new ArrayList<>();
+        
allDispatchers.addAll(readManifest("META-INF/annotation-scan/SourceDispatcher.txt"));
+        
allDispatchers.addAll(readManifest("META-INF/oal-dispatcher-classes.txt"));
+
+        for (String className : allDispatchers) {
+            Class<?> clazz = Class.forName(className);
+            Class<?> sourceType = extractSourceType(clazz);
+            if (sourceType != null) {
+                ISource source = (ISource) sourceType.newInstance();
+                map.computeIfAbsent(source.scope(), k -> new 
ArrayList<>()).add(className);
+            }
+        }
+        return map;
+    }
+
+    private Map<Integer, List<String>> buildScopeStreamMap() throws Exception {
+        Map<Integer, List<String>> map = new HashMap<>();
+
+        List<String> allStreams = new ArrayList<>();
+        allStreams.addAll(readManifest("META-INF/annotation-scan/Stream.txt"));
+        allStreams.addAll(readManifest("META-INF/oal-metrics-classes.txt"));
+
+        for (String className : allStreams) {
+            Class<?> clazz = Class.forName(className);
+            Stream stream = clazz.getAnnotation(Stream.class);
+            if (stream != null) {
+                map.computeIfAbsent(stream.scopeId(), k -> new 
ArrayList<>()).add(className);
+            }
+        }
+        return map;
+    }
+
+    private static Class<?> extractSourceType(Class<?> dispatcherClass) {
+        for (Type genericInterface : dispatcherClass.getGenericInterfaces()) {
+            if (genericInterface instanceof ParameterizedType) {
+                ParameterizedType parameterized = (ParameterizedType) 
genericInterface;
+                if (parameterized.getRawType().getTypeName().equals(
+                    SourceDispatcher.class.getName())) {
+                    Type[] args = parameterized.getActualTypeArguments();
+                    if (args.length == 1 && args[0] instanceof Class<?>) {
+                        return (Class<?>) args[0];
+                    }
+                }
+            }
+        }
+        return null;
+    }
+
+    private static Set<String> guavaScanAnnotation(
+        Class<? extends java.lang.annotation.Annotation> annotationType) 
throws IOException {
+        ClassPath classpath = ClassPath.from(
+            PrecompiledRegistrationTest.class.getClassLoader());
+        ImmutableSet<ClassPath.ClassInfo> classes =
+            classpath.getTopLevelClassesRecursive("org.apache.skywalking");
+        Set<String> result = new TreeSet<>();
+        for (ClassPath.ClassInfo info : classes) {
+            try {
+                Class<?> clazz = info.load();
+                if (clazz.isAnnotationPresent(annotationType)) {
+                    result.add(clazz.getName());
+                }
+            } catch (NoClassDefFoundError | Exception ignored) {
+            }
+        }
+        return result;
+    }
+
+    private static Set<String> guavaScanInterface(Class<?> interfaceType) 
throws IOException {
+        ClassPath classpath = ClassPath.from(
+            PrecompiledRegistrationTest.class.getClassLoader());
+        ImmutableSet<ClassPath.ClassInfo> classes =
+            classpath.getTopLevelClassesRecursive("org.apache.skywalking");
+        Set<String> result = new TreeSet<>();
+        for (ClassPath.ClassInfo info : classes) {
+            try {
+                Class<?> clazz = info.load();
+                if (!clazz.isInterface()
+                    && !Modifier.isAbstract(clazz.getModifiers())
+                    && interfaceType.isAssignableFrom(clazz)) {
+                    result.add(clazz.getName());
+                }
+            } catch (NoClassDefFoundError | Exception ignored) {
+            }
+        }
+        return result;
+    }
+
+    private static Set<String> readManifestAsSet(String resourcePath) throws 
IOException {
+        return new TreeSet<>(readManifest(resourcePath));
+    }
+
+    private static List<String> readManifest(String resourcePath) throws 
IOException {
+        List<String> lines = new ArrayList<>();
+        ClassLoader cl = PrecompiledRegistrationTest.class.getClassLoader();
+        try (InputStream is = cl.getResourceAsStream(resourcePath)) {
+            assertNotNull(is, "Manifest not found: " + resourcePath);
+            try (BufferedReader reader = new BufferedReader(
+                new InputStreamReader(is, StandardCharsets.UTF_8))) {
+                String line;
+                while ((line = reader.readLine()) != null) {
+                    line = line.trim();
+                    if (!line.isEmpty()) {
+                        lines.add(line);
+                    }
+                }
+            }
+        }
+        return lines;
+    }
+}


Reply via email to