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
The following commit(s) were added to refs/heads/main by this push:
new 7ae97f5 Bump upstream, enable gRPC TLS in native image, fix MAL
closure wiring (#22)
7ae97f5 is described below
commit 7ae97f59e43ada1e92df218dd3e138083dcb85ca
Author: 吴晟 Wu Sheng <[email protected]>
AuthorDate: Thu Mar 19 22:42:02 2026 +0800
Bump upstream, enable gRPC TLS in native image, fix MAL closure wiring (#22)
feat(graalvm): enable gRPC TLS with JDK SSL and align with SkyWalking MAL v2
- Bump `skywalking` submodule to `726ebcc321`
- Includes upstream PR #13750 (MAL v2 companion class closures)
- Syncs related UI updates
- Add `library-server-for-graalvm` module
- Provides same-FQCN `DynamicSslContext`
- Uses `SslProvider.JDK` instead of `SslProvider.OPENSSL`
- Enables gRPC TLS in native images without `netty_tcnative`
- Add SSL e2e test (`ssl`)
- Verifies gRPC TLS works with JDK SSL provider
- Reuses upstream certs and `simple-cases.yaml`
- Remove obsolete closure wiring
- Drop `wireClosures()`, `CLOSURE_TYPES`, `ClosureInfo`
- Clean up `DSL.java`, `MALScriptComparisonBase`,
`PrecompiledMALExecutionTest`
- Upstream now uses companion classes with self-wiring `static {}` blocks
- Fix so11y e2e test
- Re-enable `meter_oap_instance_metrics_aggregation`
- Use label-filtered query: `{level='L1 aggregation'}`
- Update SHA-256 tracking
- Add `DynamicSslContext.java` to staleness detector
- Update docs
- Refresh CLAUDE.md for companion class pattern and TLS replacement
---
.github/workflows/ci.yml | 2 +
changes/changes.md | 9 ++
oap-graalvm-server/pom.xml | 10 ++
.../src/main/assembly/distribution.xml | 1 +
oap-graalvm-server/src/test/CLAUDE.md | 7 +-
.../graalvm/PrecompiledMALExecutionTest.java | 65 ----------
.../graalvm/mal/MALScriptComparisonBase.java | 76 ------------
.../resources/replacement-source-sha256.properties | 4 +
oap-libs-for-graalvm/CLAUDE.md | 8 +-
.../library-server-for-graalvm/pom.xml | 64 ++++++++++
.../library/server/grpc/ssl/DynamicSslContext.java | 90 ++++++++++++++
.../skywalking/oap/meter/analyzer/v2/dsl/DSL.java | 94 +-------------
oap-libs-for-graalvm/pom.xml | 1 +
pom.xml | 10 ++
skywalking | 2 +-
test/e2e/cases/so11y/so11y-cases.yaml | 3 +-
test/e2e/cases/ssl/docker-compose.yml | 136 +++++++++++++++++++++
test/e2e/cases/ssl/e2e.yaml | 48 ++++++++
18 files changed, 394 insertions(+), 236 deletions(-)
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index c0bbfd7..1d93dc6 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -337,6 +337,8 @@ jobs:
case: virtual-mq
- name: Kafka Exporter
case: exporter
+ - name: SSL
+ case: ssl
- name: Self Observability
case: so11y
- name: MQE
diff --git a/changes/changes.md b/changes/changes.md
index 4ef2be3..c2d7a5e 100644
--- a/changes/changes.md
+++ b/changes/changes.md
@@ -2,12 +2,21 @@
## 0.3.0
+### Upstream Sync
+
+- Sync SkyWalking submodule to upstream commit `726ebcc321` (MAL v2 companion
class closures).
+
+### GraalVM Native Image Compatibility
+
+- Add `library-server-for-graalvm`: replace `DynamicSslContext` to use
`SslProvider.JDK` instead of `SslProvider.OPENSSL`, enabling gRPC TLS in native
images without `netty_tcnative`.
+
### Documentation
- Document TLS/SSL limitation: native image lacks `netty_tcnative`, recommend
service mesh for mTLS.
### E2E Tests
+- Add SSL e2e test case (gRPC TLS with JDK SSL provider in native image).
- Add Auth e2e test case (token-based agent-to-OAP authentication).
- Add OTLP Traces e2e test case (OpenTelemetry trace ingestion via Zipkin API).
- Add Virtual MQ e2e test case (Kafka-instrumented virtual MQ layer metrics).
diff --git a/oap-graalvm-server/pom.xml b/oap-graalvm-server/pom.xml
index 03ded8c..73c7552 100644
--- a/oap-graalvm-server/pom.xml
+++ b/oap-graalvm-server/pom.xml
@@ -45,6 +45,12 @@
<version>${skywalking.version}</version>
<scope>provided</scope>
</dependency>
+ <dependency>
+ <groupId>org.apache.skywalking</groupId>
+ <artifactId>library-server</artifactId>
+ <version>${skywalking.version}</version>
+ <scope>provided</scope>
+ </dependency>
<dependency>
<groupId>org.apache.skywalking</groupId>
<artifactId>server-core</artifactId>
@@ -126,6 +132,10 @@
<groupId>org.apache.skywalking</groupId>
<artifactId>library-module-for-graalvm</artifactId>
</dependency>
+ <dependency>
+ <groupId>org.apache.skywalking</groupId>
+ <artifactId>library-server-for-graalvm</artifactId>
+ </dependency>
<dependency>
<groupId>org.apache.skywalking</groupId>
<artifactId>server-core-for-graalvm</artifactId>
diff --git a/oap-graalvm-server/src/main/assembly/distribution.xml
b/oap-graalvm-server/src/main/assembly/distribution.xml
index 8f64d58..7c7995b 100644
--- a/oap-graalvm-server/src/main/assembly/distribution.xml
+++ b/oap-graalvm-server/src/main/assembly/distribution.xml
@@ -127,6 +127,7 @@
<excludes>
<exclude>org.apache.skywalking:server-core</exclude>
<exclude>org.apache.skywalking:server-starter</exclude>
+ <exclude>org.apache.skywalking:library-server</exclude>
<exclude>org.apache.skywalking:library-util</exclude>
<exclude>org.apache.skywalking:meter-analyzer</exclude>
<exclude>org.apache.skywalking:log-analyzer</exclude>
diff --git a/oap-graalvm-server/src/test/CLAUDE.md
b/oap-graalvm-server/src/test/CLAUDE.md
index 24d488a..ab8526b 100644
--- a/oap-graalvm-server/src/test/CLAUDE.md
+++ b/oap-graalvm-server/src/test/CLAUDE.md
@@ -34,9 +34,10 @@ Each metric expression is run through two independent paths:
**Path B (Pre-compiled)**: Loads the `.class` file from per-file configs under
`META-INF/mal-v2/` (mirroring original YAML directory structure) via
-`Class.forName()` using expression text as lookup key, then wires closure
-fields (TagFunction, ForEachFunction, PropertiesExtractor, DecorateFunction)
-via `LambdaMetafactory`.
+`Class.forName()` using expression text as lookup key. Closure fields
+(TagFunction, ForEachFunction, PropertiesExtractor, DecorateFunction) are
+self-wired via companion classes in the `static {}` initializer — no external
+`LambdaMetafactory` wiring needed.
If both paths produce identical `Result` (same success flag, same sample values
within 0.001 tolerance, same labels), then the build-time compilation is
diff --git
a/oap-graalvm-server/src/test/java/org/apache/skywalking/oap/server/graalvm/PrecompiledMALExecutionTest.java
b/oap-graalvm-server/src/test/java/org/apache/skywalking/oap/server/graalvm/PrecompiledMALExecutionTest.java
index 7e5671d..a44aaf2 100644
---
a/oap-graalvm-server/src/test/java/org/apache/skywalking/oap/server/graalvm/PrecompiledMALExecutionTest.java
+++
b/oap-graalvm-server/src/test/java/org/apache/skywalking/oap/server/graalvm/PrecompiledMALExecutionTest.java
@@ -21,12 +21,6 @@ import com.google.common.collect.ImmutableMap;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
-import java.lang.invoke.CallSite;
-import java.lang.invoke.LambdaMetafactory;
-import java.lang.invoke.MethodHandle;
-import java.lang.invoke.MethodHandles;
-import java.lang.invoke.MethodType;
-import java.lang.reflect.Field;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;
@@ -37,7 +31,6 @@ import org.apache.skywalking.oap.meter.analyzer.v2.dsl.Result;
import org.apache.skywalking.oap.meter.analyzer.v2.dsl.Sample;
import org.apache.skywalking.oap.meter.analyzer.v2.dsl.SampleFamily;
import org.apache.skywalking.oap.meter.analyzer.v2.dsl.SampleFamilyBuilder;
-import org.apache.skywalking.oap.meter.analyzer.v2.dsl.SampleFamilyFunctions;
import org.apache.skywalking.oap.server.core.analysis.meter.MeterEntity;
import org.apache.skywalking.oap.server.core.config.NamingControl;
import org.apache.skywalking.oap.server.core.config.group.EndpointNameGrouping;
@@ -190,67 +183,9 @@ class PrecompiledMALExecutionTest {
Class<?> exprClass = Class.forName(className);
MalExpression malExpr = (MalExpression)
exprClass.getDeclaredConstructor().newInstance();
- wireClosures(exprClass, malExpr);
return new Expression(metricName, expression, malExpr);
}
- private static final Map<String, ClosureInfo> CLOSURE_TYPES = new
HashMap<>();
-
- private record ClosureInfo(Class<?> interfaceClass, String samName,
- MethodType samType, MethodType instantiatedType,
- MethodType methodType) {
- }
-
- static {
- CLOSURE_TYPES.put(
- SampleFamilyFunctions.TagFunction.class.getName(),
- new ClosureInfo(SampleFamilyFunctions.TagFunction.class, "apply",
- MethodType.methodType(Object.class, Object.class),
- MethodType.methodType(Map.class, Map.class),
- MethodType.methodType(Map.class, Map.class)));
- CLOSURE_TYPES.put(
- SampleFamilyFunctions.PropertiesExtractor.class.getName(),
- new ClosureInfo(SampleFamilyFunctions.PropertiesExtractor.class,
"apply",
- MethodType.methodType(Object.class, Object.class),
- MethodType.methodType(Map.class, Map.class),
- MethodType.methodType(Map.class, Map.class)));
- CLOSURE_TYPES.put(
- SampleFamilyFunctions.ForEachFunction.class.getName(),
- new ClosureInfo(SampleFamilyFunctions.ForEachFunction.class,
"accept",
- MethodType.methodType(void.class, String.class, Map.class),
- MethodType.methodType(void.class, String.class, Map.class),
- MethodType.methodType(void.class, String.class, Map.class)));
- CLOSURE_TYPES.put(
- SampleFamilyFunctions.DecorateFunction.class.getName(),
- new ClosureInfo(SampleFamilyFunctions.DecorateFunction.class,
"accept",
- MethodType.methodType(void.class, Object.class),
- MethodType.methodType(void.class, Object.class),
- MethodType.methodType(void.class, Object.class)));
- }
-
- private static void wireClosures(final Class<?> clazz, final Object
instance) throws Exception {
- try {
- MethodHandles.Lookup lookup = MethodHandles.privateLookupIn(clazz,
MethodHandles.lookup());
- for (Field field : clazz.getFields()) {
- ClosureInfo info =
CLOSURE_TYPES.get(field.getType().getName());
- if (info == null) {
- continue;
- }
- String methodName = field.getName() + "_" + info.samName;
- MethodHandle mh = lookup.findVirtual(clazz, methodName,
info.methodType);
- CallSite site = LambdaMetafactory.metafactory(
- lookup, info.samName,
- MethodType.methodType(info.interfaceClass, clazz),
- info.samType, mh, info.instantiatedType);
- field.set(instance, site.getTarget().invoke(instance));
- }
- } catch (Exception e) {
- throw e;
- } catch (Throwable t) {
- throw new RuntimeException("Failed to wire closures for " +
clazz.getName(), t);
- }
- }
-
private static Map<String, String> loadExpressionMap() throws Exception {
Map<String, String> map = new HashMap<>();
ClassLoader cl = PrecompiledMALExecutionTest.class.getClassLoader();
diff --git
a/oap-graalvm-server/src/test/java/org/apache/skywalking/oap/server/graalvm/mal/MALScriptComparisonBase.java
b/oap-graalvm-server/src/test/java/org/apache/skywalking/oap/server/graalvm/mal/MALScriptComparisonBase.java
index 924c581..88b189d 100644
---
a/oap-graalvm-server/src/test/java/org/apache/skywalking/oap/server/graalvm/mal/MALScriptComparisonBase.java
+++
b/oap-graalvm-server/src/test/java/org/apache/skywalking/oap/server/graalvm/mal/MALScriptComparisonBase.java
@@ -22,12 +22,6 @@ import com.google.common.collect.ImmutableMap;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
-import java.lang.invoke.CallSite;
-import java.lang.invoke.LambdaMetafactory;
-import java.lang.invoke.MethodHandle;
-import java.lang.invoke.MethodHandles;
-import java.lang.invoke.MethodType;
-import java.lang.reflect.Field;
import java.nio.charset.StandardCharsets;
import java.time.Instant;
import java.util.ArrayList;
@@ -48,7 +42,6 @@ import org.apache.skywalking.oap.meter.analyzer.v2.dsl.Result;
import org.apache.skywalking.oap.meter.analyzer.v2.dsl.Sample;
import org.apache.skywalking.oap.meter.analyzer.v2.dsl.SampleFamily;
import org.apache.skywalking.oap.meter.analyzer.v2.dsl.SampleFamilyBuilder;
-import org.apache.skywalking.oap.meter.analyzer.v2.dsl.SampleFamilyFunctions;
import org.apache.skywalking.oap.meter.analyzer.v2.prometheus.rule.MetricsRule;
import org.apache.skywalking.oap.meter.analyzer.v2.prometheus.rule.Rule;
import org.apache.skywalking.oap.server.core.analysis.meter.MeterEntity;
@@ -288,7 +281,6 @@ abstract class MALScriptComparisonBase {
Class<?> exprClass = Class.forName(className);
MalExpression malExpr =
(MalExpression)
exprClass.getDeclaredConstructor().newInstance();
- wireClosures(exprClass, malExpr);
return new Expression(metricName, expression, malExpr);
} catch (Exception e) {
throw new AssertionError(
@@ -297,74 +289,6 @@ abstract class MALScriptComparisonBase {
}
}
- // ---------------------------------------------------------------
- // Closure wiring for pre-compiled classes
- // ---------------------------------------------------------------
-
- private record ClosureInfo(Class<?> interfaceClass, String samName,
- MethodType samType, MethodType instantiatedType,
- MethodType methodType) {
- }
-
- private static final Map<String, ClosureInfo> CLOSURE_TYPES = new
HashMap<>();
-
- static {
- CLOSURE_TYPES.put(
- SampleFamilyFunctions.TagFunction.class.getName(),
- new ClosureInfo(SampleFamilyFunctions.TagFunction.class, "apply",
- MethodType.methodType(Object.class, Object.class),
- MethodType.methodType(Map.class, Map.class),
- MethodType.methodType(Map.class, Map.class)));
-
- CLOSURE_TYPES.put(
- SampleFamilyFunctions.PropertiesExtractor.class.getName(),
- new ClosureInfo(SampleFamilyFunctions.PropertiesExtractor.class,
"apply",
- MethodType.methodType(Object.class, Object.class),
- MethodType.methodType(Map.class, Map.class),
- MethodType.methodType(Map.class, Map.class)));
-
- CLOSURE_TYPES.put(
- SampleFamilyFunctions.ForEachFunction.class.getName(),
- new ClosureInfo(SampleFamilyFunctions.ForEachFunction.class,
"accept",
- MethodType.methodType(void.class, String.class, Map.class),
- MethodType.methodType(void.class, String.class, Map.class),
- MethodType.methodType(void.class, String.class, Map.class)));
-
- CLOSURE_TYPES.put(
- SampleFamilyFunctions.DecorateFunction.class.getName(),
- new ClosureInfo(SampleFamilyFunctions.DecorateFunction.class,
"accept",
- MethodType.methodType(void.class, Object.class),
- MethodType.methodType(void.class, Object.class),
- MethodType.methodType(void.class, Object.class)));
- }
-
- private static void wireClosures(final Class<?> clazz, final Object
instance) {
- try {
- final MethodHandles.Lookup lookup =
- MethodHandles.privateLookupIn(clazz, MethodHandles.lookup());
-
- for (final Field field : clazz.getFields()) {
- final ClosureInfo info =
CLOSURE_TYPES.get(field.getType().getName());
- if (info == null) {
- continue;
- }
- final String methodName = field.getName() + "_" + info.samName;
- final MethodHandle mh = lookup.findVirtual(
- clazz, methodName, info.methodType);
- final CallSite site = LambdaMetafactory.metafactory(
- lookup,
- info.samName,
- MethodType.methodType(info.interfaceClass, clazz),
- info.samType,
- mh,
- info.instantiatedType);
- field.set(instance, site.getTarget().invoke(instance));
- }
- } catch (Throwable e) {
- throw new AssertionError("Failed to wire closures for " +
clazz.getName(), e);
- }
- }
-
// ---------------------------------------------------------------
// Input builders
// ---------------------------------------------------------------
diff --git
a/oap-graalvm-server/src/test/resources/replacement-source-sha256.properties
b/oap-graalvm-server/src/test/resources/replacement-source-sha256.properties
index 53a6da6..8229f9b 100644
--- a/oap-graalvm-server/src/test/resources/replacement-source-sha256.properties
+++ b/oap-graalvm-server/src/test/resources/replacement-source-sha256.properties
@@ -61,6 +61,10 @@
skywalking/oap-server/analyzer/agent-analyzer/src/main/java/org/apache/skywalkin
# Config loader: load from JSON manifests instead of filesystem YAML
skywalking/oap-server/analyzer/agent-analyzer/src/main/java/org/apache/skywalking/oap/server/analyzer/provider/meter/config/MeterConfigs.java
= 979b1d081a7e0aa1b627a525157d9f024f81a3db289e76284c54dbf508c494b1
+# --- library-server-for-graalvm ---
+# gRPC TLS: JDK SSL provider instead of OpenSSL (no netty_tcnative needed)
+skywalking/oap-server/server-library/library-server/src/main/java/org/apache/skywalking/oap/server/library/server/grpc/ssl/DynamicSslContext.java
= d93a977d2282c7bfca309fe1fbdfb1f73d8c95f0d29be40a3f7bd837216563fe
+
# --- library-module-for-graalvm ---
# Direct provider wiring without ServiceLoader
skywalking/oap-server/server-library/library-module/src/main/java/org/apache/skywalking/oap/server/library/module/ModuleDefine.java
= 89add3015c4265f50a13a9e5800d68aaa43e73f50c1224343712d2744b6e2437
diff --git a/oap-libs-for-graalvm/CLAUDE.md b/oap-libs-for-graalvm/CLAUDE.md
index fdc5cce..2c1aab7 100644
--- a/oap-libs-for-graalvm/CLAUDE.md
+++ b/oap-libs-for-graalvm/CLAUDE.md
@@ -54,11 +54,17 @@ JAVA_HOME=/Users/wusheng/.sdkman/candidates/java/25-graal
make build-distro
| `HierarchyService` | `server-core/.../hierarchy/HierarchyService.java` |
Support for Java-backed closures | No |
| `HttpAlarmCallback` | `server-core/.../alarm/HttpAlarmCallback.java` | Lazy
HttpClient init (static final breaks in native image) | No |
+### library-server-for-graalvm (1 class)
+
+| Replacement Class | Upstream Source | Change | Staleness Tracked |
+|---|---|---|---|
+| `DynamicSslContext` | `library-server/.../grpc/ssl/DynamicSslContext.java` |
`SslProvider.JDK` instead of `SslProvider.OPENSSL` (no `netty_tcnative` needed
for native image) | **Yes** |
+
### meter-analyzer-for-graalvm (2 classes)
| Replacement Class | Upstream Source | Change | Staleness Tracked |
|---|---|---|---|
-| `DSL` | `meter-analyzer/.../v2/dsl/DSL.java` | Load pre-compiled
`MalExpression` from per-file configs (`META-INF/mal-v2/`); look up by
expression text; wires closure fields via `LambdaMetafactory` after
instantiation | No |
+| `DSL` | `meter-analyzer/.../v2/dsl/DSL.java` | Load pre-compiled
`MalExpression` from per-file configs (`META-INF/mal-v2/`); look up by
expression text; closure fields are self-wired by companion classes in the
static initializer (no `LambdaMetafactory`) | No |
| `FilterExpression` | `meter-analyzer/.../v2/dsl/FilterExpression.java` |
Load pre-compiled `MalFilter` from v2 manifest | No |
### log-analyzer-for-graalvm (1 class)
diff --git a/oap-libs-for-graalvm/library-server-for-graalvm/pom.xml
b/oap-libs-for-graalvm/library-server-for-graalvm/pom.xml
new file mode 100644
index 0000000..6f1914d
--- /dev/null
+++ b/oap-libs-for-graalvm/library-server-for-graalvm/pom.xml
@@ -0,0 +1,64 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ 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.
+ -->
+
+<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">
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>org.apache.skywalking</groupId>
+ <artifactId>oap-libs-for-graalvm</artifactId>
+ <version>0.3.0-SNAPSHOT</version>
+ </parent>
+
+ <artifactId>library-server-for-graalvm</artifactId>
+ <name>Library Server for GraalVM</name>
+ <description>Repackaged library-server with DynamicSslContext using JDK
SSL provider instead of OpenSSL</description>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.apache.skywalking</groupId>
+ <artifactId>library-server</artifactId>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-shade-plugin</artifactId>
+ <configuration>
+ <artifactSet>
+ <includes>
+
<include>org.apache.skywalking:library-server</include>
+ </includes>
+ </artifactSet>
+ <filters>
+ <filter>
+
<artifact>org.apache.skywalking:library-server</artifact>
+ <excludes>
+
<exclude>org/apache/skywalking/oap/server/library/server/grpc/ssl/DynamicSslContext.class</exclude>
+ </excludes>
+ </filter>
+ </filters>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+</project>
diff --git
a/oap-libs-for-graalvm/library-server-for-graalvm/src/main/java/org/apache/skywalking/oap/server/library/server/grpc/ssl/DynamicSslContext.java
b/oap-libs-for-graalvm/library-server-for-graalvm/src/main/java/org/apache/skywalking/oap/server/library/server/grpc/ssl/DynamicSslContext.java
new file mode 100644
index 0000000..c255514
--- /dev/null
+++
b/oap-libs-for-graalvm/library-server-for-graalvm/src/main/java/org/apache/skywalking/oap/server/library/server/grpc/ssl/DynamicSslContext.java
@@ -0,0 +1,90 @@
+/*
+ * 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.library.server.grpc.ssl;
+
+import io.grpc.netty.GrpcSslContexts;
+import io.netty.handler.ssl.ClientAuth;
+import io.netty.handler.ssl.SslContextBuilder;
+import io.netty.handler.ssl.SslProvider;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.file.Paths;
+import javax.net.ssl.SSLException;
+import org.apache.skywalking.oap.server.library.util.StringUtil;
+import org.apache.skywalking.oap.server.library.server.ssl.AbstractSslContext;
+import org.apache.skywalking.oap.server.library.server.ssl.PrivateKeyUtil;
+
+/**
+ * Same-FQCN replacement for upstream DynamicSslContext.
+ *
+ * <p>Uses {@link SslProvider#JDK} instead of {@link SslProvider#OPENSSL} so
that
+ * gRPC TLS works in GraalVM native images without the {@code netty_tcnative}
+ * native library.
+ */
+public class DynamicSslContext extends AbstractSslContext {
+
+ public static DynamicSslContext forServer(final String privateKeyFile,
+ final String certChainFile,
+ final String trustedCAsFile) {
+ return new DynamicSslContext(privateKeyFile, certChainFile,
trustedCAsFile);
+ }
+
+ public static DynamicSslContext forClient(final String caFile) {
+ return new DynamicSslContext(caFile);
+ }
+
+ protected DynamicSslContext(String privateKeyFile, String certChainFile,
String trustedCAsFile) {
+ super(privateKeyFile, certChainFile, trustedCAsFile);
+ }
+
+ protected DynamicSslContext(String caFile) {
+ super(caFile);
+ }
+
+ @Override
+ protected void updateContext(String caFile) {
+ try {
+
setCtx(GrpcSslContexts.forClient().trustManager(Paths.get(caFile).toFile()).build());
+ } catch (SSLException e) {
+ throw new IllegalArgumentException(e);
+ }
+ }
+
+ protected void updateContext(final String privateKeyFile, final String
certChainFile, final String trustedCAsFile) {
+ try (InputStream cert = new
FileInputStream(Paths.get(certChainFile).toFile());
+ InputStream key =
PrivateKeyUtil.loadDecryptionKey(privateKeyFile)) {
+
+ SslContextBuilder builder = GrpcSslContexts.configure(
+ SslContextBuilder.forServer(cert, key),
+ SslProvider.JDK
+ );
+
+ if (StringUtil.isNotEmpty(trustedCAsFile)) {
+ builder.trustManager(Paths.get(trustedCAsFile).toFile())
+ .clientAuth(ClientAuth.REQUIRE);
+ }
+
+ setCtx(builder.build());
+ } catch (IOException e) {
+ throw new IllegalArgumentException(e);
+ }
+ }
+
+}
diff --git
a/oap-libs-for-graalvm/meter-analyzer-for-graalvm/src/main/java/org/apache/skywalking/oap/meter/analyzer/v2/dsl/DSL.java
b/oap-libs-for-graalvm/meter-analyzer-for-graalvm/src/main/java/org/apache/skywalking/oap/meter/analyzer/v2/dsl/DSL.java
index 4cd9880..4421acb 100644
---
a/oap-libs-for-graalvm/meter-analyzer-for-graalvm/src/main/java/org/apache/skywalking/oap/meter/analyzer/v2/dsl/DSL.java
+++
b/oap-libs-for-graalvm/meter-analyzer-for-graalvm/src/main/java/org/apache/skywalking/oap/meter/analyzer/v2/dsl/DSL.java
@@ -21,12 +21,6 @@ import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
-import java.lang.invoke.CallSite;
-import java.lang.invoke.LambdaMetafactory;
-import java.lang.invoke.MethodHandle;
-import java.lang.invoke.MethodHandles;
-import java.lang.invoke.MethodType;
-import java.lang.reflect.Field;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;
@@ -48,6 +42,11 @@ import lombok.extern.slf4j.Slf4j;
*
* <p>At runtime, all per-file configs are loaded once and indexed by
* expression text for O(1) lookup in {@link #parse(String, String, String)}.
+ *
+ * <p>Closure fields (TagFunction, ForEachFunction, etc.) are now self-wired
+ * via companion classes generated at build time. The main class static
+ * initializer instantiates each companion class directly — no external
+ * LambdaMetafactory wiring is needed.
*/
@Slf4j
public final class DSL {
@@ -76,7 +75,6 @@ public final class DSL {
try {
final Class<?> exprClass = Class.forName(className);
final MalExpression malExpr = (MalExpression)
exprClass.getDeclaredConstructor().newInstance();
- wireClosures(exprClass, malExpr);
final int count = LOADED_COUNT.incrementAndGet();
log.debug("Loaded pre-compiled MAL expression [{}/{}]: {} -> {}",
count, EXPRESSION_MAP.size(), metricName, className);
@@ -179,86 +177,4 @@ public final class DSL {
log.warn("Failed to load MAL v2 per-file config: {}", configPath,
e);
}
}
-
- /**
- * Closure type metadata for LambdaMetafactory wiring.
- * Maps functional interface type name to SAM method info.
- */
- private static final Map<String, ClosureInfo> CLOSURE_TYPES = new
HashMap<>();
-
- static {
- // TagFunction extends Function<Map, Map>
- CLOSURE_TYPES.put(
- SampleFamilyFunctions.TagFunction.class.getName(),
- new ClosureInfo(SampleFamilyFunctions.TagFunction.class, "apply",
- MethodType.methodType(Object.class, Object.class),
- MethodType.methodType(Map.class, Map.class),
- MethodType.methodType(Map.class, Map.class)));
-
- // PropertiesExtractor extends Function<Map, Map>
- CLOSURE_TYPES.put(
- SampleFamilyFunctions.PropertiesExtractor.class.getName(),
- new ClosureInfo(SampleFamilyFunctions.PropertiesExtractor.class,
"apply",
- MethodType.methodType(Object.class, Object.class),
- MethodType.methodType(Map.class, Map.class),
- MethodType.methodType(Map.class, Map.class)));
-
- // ForEachFunction — not generic, SAM = instantiated
- CLOSURE_TYPES.put(
- SampleFamilyFunctions.ForEachFunction.class.getName(),
- new ClosureInfo(SampleFamilyFunctions.ForEachFunction.class,
"accept",
- MethodType.methodType(void.class, String.class, Map.class),
- MethodType.methodType(void.class, String.class, Map.class),
- MethodType.methodType(void.class, String.class, Map.class)));
-
- // DecorateFunction extends Consumer<MeterEntity>
- CLOSURE_TYPES.put(
- SampleFamilyFunctions.DecorateFunction.class.getName(),
- new ClosureInfo(SampleFamilyFunctions.DecorateFunction.class,
"accept",
- MethodType.methodType(void.class, Object.class),
- MethodType.methodType(void.class, Object.class),
- MethodType.methodType(void.class, Object.class)));
- }
-
- private record ClosureInfo(Class<?> interfaceClass, String samName,
- MethodType samType, MethodType instantiatedType,
- MethodType methodType) {
- }
-
- /**
- * Wire closure fields on a pre-compiled MalExpression instance.
- *
- * <p>Generated classes have public fields typed as functional interfaces
- * (TagFunction, ForEachFunction, etc.) with corresponding methods that
- * implement the closure body. MALClassGenerator normally wires these via
- * LambdaMetafactory after compilation. When loading from the manifest JAR,
- * we replicate that wiring here.
- */
- static void wireClosures(final Class<?> clazz,
- final Object instance) {
- try {
- final MethodHandles.Lookup lookup =
- MethodHandles.privateLookupIn(clazz, MethodHandles.lookup());
-
- for (final Field field : clazz.getFields()) {
- final ClosureInfo info =
CLOSURE_TYPES.get(field.getType().getName());
- if (info == null) {
- continue;
- }
- final String methodName = field.getName() + "_" + info.samName;
- final MethodHandle mh = lookup.findVirtual(
- clazz, methodName, info.methodType);
- final CallSite site = LambdaMetafactory.metafactory(
- lookup,
- info.samName,
- MethodType.methodType(info.interfaceClass, clazz),
- info.samType,
- mh,
- info.instantiatedType);
- field.set(instance, site.getTarget().invoke(instance));
- }
- } catch (Throwable e) {
- log.warn("Failed to wire closures for {}", clazz.getName(), e);
- }
- }
}
diff --git a/oap-libs-for-graalvm/pom.xml b/oap-libs-for-graalvm/pom.xml
index 95f8176..100794c 100644
--- a/oap-libs-for-graalvm/pom.xml
+++ b/oap-libs-for-graalvm/pom.xml
@@ -35,6 +35,7 @@
<modules>
<module>server-core-for-graalvm</module>
<module>library-module-for-graalvm</module>
+ <module>library-server-for-graalvm</module>
<module>library-util-for-graalvm</module>
<module>meter-analyzer-for-graalvm</module>
<module>log-analyzer-for-graalvm</module>
diff --git a/pom.xml b/pom.xml
index 68e32ec..ddcd2ba 100644
--- a/pom.xml
+++ b/pom.xml
@@ -112,6 +112,11 @@
<artifactId>library-module</artifactId>
<version>${skywalking.version}</version>
</dependency>
+ <dependency>
+ <groupId>org.apache.skywalking</groupId>
+ <artifactId>library-server</artifactId>
+ <version>${skywalking.version}</version>
+ </dependency>
<dependency>
<groupId>org.apache.skywalking</groupId>
<artifactId>library-util</artifactId>
@@ -378,6 +383,11 @@
<artifactId>library-module-for-graalvm</artifactId>
<version>${project.version}</version>
</dependency>
+ <dependency>
+ <groupId>org.apache.skywalking</groupId>
+ <artifactId>library-server-for-graalvm</artifactId>
+ <version>${project.version}</version>
+ </dependency>
<dependency>
<groupId>org.apache.skywalking</groupId>
<artifactId>library-util-for-graalvm</artifactId>
diff --git a/skywalking b/skywalking
index 64a1795..726ebcc 160000
--- a/skywalking
+++ b/skywalking
@@ -1 +1 @@
-Subproject commit 64a1795d8a582f2216f47bfe572b3ab649733c01
+Subproject commit 726ebcc321dbd3c258963fc4bc23d320b903f6d9
diff --git a/test/e2e/cases/so11y/so11y-cases.yaml
b/test/e2e/cases/so11y/so11y-cases.yaml
index e1fd1c3..698938d 100644
--- a/test/e2e/cases/so11y/so11y-cases.yaml
+++ b/test/e2e/cases/so11y/so11y-cases.yaml
@@ -29,7 +29,8 @@
# OAP application-level metrics (available in native image)
- query: swctl --display yaml
--base-url=http://${oap_host}:${oap_12800}/graphql metrics exec
--expression=meter_oap_instance_trace_count
--instance-name=http://localhost:1234 --service-name=oap-server
expected:
../../../../skywalking/test/e2e-v2/cases/so11y/expected/metrics-has-value-label-trace.yml
- # meter_oap_instance_metrics_aggregation skipped: .tag() closure NPE in
pre-compiled MAL
+ - query: swctl --display yaml
--base-url=http://${oap_host}:${oap_12800}/graphql metrics exec
--expression="meter_oap_instance_metrics_aggregation{level='L1 aggregation'}"
--instance-name=http://localhost:1234 --service-name=oap-server
+ expected:
../../../../skywalking/test/e2e-v2/cases/so11y/expected/metrics-has-value-label.yml
- query: swctl --display yaml
--base-url=http://${oap_host}:${oap_12800}/graphql metrics exec
--expression=meter_oap_instance_persistence_prepare_count
--instance-name=http://localhost:1234 --service-name=oap-server
expected:
../../../../skywalking/test/e2e-v2/cases/so11y/expected/metrics-has-value.yml
- query: swctl --display yaml
--base-url=http://${oap_host}:${oap_12800}/graphql metrics exec
--expression=meter_oap_instance_persistence_execute_count
--instance-name=http://localhost:1234 --service-name=oap-server
diff --git a/test/e2e/cases/ssl/docker-compose.yml
b/test/e2e/cases/ssl/docker-compose.yml
new file mode 100644
index 0000000..b446950
--- /dev/null
+++ b/test/e2e/cases/ssl/docker-compose.yml
@@ -0,0 +1,136 @@
+# 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.
+
+services:
+ banyandb:
+ extends:
+ file:
../../../../skywalking/test/e2e-v2/script/docker-compose/base-compose.yml
+ service: banyandb
+ ports:
+ - 17912
+
+ oap:
+ image: skywalking-oap-native:latest
+ volumes:
+ -
../../../../skywalking/test/e2e-v2/cases/simple/ssl/certs:/skywalking/certs
+ expose:
+ - 11800
+ - 12800
+ networks:
+ - e2e
+ ports:
+ - 12800
+ environment:
+ SW_HEALTH_CHECKER: default
+ SW_STORAGE_BANYANDB_TARGETS: banyandb:17912
+ SW_CONFIGURATION: none
+ SW_CORE_GRPC_SSL_ENABLED: "true"
+ SW_CORE_GRPC_SSL_KEY_PATH: /skywalking/certs/server-key.pem
+ SW_CORE_GRPC_SSL_CERT_CHAIN_PATH: /skywalking/certs/server.crt
+ SW_CORE_GRPC_SSL_TRUSTED_CA_PATH: /skywalking/certs/ca.crt
+ depends_on:
+ banyandb:
+ condition: service_healthy
+ healthcheck:
+ test: ["CMD-SHELL", "nc -nz 127.0.0.1 11800 || exit 1"]
+ interval: 5s
+ timeout: 60s
+ retries: 120
+
+ # Init containers: copy pre-built service JARs into a shared volume
+ provider-jar:
+ image:
"ghcr.io/apache/skywalking/e2e-service-provider:${SW_E2E_SERVICE_COMMIT}"
+ entrypoint: ["cp", "/app.jar", "/jars/services_provider.jar"]
+ volumes:
+ - service-jars:/jars
+ networks:
+ - e2e
+
+ consumer-jar:
+ image:
"ghcr.io/apache/skywalking/e2e-service-consumer:${SW_E2E_SERVICE_COMMIT}"
+ entrypoint: ["cp", "/app.jar", "/jars/services_consumer.jar"]
+ volumes:
+ - service-jars:/jars
+ networks:
+ - e2e
+
+ provider:
+ image:
"ghcr.io/apache/skywalking-java/skywalking-java:${SW_AGENT_JAVA_COMMIT}-java${SW_AGENT_JDK_VERSION}"
+ command: ["java", "-jar", "/jars/services_provider.jar"]
+ volumes:
+ - service-jars:/jars
+ -
../../../../skywalking/test/e2e-v2/cases/simple/ssl/ca:/skywalking/agent/ca
+ networks:
+ - e2e
+ expose:
+ - 9090
+ ports:
+ - 9090
+ environment:
+ SW_AGENT_COLLECTOR_BACKEND_SERVICES: oap:11800
+ SW_LOGGING_OUTPUT: CONSOLE
+ SW_AGENT_NAME: e2e-service-provider
+ SW_AGENT_INSTANCE_NAME: provider1
+ SW_AGENT_COLLECTOR_GET_PROFILE_TASK_INTERVAL: 1
+ SW_AGENT_COLLECTOR_GET_AGENT_DYNAMIC_CONFIG_INTERVAL: 1
+ SW_METER_ACTIVE: 'false'
+ healthcheck:
+ test: ["CMD", "bash", "-c", "cat < /dev/null > /dev/tcp/127.0.0.1/9090"]
+ interval: 5s
+ timeout: 60s
+ retries: 120
+ depends_on:
+ oap:
+ condition: service_healthy
+ provider-jar:
+ condition: service_completed_successfully
+
+ consumer:
+ image:
"ghcr.io/apache/skywalking-java/skywalking-java:${SW_AGENT_JAVA_COMMIT}-java${SW_AGENT_JDK_VERSION}"
+ command: ["java", "-jar", "/jars/services_consumer.jar"]
+ volumes:
+ - service-jars:/jars
+ -
../../../../skywalking/test/e2e-v2/cases/simple/ssl/ca:/skywalking/agent/ca
+ networks:
+ - e2e
+ expose:
+ - 9092
+ ports:
+ - 9092
+ environment:
+ SW_AGENT_COLLECTOR_BACKEND_SERVICES: oap:11800
+ SW_LOGGING_OUTPUT: CONSOLE
+ PROVIDER_URL: http://provider:9090
+ SW_AGENT_NAME: e2e-service-consumer
+ SW_AGENT_INSTANCE_NAME: consumer1
+ SW_AGENT_COLLECTOR_GET_PROFILE_TASK_INTERVAL: 1
+ SW_AGENT_COLLECTOR_GET_AGENT_DYNAMIC_CONFIG_INTERVAL: 1
+ SW_METER_ACTIVE: 'false'
+ healthcheck:
+ test: ["CMD", "bash", "-c", "cat < /dev/null > /dev/tcp/127.0.0.1/9092"]
+ interval: 5s
+ timeout: 60s
+ retries: 120
+ depends_on:
+ provider:
+ condition: service_healthy
+ consumer-jar:
+ condition: service_completed_successfully
+
+volumes:
+ service-jars:
+
+networks:
+ e2e:
diff --git a/test/e2e/cases/ssl/e2e.yaml b/test/e2e/cases/ssl/e2e.yaml
new file mode 100644
index 0000000..33723a8
--- /dev/null
+++ b/test/e2e/cases/ssl/e2e.yaml
@@ -0,0 +1,48 @@
+# 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.
+
+# SSL E2E — native OAP with gRPC TLS enabled (JDK SSL provider).
+# Verifies that agents can report traces over TLS-encrypted gRPC.
+
+setup:
+ env: compose
+ file: docker-compose.yml
+ timeout: 20m
+ init-system-environment: ../../script/env
+ steps:
+ - name: set PATH
+ command: export PATH=/tmp/skywalking-infra-e2e/bin:$PATH
+ - name: install yq
+ command: bash
skywalking/test/e2e-v2/script/prepare/setup-e2e-shell/install.sh yq
+ - name: install swctl
+ command: bash
skywalking/test/e2e-v2/script/prepare/setup-e2e-shell/install.sh swctl
+
+trigger:
+ action: http
+ interval: 3s
+ times: -1
+ url: http://${consumer_host}:${consumer_9092}/users
+ method: POST
+ body: '{"id":"123","name":"skywalking"}'
+ headers:
+ "Content-Type": "application/json"
+
+verify:
+ retry:
+ count: 20
+ interval: 3s
+ cases:
+ - includes:
+ - ../../../../skywalking/test/e2e-v2/cases/simple/simple-cases.yaml