test: add dubbo integration-test

This commit is contained in:
ReaJason
2026-06-28 21:22:31 +08:00
parent fab736e175
commit a267048ed3
33 changed files with 1780 additions and 5 deletions
@@ -9,6 +9,7 @@ on:
- './github/workflows/memshell-integration-test.yml'
- '**/memshell/**'
- '**/packer/**'
- '**/dubbo/**'
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}-${{ github.event_name }}
@@ -54,6 +55,8 @@ jobs:
depend_tasks: ""
- middleware: "geronimo"
depend_tasks: ":vul:vul-webapp:war"
- middleware: "dubbo"
depend_tasks: ":vul:vul-dubbo:dubboProviderFatJars :tools:command:dubboClientClasspath"
runs-on: ubuntu-22.04
name: ${{ matrix.cases.middleware }}
steps:
@@ -73,10 +76,15 @@ jobs:
run: ./gradlew ${{ matrix.cases.depend_tasks }}
- name: Integration Test with gradle
if: matrix.cases.middleware != 'dubbo'
run: ./gradlew :integration-test:test --tests '*.memshell.${{ matrix.cases.middleware }}.*' --info
- name: Dubbo Integration Test with gradle
if: matrix.cases.middleware == 'dubbo'
run: ./gradlew :integration-test:dubboContainerTest --info
- name: Export Integration Test Summary
uses: mikepenz/action-junit-report@v5
if: success() || failure()
with:
report_paths: '**/build/test-results/test/TEST-*.xml'
report_paths: '**/build/test-results/*/TEST-*.xml'
+58 -2
View File
@@ -7,6 +7,9 @@ plugins {
group = "io.github.reajason"
version = rootProject.version
evaluationDependsOn(":vul:vul-dubbo")
evaluationDependsOn(":tools:command")
idea {
module {
excludeDirs.add(file("src/main"))
@@ -36,8 +39,41 @@ dependencies {
}
}
tasks.test {
useJUnitPlatform()
val dubboProviderProject = project(":vul:vul-dubbo")
val dubboCommandProject = project(":tools:command")
fun dubboProviderJar(taskName: String): Provider<String> {
return dubboProviderProject.tasks.named<Jar>(taskName).flatMap { task ->
task.archiveFile.map { it.asFile.absolutePath }
}
}
fun dubboClientClasspath(clientKind: String): Provider<String> {
return dubboCommandProject.layout.buildDirectory.file("dubbo-client-classpaths/$clientKind.txt").map {
it.asFile.readText()
}
}
fun Test.configureDubboSystemProperties() {
dependsOn(":vul:vul-dubbo:dubboProviderFatJars", ":tools:command:dubboClientClasspath")
val systemProperties = mapOf(
"dubbo.alibaba.client.classpath" to dubboClientClasspath("alibaba"),
"dubbo.apache.client.classpath" to dubboClientClasspath("apache"),
"dubbo.alibaba.provider.jar" to dubboProviderJar("dubboAlibabaProviderFatJar"),
"dubbo.apache276.provider.jar" to dubboProviderJar("dubboApache276ProviderFatJar"),
"dubbo.apache277.provider.jar" to dubboProviderJar("dubboApache277ProviderFatJar"),
"dubbo.apache278.provider.jar" to dubboProviderJar("dubboApache278ProviderFatJar"),
"dubbo.apache2723.provider.jar" to dubboProviderJar("dubboApache2723ProviderFatJar"),
"dubbo.apache336.provider.jar" to dubboProviderJar("dubboApache336ProviderFatJar")
)
doFirst {
systemProperties.forEach { (key, value) ->
systemProperty(key, value.get())
}
}
}
fun Test.configureIntegrationJvm() {
jvmArgs(
"--add-opens=java.base/java.util=ALL-UNNAMED",
"--add-opens=java.xml/com.sun.org.apache.xalan.internal.xsltc.trax=ALL-UNNAMED",
@@ -47,3 +83,23 @@ tasks.test {
events("passed", "skipped", "failed")
}
}
tasks.test {
useJUnitPlatform {
excludeTags("dubbo-container")
}
configureIntegrationJvm()
}
tasks.register<Test>("dubboContainerTest") {
group = "verification"
description = "Runs DubboService provider/client integration tests."
testClassesDirs = sourceSets.test.get().output.classesDirs
classpath = sourceSets.test.get().runtimeClasspath
useJUnitPlatform {
includeTags("dubbo-container")
}
dependsOn("testClasses")
configureDubboSystemProperties()
configureIntegrationJvm()
}
@@ -0,0 +1,36 @@
package com.reajason.javaweb.integration.memshell.dubbo;
import net.bytebuddy.jar.asm.Opcodes;
import org.junit.jupiter.api.Tag;
import org.junit.jupiter.api.Test;
import org.testcontainers.containers.GenericContainer;
import org.testcontainers.junit.jupiter.Container;
import org.testcontainers.junit.jupiter.Testcontainers;
import java.util.List;
@Testcontainers
@Tag("dubbo-container")
class AlibabaDubbo2612ContainerTest {
private static final DubboProviderScenario SCENARIO = new DubboProviderScenario(
"alibaba-dubbo-2.6.12",
"alibaba",
"dubbo.alibaba.provider.jar",
"eclipse-temurin:8-jre",
20880,
Opcodes.V1_8,
List.of(
new DubboProtocolTarget("dubbo", 20880),
new DubboProtocolTarget("hessian", 28080)
)
);
@Container
static final GenericContainer<?> container = DubboContainerFactory.buildProvider(SCENARIO);
@Test
void testDubboServiceRegistration() {
DubboServiceAssertion.assertCommandService(container, SCENARIO);
}
}
@@ -0,0 +1,84 @@
package com.reajason.javaweb.integration.memshell.dubbo;
import net.bytebuddy.jar.asm.Opcodes;
import org.junit.jupiter.api.Tag;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
import org.testcontainers.containers.GenericContainer;
import org.testcontainers.junit.jupiter.Container;
import org.testcontainers.junit.jupiter.Testcontainers;
import java.util.List;
import java.util.stream.Stream;
@Testcontainers
@Tag("dubbo-container")
class ApacheDubbo27xContainerTest {
private static final String IMAGE = "eclipse-temurin:17-jdk";
private static final DubboProviderScenario APACHE_276 = apacheScenario(
"apache-dubbo-2.7.6",
"dubbo.apache276.provider.jar",
20885,
28086
);
private static final DubboProviderScenario APACHE_277 = apacheScenario(
"apache-dubbo-2.7.7",
"dubbo.apache277.provider.jar",
20886,
28088
);
private static final DubboProviderScenario APACHE_278 = apacheScenario(
"apache-dubbo-2.7.8",
"dubbo.apache278.provider.jar",
20887,
28090
);
private static final DubboProviderScenario APACHE_2723 = apacheScenario(
"apache-dubbo-2.7.23",
"dubbo.apache2723.provider.jar",
20881,
28082
);
@Container
static final GenericContainer<?> apache276 = DubboContainerFactory.buildProvider(APACHE_276);
@Container
static final GenericContainer<?> apache277 = DubboContainerFactory.buildProvider(APACHE_277);
@Container
static final GenericContainer<?> apache278 = DubboContainerFactory.buildProvider(APACHE_278);
@Container
static final GenericContainer<?> apache2723 = DubboContainerFactory.buildProvider(APACHE_2723);
static Stream<Arguments> scenarios() {
return Stream.of(
Arguments.of(apache276, APACHE_276),
Arguments.of(apache277, APACHE_277),
Arguments.of(apache278, APACHE_278),
Arguments.of(apache2723, APACHE_2723)
);
}
@ParameterizedTest(name = "{1}")
@MethodSource("scenarios")
void testDubboServiceRegistration(GenericContainer<?> container, DubboProviderScenario scenario) {
DubboServiceAssertion.assertCommandService(container, scenario);
}
private static DubboProviderScenario apacheScenario(String name, String providerJarProperty, int dubboPort, int hessianPort) {
return new DubboProviderScenario(
name,
"apache",
providerJarProperty,
IMAGE,
dubboPort,
Opcodes.V1_8,
List.of(
new DubboProtocolTarget("dubbo", dubboPort),
new DubboProtocolTarget("hessian", hessianPort)
)
);
}
}
@@ -0,0 +1,37 @@
package com.reajason.javaweb.integration.memshell.dubbo;
import net.bytebuddy.jar.asm.Opcodes;
import org.junit.jupiter.api.Tag;
import org.junit.jupiter.api.Test;
import org.testcontainers.containers.GenericContainer;
import org.testcontainers.junit.jupiter.Container;
import org.testcontainers.junit.jupiter.Testcontainers;
import java.util.List;
@Testcontainers
@Tag("dubbo-container")
class ApacheDubbo336ContainerTest {
private static final DubboProviderScenario SCENARIO = new DubboProviderScenario(
"apache-dubbo-3.3.6",
"apache",
"dubbo.apache336.provider.jar",
"eclipse-temurin:17-jdk",
20882,
Opcodes.V1_8,
List.of(
new DubboProtocolTarget("dubbo", 20882),
new DubboProtocolTarget("hessian", 28084),
new DubboProtocolTarget("tri", 50051)
)
);
@Container
static final GenericContainer<?> container = DubboContainerFactory.buildProvider(SCENARIO);
@Test
void testDubboServiceRegistration() {
DubboServiceAssertion.assertCommandService(container, SCENARIO);
}
}
@@ -0,0 +1,130 @@
package com.reajason.javaweb.integration.memshell.dubbo;
import lombok.SneakyThrows;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
final class DubboClientRunner {
private DubboClientRunner() {
}
static String loadBytes(String clientKind, String url, String base64) {
return run(clientKind, "load-bytes", url, base64);
}
static String runCommand(String clientKind, String url, String interfaceName, String command) {
return run(clientKind, "run-command", url, interfaceName, command);
}
@SneakyThrows
private static String run(String clientKind, String... args) {
List<String> command = new ArrayList<String>();
String javaExecutable = javaExecutable(clientKind);
command.add(javaExecutable);
if (supportsAddOpens(javaExecutable)) {
command.add("--add-opens=java.base/java.lang=ALL-UNNAMED");
command.add("--add-opens=java.base/java.math=ALL-UNNAMED");
}
command.add("-cp");
command.add(clientClasspath(clientKind));
command.add(mainClass(clientKind));
for (String arg : args) {
command.add(arg);
}
Process process = new ProcessBuilder(command)
.redirectErrorStream(true)
.start();
ByteArrayOutputStream output = new ByteArrayOutputStream();
copy(process.getInputStream(), output);
int exitCode = process.waitFor();
String rendered = output.toString(StandardCharsets.UTF_8).trim();
if (exitCode != 0) {
throw new IllegalStateException("Dubbo client failed with exit code " + exitCode + "\n" + rendered);
}
return rendered;
}
private static String clientClasspath(String clientKind) {
String property = System.getProperty("dubbo." + clientKind + ".client.classpath");
if (isBlank(property)) {
throw new IllegalStateException("missing dubbo." + clientKind + ".client.classpath system property");
}
return property;
}
private static String mainClass(String clientKind) {
if ("alibaba".equals(clientKind)) {
return "io.github.reajason.dubbo.fixture.client.alibaba.AlibabaDubboClient";
}
if ("apache".equals(clientKind)) {
return "io.github.reajason.dubbo.fixture.client.apache.ApacheDubboClient";
}
throw new IllegalArgumentException("unsupported client kind: " + clientKind);
}
private static String javaExecutable(String clientKind) {
if (!"alibaba".equals(clientKind)) {
return "java";
}
String java8Home = System.getenv("JAVA8_HOME");
if (isBlank(java8Home)) {
java8Home = System.getProperty("java8.home");
}
if (isBlank(java8Home)) {
return "java";
}
return java8Home + "/bin/java";
}
private static boolean supportsAddOpens(String javaExecutable) throws java.io.IOException, InterruptedException {
Process process = new ProcessBuilder(javaExecutable, "-version")
.redirectErrorStream(true)
.start();
ByteArrayOutputStream output = new ByteArrayOutputStream();
copy(process.getInputStream(), output);
int exitCode = process.waitFor();
if (exitCode != 0) {
return true;
}
return majorVersion(output.toString(StandardCharsets.UTF_8)) >= 9;
}
private static int majorVersion(String versionOutput) {
Matcher matcher = Pattern.compile("version \"([^\"]+)\"").matcher(versionOutput);
if (!matcher.find()) {
return 9;
}
String version = matcher.group(1);
if (version.startsWith("1.")) {
int dot = version.indexOf('.', 2);
return leadingInteger(dot < 0 ? version.substring(2) : version.substring(2, dot));
}
int dot = version.indexOf('.');
return leadingInteger(dot < 0 ? version : version.substring(0, dot));
}
private static int leadingInteger(String value) {
Matcher matcher = Pattern.compile("^(\\d+)").matcher(value);
return matcher.find() ? Integer.parseInt(matcher.group(1)) : 9;
}
private static boolean isBlank(String value) {
return value == null || value.trim().isEmpty();
}
private static void copy(InputStream inputStream, ByteArrayOutputStream outputStream) throws java.io.IOException {
byte[] buffer = new byte[4096];
int read;
while ((read = inputStream.read(buffer)) >= 0) {
outputStream.write(buffer, 0, read);
}
}
}
@@ -0,0 +1,56 @@
package com.reajason.javaweb.integration.memshell.dubbo;
import org.testcontainers.containers.GenericContainer;
import org.testcontainers.containers.wait.strategy.Wait;
import org.testcontainers.utility.MountableFile;
import java.nio.file.Files;
import java.nio.file.Path;
import java.time.Duration;
final class DubboContainerFactory {
private DubboContainerFactory() {
}
static GenericContainer<?> buildProvider(DubboProviderScenario scenario) {
String jarPath = providerJarPath(scenario.providerJarProperty());
GenericContainer<?> container = new GenericContainer<>(scenario.imageName())
.withCopyFileToContainer(MountableFile.forHostPath(jarPath), "/app/app.jar")
.withWorkingDirectory("/app")
.withExposedPorts(exposedPorts(scenario))
.waitingFor(Wait.forLogMessage(".*Provider started.*", 1)
.withStartupTimeout(Duration.ofMinutes(3)));
if ("alibaba".equals(scenario.clientKind())) {
container.withCommand("java", "-jar", "/app/app.jar");
} else {
container.withCommand(
"java",
"--add-opens", "java.base/java.lang=ALL-UNNAMED",
"--add-opens", "java.base/java.math=ALL-UNNAMED",
"-jar", "/app/app.jar"
);
}
return container;
}
private static Integer[] exposedPorts(DubboProviderScenario scenario) {
return scenario.commandTargets().stream()
.map(DubboProtocolTarget::port)
.distinct()
.toArray(Integer[]::new);
}
private static String providerJarPath(String propertyName) {
String value = System.getProperty(propertyName);
if (value == null || value.trim().isEmpty()) {
throw new IllegalStateException("missing " + propertyName + " system property");
}
Path path = Path.of(value);
if (!Files.isRegularFile(path)) {
throw new IllegalStateException("provider jar does not exist: " + path);
}
return path.toAbsolutePath().toString();
}
}
@@ -0,0 +1,27 @@
package com.reajason.javaweb.integration.memshell.dubbo;
final class DubboProtocolTarget {
private final String protocol;
private final int port;
DubboProtocolTarget(String protocol, int port) {
this.protocol = protocol;
this.port = port;
}
String protocol() {
return protocol;
}
int port() {
return port;
}
String url(String host, String interfaceName) {
return protocol + "://" + host + ":" + port + "/" + interfaceName;
}
String url(String host, int mappedPort, String interfaceName) {
return protocol + "://" + host + ":" + mappedPort + "/" + interfaceName;
}
}
@@ -0,0 +1,66 @@
package com.reajason.javaweb.integration.memshell.dubbo;
import com.reajason.javaweb.memshell.ShellType;
import java.util.List;
final class DubboProviderScenario {
private final String name;
private final String clientKind;
private final String providerJarProperty;
private final String imageName;
private final int loaderPort;
private final int targetJdkVersion;
private final List<DubboProtocolTarget> commandTargets;
DubboProviderScenario(String name, String clientKind, String providerJarProperty, String imageName,
int loaderPort, int targetJdkVersion, List<DubboProtocolTarget> commandTargets) {
this.name = name;
this.clientKind = clientKind;
this.providerJarProperty = providerJarProperty;
this.imageName = imageName;
this.loaderPort = loaderPort;
this.targetJdkVersion = targetJdkVersion;
this.commandTargets = commandTargets;
}
String name() {
return name;
}
String clientKind() {
return clientKind;
}
String providerJarProperty() {
return providerJarProperty;
}
String imageName() {
return imageName;
}
int loaderPort() {
return loaderPort;
}
int targetJdkVersion() {
return targetJdkVersion;
}
List<DubboProtocolTarget> commandTargets() {
return commandTargets;
}
String shellType() {
if ("alibaba".equals(clientKind)) {
return ShellType.ALIBABA_DUBBO_SERVICE;
}
return ShellType.APACHE_DUBBO_SERVICE;
}
@Override
public String toString() {
return name;
}
}
@@ -0,0 +1,64 @@
package com.reajason.javaweb.integration.memshell.dubbo;
import com.reajason.javaweb.Server;
import com.reajason.javaweb.integration.ShellAssertion;
import com.reajason.javaweb.memshell.MemShellResult;
import com.reajason.javaweb.memshell.ShellTool;
import com.reajason.javaweb.memshell.config.CommandConfig;
import com.reajason.javaweb.memshell.config.ShellToolConfig;
import com.reajason.javaweb.packer.Packers;
import lombok.extern.slf4j.Slf4j;
import org.testcontainers.containers.GenericContainer;
import java.util.List;
import java.util.stream.Collectors;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.anyOf;
import static org.hamcrest.Matchers.containsString;
@Slf4j
final class DubboServiceAssertion {
private static final String LOADER_INTERFACE = "io.github.reajason.dubbo.fixture.api.BytecodeLoadingService";
private static final String COMMAND = "id";
private DubboServiceAssertion() {
}
static void assertCommandService(GenericContainer<?> container, DubboProviderScenario scenario) {
ShellToolConfig shellToolConfig = ShellAssertion.getShellToolConfig(
scenario.shellType(),
ShellTool.Command,
Packers.Base64
);
MemShellResult result = ShellAssertion.generate(
null,
Server.Dubbo,
null,
scenario.shellType(),
ShellTool.Command,
scenario.targetJdkVersion(),
shellToolConfig,
Packers.Base64
);
String interfaceName = result.getInjectorConfig().getUrlPattern();
String host = container.getHost();
String loaderUrl = "dubbo://" + host + ":" + container.getMappedPort(scenario.loaderPort()) + "/" + LOADER_INTERFACE;
log.info("loading {} into {} via {}", interfaceName, scenario.name(), loaderUrl);
String loadOutput = DubboClientRunner.loadBytes(scenario.clientKind(), loaderUrl, result.getInjectorBytesBase64Str());
log.info("{} load output: {}", scenario.name(), loadOutput);
List<String> fallbackUrls = scenario.commandTargets().stream()
.map(target -> target.url(host, container.getMappedPort(target.port()), interfaceName))
.collect(Collectors.toList());
List<String> commandUrls = DubboUrlResolver.resolveCommandUrls(loadOutput, interfaceName, fallbackUrls);
for (String commandUrl : commandUrls) {
String output = DubboClientRunner.runCommand(scenario.clientKind(), commandUrl, interfaceName, COMMAND);
log.info("{} {} command output: {}", scenario.name(), DubboUrlResolver.protocolOf(commandUrl), output);
assertThat(output, anyOf(containsString("uid="), containsString("injected-ok")));
}
}
}
@@ -0,0 +1,72 @@
package com.reajason.javaweb.integration.memshell.dubbo;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
final class DubboUrlResolver {
private DubboUrlResolver() {
}
static List<String> resolveCommandUrls(String loadOutput, String interfaceName, List<String> fallbackUrls) {
Map<String, String> discoveredByProtocol = new LinkedHashMap<String, String>();
Pattern pattern = Pattern.compile("(dubbo|hessian|tri)://[^\\s,]*" + Pattern.quote(interfaceName) + "(?:\\?[^\\s,]*)?");
Matcher matcher = pattern.matcher(loadOutput);
while (matcher.find()) {
String candidate = matcher.group();
discoveredByProtocol.put(protocolOf(candidate), candidate);
}
return fallbackUrls.stream()
.map(fallback -> {
String discovered = discoveredByProtocol.get(protocolOf(fallback));
return discovered == null ? fallback : rewriteEndpoint(discovered, fallback);
})
.collect(Collectors.toList());
}
static String protocolOf(String url) {
int separator = url.indexOf("://");
return separator < 0 ? url : url.substring(0, separator);
}
static String rewriteHost(String url, String host) {
try {
URI uri = new URI(url);
return new URI(
uri.getScheme(),
uri.getUserInfo(),
host,
uri.getPort(),
uri.getPath(),
uri.getQuery(),
uri.getFragment()
).toString();
} catch (URISyntaxException e) {
throw new IllegalArgumentException("invalid direct url: " + url, e);
}
}
static String rewriteEndpoint(String url, String endpointUrl) {
try {
URI uri = new URI(url);
URI endpoint = new URI(endpointUrl);
return new URI(
uri.getScheme(),
uri.getUserInfo(),
endpoint.getHost(),
endpoint.getPort(),
uri.getPath(),
uri.getQuery(),
uri.getFragment()
).toString();
} catch (URISyntaxException e) {
throw new IllegalArgumentException("invalid direct url: " + url + " or " + endpointUrl, e);
}
}
}
@@ -0,0 +1,34 @@
package com.reajason.javaweb.integration.memshell.dubbo;
import org.junit.jupiter.api.Test;
import java.util.List;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.contains;
class DubboUrlResolverTest {
@Test
void discoveredUrlsOverrideFallbackByProtocolAndRewriteHost() {
String interfaceName = "org.example.ICommandService";
String loadOutput = "ok dubbo://x.x.x.x:20880/" + interfaceName + "?side=provider, "
+ "hessian://10.1.2.3:28080/" + interfaceName;
List<String> resolved = DubboUrlResolver.resolveCommandUrls(
loadOutput,
interfaceName,
List.of(
"dubbo://127.0.0.1:1111/" + interfaceName,
"hessian://127.0.0.1:2222/" + interfaceName,
"tri://127.0.0.1:3333/" + interfaceName
)
);
assertThat(resolved, contains(
"dubbo://127.0.0.1:1111/" + interfaceName + "?side=provider",
"hessian://127.0.0.1:2222/" + interfaceName,
"tri://127.0.0.1:3333/" + interfaceName
));
}
}
+2 -1
View File
@@ -28,7 +28,7 @@ dependencyResolutionManagement {
rootProject.name = "memshell-party"
include("memshell-party-common")
include("tools:godzilla", "tools:behinder", "tools:suo5", "tools:ant-sword")
include("tools:godzilla", "tools:behinder", "tools:suo5", "tools:ant-sword", "tools:command")
include("packer")
include("boot")
include("generator")
@@ -47,6 +47,7 @@ include("vul:vul-springboot359")
include("vul:vul-springboot2-webflux")
include("vul:vul-springboot3-webflux")
include("vul:vul-playframework")
include("vul:vul-dubbo")
include("memshell-agent:memshell-agent-attacher")
include("memshell-agent:memshell-agent-asm")
include("memshell-agent:memshell-agent-javassist")
+96
View File
@@ -0,0 +1,96 @@
plugins {
id("java")
id("idea")
}
group = "io.github.reajason"
version = rootProject.version
val dubboClientCommon: SourceSet by sourceSets.creating
val dubboClientAlibaba: SourceSet by sourceSets.creating
val dubboClientApache: SourceSet by sourceSets.creating
val dubboClientCommonImplementation: Configuration by configurations.getting
val dubboClientAlibabaImplementation: Configuration by configurations.getting
val dubboClientApacheImplementation: Configuration by configurations.getting
val dubboClientCommonOutput = dubboClientCommon.output
dubboClientAlibaba.compileClasspath += dubboClientCommonOutput
dubboClientAlibaba.runtimeClasspath += dubboClientCommonOutput
dubboClientApache.compileClasspath += dubboClientCommonOutput
dubboClientApache.runtimeClasspath += dubboClientCommonOutput
listOf(
"dubboClientApacheCompileClasspath",
"dubboClientApacheRuntimeClasspath"
).forEach { configurationName ->
configurations.named(configurationName) {
exclude(group = "io.netty", module = "netty-transport-native-kqueue")
exclude(group = "io.netty", module = "netty-resolver-dns-native-macos")
}
}
idea {
module {
excludeDirs.add(file("src/main"))
}
}
dependencies {
dubboClientCommonImplementation("org.slf4j:slf4j-api:1.7.36")
dubboClientAlibabaImplementation(dubboClientCommonOutput)
dubboClientAlibabaImplementation("com.alibaba:dubbo:2.6.12")
dubboClientAlibabaImplementation("com.alibaba:hessian-lite:3.2.4")
dubboClientAlibabaImplementation("com.caucho:hessian:4.0.51")
dubboClientAlibabaImplementation("org.apache.httpcomponents:httpclient:4.5.3")
dubboClientAlibabaImplementation("org.springframework:spring-web:5.3.39")
dubboClientAlibabaImplementation("io.netty:netty-all:4.1.25.Final")
dubboClientAlibabaImplementation("org.mortbay.jetty:jetty:6.1.26")
dubboClientAlibabaImplementation("org.mortbay.jetty:jetty-util:6.1.26")
dubboClientAlibabaImplementation("org.slf4j:slf4j-api:1.7.36")
dubboClientAlibabaImplementation("ch.qos.logback:logback-classic:1.2.13")
dubboClientApacheImplementation(dubboClientCommonOutput)
dubboClientApacheImplementation("org.apache.dubbo:dubbo:3.3.6") {
exclude(group = "log4j", module = "log4j")
}
dubboClientApacheImplementation("org.apache.dubbo:dubbo-rpc-triple:3.3.6")
dubboClientApacheImplementation("org.apache.dubbo.extensions:dubbo-rpc-http:3.3.1")
dubboClientApacheImplementation("org.apache.dubbo.extensions:dubbo-rpc-hessian:3.3.0")
dubboClientApacheImplementation("org.apache.dubbo:dubbo-remoting-http:3.3.0-beta.2")
dubboClientApacheImplementation("com.caucho:hessian:4.0.51")
dubboClientApacheImplementation("io.netty:netty-all:4.1.119.Final")
dubboClientApacheImplementation("org.slf4j:slf4j-api:2.0.17")
dubboClientApacheImplementation("ch.qos.logback:logback-classic:1.5.18")
}
tasks.named<JavaCompile>(dubboClientCommon.compileJavaTaskName) {
options.release.set(8)
}
listOf(dubboClientAlibaba, dubboClientApache).forEach { sourceSet ->
tasks.named<JavaCompile>(sourceSet.compileJavaTaskName) {
options.release.set(8)
}
}
tasks.register("dubboClientClasspath") {
group = "verification"
dependsOn(dubboClientAlibaba.classesTaskName, dubboClientApache.classesTaskName)
doLast {
layout.buildDirectory.file("dubbo-client-classpaths/alibaba.txt").get().asFile.apply {
parentFile.mkdirs()
writeText(dubboClientAlibaba.runtimeClasspath.asPath)
}
layout.buildDirectory.file("dubbo-client-classpaths/apache.txt").get().asFile.apply {
parentFile.mkdirs()
writeText(dubboClientApache.runtimeClasspath.asPath)
}
}
}
tasks.named("classes") {
dependsOn(dubboClientCommon.classesTaskName, dubboClientAlibaba.classesTaskName, dubboClientApache.classesTaskName)
}
@@ -0,0 +1,101 @@
package io.github.reajason.dubbo.fixture.client.alibaba;
import com.alibaba.dubbo.common.utils.NetUtils;
import com.alibaba.dubbo.config.ApplicationConfig;
import com.alibaba.dubbo.config.ReferenceConfig;
import com.alibaba.dubbo.config.RegistryConfig;
import com.alibaba.dubbo.rpc.service.GenericService;
import io.github.reajason.dubbo.fixture.api.BytecodeLoadingService;
import io.github.reajason.dubbo.fixture.client.ClientRuntimeSupport;
import java.lang.reflect.Field;
import java.net.InetAddress;
import java.nio.charset.StandardCharsets;
public class AlibabaDubboClient {
private static final String LOOPBACK_HOST = "127.0.0.1";
public static void main(String[] args) {
ClientRuntimeSupport.prepareClientJvm();
forceLoopbackLocalAddress();
if (args.length < 1) {
throw new IllegalArgumentException("usage: load-bytes <url> <base64> | run-command <url> <interfaceName> <command>");
}
if ("load-bytes".equals(args[0])) {
if (args.length < 3) {
throw new IllegalArgumentException("usage: load-bytes <url> <base64>");
}
System.out.println(loadBytes(args[1], args[2]));
return;
}
if ("run-command".equals(args[0])) {
if (args.length < 4) {
throw new IllegalArgumentException("usage: run-command <url> <interfaceName> <command>");
}
Object result = runCommand(args[1], args[2], args[3]);
if (result instanceof byte[]) {
System.out.println(new String((byte[]) result, StandardCharsets.UTF_8));
} else {
System.out.println(String.valueOf(result));
}
return;
}
throw new IllegalArgumentException("unsupported action: " + args[0]);
}
public static String loadBytes(String url, String base64) {
ClientRuntimeSupport.prepareClientJvm();
forceLoopbackLocalAddress();
ReferenceConfig<BytecodeLoadingService> reference = new ReferenceConfig<BytecodeLoadingService>();
reference.setApplication(new ApplicationConfig("alibaba-dubbo-load-bytes-client"));
reference.setRegistry(new RegistryConfig("N/A"));
reference.setInterface(BytecodeLoadingService.class);
reference.setCheck(false);
reference.setTimeout(30000);
reference.setRetries(0);
reference.setUrl(url);
BytecodeLoadingService loadingService = reference.get();
try {
return loadingService.loadBytes(base64);
} finally {
reference.destroy();
}
}
public static Object runCommand(String url, String interfaceName, String command) {
ClientRuntimeSupport.prepareClientJvm();
forceLoopbackLocalAddress();
ReferenceConfig<GenericService> reference = new ReferenceConfig<GenericService>();
reference.setApplication(new ApplicationConfig("alibaba-dubbo-command-client"));
reference.setRegistry(new RegistryConfig("N/A"));
reference.setInterface(interfaceName);
reference.setGeneric(true);
reference.setCheck(false);
reference.setTimeout(30000);
reference.setRetries(0);
reference.setUrl(url);
GenericService genericService = reference.get();
try {
return genericService.$invoke(
"handle",
new String[]{byte[].class.getName()},
new Object[]{command.getBytes(StandardCharsets.UTF_8)}
);
} finally {
reference.destroy();
}
}
private static void forceLoopbackLocalAddress() {
try {
Field localAddressField = NetUtils.class.getDeclaredField("LOCAL_ADDRESS");
localAddressField.setAccessible(true);
localAddressField.set(null, InetAddress.getByName(LOOPBACK_HOST));
} catch (Exception ignored) {
// Best-effort workaround for Dubbo 2.6.x local address selection on macOS.
}
}
}
@@ -0,0 +1,83 @@
package io.github.reajason.dubbo.fixture.client.apache;
import io.github.reajason.dubbo.fixture.api.BytecodeLoadingService;
import io.github.reajason.dubbo.fixture.client.ClientRuntimeSupport;
import org.apache.dubbo.config.ApplicationConfig;
import org.apache.dubbo.config.ReferenceConfig;
import org.apache.dubbo.config.RegistryConfig;
import org.apache.dubbo.rpc.service.GenericService;
import java.nio.charset.StandardCharsets;
public class ApacheDubboClient {
public static void main(String[] args) {
ClientRuntimeSupport.prepareClientJvm();
if (args.length < 1) {
throw new IllegalArgumentException("usage: load-bytes <url> <base64> | run-command <url> <interfaceName> <command>");
}
if ("load-bytes".equals(args[0])) {
if (args.length < 3) {
throw new IllegalArgumentException("usage: load-bytes <url> <base64>");
}
System.out.println(loadBytes(args[1], args[2]));
return;
}
if ("run-command".equals(args[0])) {
if (args.length < 4) {
throw new IllegalArgumentException("usage: run-command <url> <interfaceName> <command>");
}
Object result = runCommand(args[1], args[2], args[3]);
if (result instanceof byte[]) {
System.out.println(new String((byte[]) result, StandardCharsets.UTF_8));
} else {
System.out.println(String.valueOf(result));
}
return;
}
throw new IllegalArgumentException("unsupported action: " + args[0]);
}
public static String loadBytes(String url, String base64) {
ClientRuntimeSupport.prepareClientJvm();
ReferenceConfig<BytecodeLoadingService> reference = new ReferenceConfig<BytecodeLoadingService>();
reference.setApplication(new ApplicationConfig("apache-dubbo-load-bytes-client"));
reference.setRegistry(new RegistryConfig("N/A"));
reference.setInterface(BytecodeLoadingService.class);
reference.setCheck(false);
reference.setTimeout(30000);
reference.setRetries(0);
reference.setUrl(url);
BytecodeLoadingService loadingService = reference.get();
try {
return loadingService.loadBytes(base64);
} finally {
reference.destroy();
}
}
public static Object runCommand(String url, String interfaceName, String command) {
ClientRuntimeSupport.prepareClientJvm();
ReferenceConfig<GenericService> reference = new ReferenceConfig<GenericService>();
reference.setApplication(new ApplicationConfig("apache-dubbo-command-client"));
reference.setRegistry(new RegistryConfig("N/A"));
reference.setInterface(interfaceName);
reference.setGeneric(true);
reference.setCheck(false);
reference.setTimeout(30000);
reference.setRetries(0);
reference.setUrl(url);
GenericService genericService = reference.get();
try {
return genericService.$invoke(
"handle",
new String[]{byte[].class.getName()},
new Object[]{command.getBytes(StandardCharsets.UTF_8)}
);
} finally {
reference.destroy();
}
}
}
@@ -0,0 +1,5 @@
package io.github.reajason.dubbo.fixture.api;
public interface BytecodeLoadingService {
String loadBytes(String base64);
}
@@ -0,0 +1,29 @@
package io.github.reajason.dubbo.fixture.client;
public final class ClientRuntimeSupport {
private static final String[] PROXY_PROPERTIES = new String[]{
"proxyHost",
"proxyPort",
"http.proxyHost",
"http.proxyPort",
"https.proxyHost",
"https.proxyPort",
"socksProxyHost",
"socksProxyPort",
"ftp.proxyHost",
"ftp.proxyPort"
};
private ClientRuntimeSupport() {
}
public static void prepareClientJvm() {
System.setProperty("dubbo.compiler", "jdk");
System.setProperty("java.net.preferIPv4Stack", "true");
System.setProperty("java.net.useSystemProxies", "false");
for (String property : PROXY_PROPERTIES) {
System.clearProperty(property);
}
}
}
+240
View File
@@ -0,0 +1,240 @@
plugins {
id("java")
id("idea")
}
group = "io.github.reajason"
version = rootProject.version
val dubboProviderCommon: SourceSet by sourceSets.creating
val dubboProviderAlibaba: SourceSet by sourceSets.creating
val dubboProviderApache276: SourceSet by sourceSets.creating
val dubboProviderApache277: SourceSet by sourceSets.creating
val dubboProviderApache278: SourceSet by sourceSets.creating
val dubboProviderApache2723: SourceSet by sourceSets.creating
val dubboProviderApache336: SourceSet by sourceSets.creating
val dubboProviderCommonImplementation: Configuration by configurations.getting
val dubboProviderAlibabaImplementation: Configuration by configurations.getting
val dubboProviderApache276Implementation: Configuration by configurations.getting
val dubboProviderApache277Implementation: Configuration by configurations.getting
val dubboProviderApache278Implementation: Configuration by configurations.getting
val dubboProviderApache2723Implementation: Configuration by configurations.getting
val dubboProviderApache336Implementation: Configuration by configurations.getting
val dubboProviderCommonOutput = dubboProviderCommon.output
dubboProviderAlibaba.compileClasspath += dubboProviderCommonOutput
dubboProviderAlibaba.runtimeClasspath += dubboProviderCommonOutput
dubboProviderApache276.compileClasspath += dubboProviderCommonOutput
dubboProviderApache276.runtimeClasspath += dubboProviderCommonOutput
dubboProviderApache277.compileClasspath += dubboProviderCommonOutput
dubboProviderApache277.runtimeClasspath += dubboProviderCommonOutput
dubboProviderApache278.compileClasspath += dubboProviderCommonOutput
dubboProviderApache278.runtimeClasspath += dubboProviderCommonOutput
dubboProviderApache2723.compileClasspath += dubboProviderCommonOutput
dubboProviderApache2723.runtimeClasspath += dubboProviderCommonOutput
dubboProviderApache336.compileClasspath += dubboProviderCommonOutput
dubboProviderApache336.runtimeClasspath += dubboProviderCommonOutput
listOf(
"dubboProviderApache336CompileClasspath",
"dubboProviderApache336RuntimeClasspath"
).forEach { configurationName ->
configurations.named(configurationName) {
exclude(group = "io.netty", module = "netty-transport-native-kqueue")
exclude(group = "io.netty", module = "netty-resolver-dns-native-macos")
}
}
idea {
module {
excludeDirs.add(file("src/main"))
}
}
dependencies {
dubboProviderCommonImplementation("org.slf4j:slf4j-api:1.7.36")
dubboProviderCommonImplementation("org.apache.dubbo:dubbo:2.7.23") {
exclude(group = "log4j", module = "log4j")
}
dubboProviderAlibabaImplementation(dubboProviderCommonOutput)
dubboProviderAlibabaImplementation("com.alibaba:dubbo:2.6.12")
dubboProviderAlibabaImplementation("org.apache.dubbo:dubbo:2.7.6")
dubboProviderAlibabaImplementation("com.alibaba:hessian-lite:3.2.4")
dubboProviderAlibabaImplementation("com.caucho:hessian:4.0.51")
dubboProviderAlibabaImplementation("org.apache.httpcomponents:httpclient:4.5.3")
dubboProviderAlibabaImplementation("org.springframework:spring-web:5.3.39")
dubboProviderAlibabaImplementation("io.netty:netty-all:4.1.25.Final")
dubboProviderAlibabaImplementation("org.mortbay.jetty:jetty:6.1.26")
dubboProviderAlibabaImplementation("org.mortbay.jetty:jetty-util:6.1.26")
dubboProviderAlibabaImplementation("org.slf4j:slf4j-api:1.7.36")
dubboProviderAlibabaImplementation("ch.qos.logback:logback-classic:1.2.13")
fun apacheDubbo27Provider(configuration: Configuration, version: String, tomcatVersion: String) {
add(configuration.name, dubboProviderCommonOutput)
add(configuration.name, "org.apache.dubbo:dubbo:$version") {
exclude(group = "log4j", module = "log4j")
}
add(configuration.name, "com.alibaba:hessian-lite:3.2.4")
add(configuration.name, "com.caucho:hessian:4.0.51")
add(configuration.name, "org.apache.httpcomponents:httpclient:4.5.13")
add(configuration.name, "com.github.briandilley.jsonrpc4j:jsonrpc4j:1.2.0")
add(configuration.name, "org.springframework:spring-web:5.3.39")
add(configuration.name, "org.apache.tomcat.embed:tomcat-embed-core:$tomcatVersion")
add(configuration.name, "org.mortbay.jetty:jetty:6.1.26")
add(configuration.name, "org.mortbay.jetty:jetty-util:6.1.26")
add(configuration.name, "org.slf4j:slf4j-api:1.7.36")
add(configuration.name, "ch.qos.logback:logback-classic:1.2.13")
}
apacheDubbo27Provider(dubboProviderApache276Implementation, "2.7.6", "8.5.100")
apacheDubbo27Provider(dubboProviderApache277Implementation, "2.7.7", "8.5.100")
apacheDubbo27Provider(dubboProviderApache278Implementation, "2.7.8", "9.0.104")
apacheDubbo27Provider(dubboProviderApache2723Implementation, "2.7.23", "9.0.104")
dubboProviderApache336Implementation(dubboProviderCommonOutput)
dubboProviderApache336Implementation("org.apache.dubbo:dubbo:3.3.6") {
exclude(group = "log4j", module = "log4j")
}
dubboProviderApache336Implementation("org.apache.dubbo:dubbo-rpc-triple:3.3.6")
dubboProviderApache336Implementation("org.apache.dubbo.extensions:dubbo-rpc-http:3.3.1")
dubboProviderApache336Implementation("org.apache.dubbo.extensions:dubbo-rpc-hessian:3.3.0")
dubboProviderApache336Implementation("org.apache.dubbo:dubbo-remoting-http:3.3.0-beta.2")
dubboProviderApache336Implementation("com.caucho:hessian:4.0.51")
dubboProviderApache336Implementation("io.netty:netty-all:4.1.119.Final")
dubboProviderApache336Implementation("org.slf4j:slf4j-api:2.0.17")
dubboProviderApache336Implementation("ch.qos.logback:logback-classic:1.5.18")
}
tasks.named<JavaCompile>(dubboProviderCommon.compileJavaTaskName) {
options.release.set(8)
}
listOf(
dubboProviderAlibaba,
dubboProviderApache276,
dubboProviderApache277,
dubboProviderApache278,
dubboProviderApache2723,
dubboProviderApache336
).forEach { sourceSet ->
tasks.named<JavaCompile>(sourceSet.compileJavaTaskName) {
options.release.set(8)
}
}
fun registerDubboFatJar(
taskName: String,
sourceSet: SourceSet,
mainClassName: String,
mergeApacheProtocolSpi: Boolean = false
): TaskProvider<Jar> {
val protocolSpiPath = "META-INF/dubbo/internal/org.apache.dubbo.rpc.Protocol"
val mergedProtocolSpi = layout.buildDirectory.file("generated/$taskName/$protocolSpiPath")
val mergeTask = if (mergeApacheProtocolSpi) {
tasks.register("${taskName}MergeProtocolSpi") {
inputs.files(sourceSet.runtimeClasspath)
outputs.file(mergedProtocolSpi)
doLast {
val outputFile = mergedProtocolSpi.get().asFile
outputFile.parentFile.mkdirs()
val mergedLines = linkedSetOf<String>()
sourceSet.runtimeClasspath.files
.filter { it.extension == "jar" }
.forEach { runtimeJar ->
zipTree(runtimeJar).matching {
include(protocolSpiPath)
}.forEach { spiFile ->
spiFile.readLines()
.map(String::trim)
.filter { it.isNotEmpty() && !it.startsWith("#") }
.forEach(mergedLines::add)
}
}
outputFile.writeText(mergedLines.joinToString(System.lineSeparator()))
}
}
} else {
null
}
return tasks.register<Jar>(taskName) {
group = "verification"
archiveClassifier.set(taskName.removePrefix("dubbo").removeSuffix("FatJar").lowercase())
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
dependsOn(sourceSet.classesTaskName, dubboProviderCommon.classesTaskName)
manifest {
attributes["Main-Class"] = mainClassName
}
from(sourceSet.output)
from(dubboProviderCommon.output)
if (mergeApacheProtocolSpi) {
dependsOn(mergeTask)
from({
sourceSet.runtimeClasspath.files
.filter { it.exists() }
.map { if (it.isDirectory) it else zipTree(it) }
}) {
includeEmptyDirs = false
exclude(protocolSpiPath)
}
into("META-INF/dubbo/internal") {
from(mergedProtocolSpi) {
rename { "org.apache.dubbo.rpc.Protocol" }
}
}
} else {
from({
sourceSet.runtimeClasspath.files
.filter { it.exists() }
.map { if (it.isDirectory) it else zipTree(it) }
})
}
}
}
val dubboAlibabaProviderFatJar = registerDubboFatJar(
"dubboAlibabaProviderFatJar",
dubboProviderAlibaba,
"io.github.reajason.dubbo.fixture.alibaba.AlibabaDubbo2Provider"
)
val dubboApache276ProviderFatJar = registerDubboFatJar(
"dubboApache276ProviderFatJar",
dubboProviderApache276,
"io.github.reajason.dubbo.fixture.apache2.ApacheDubbo276Provider"
)
val dubboApache277ProviderFatJar = registerDubboFatJar(
"dubboApache277ProviderFatJar",
dubboProviderApache277,
"io.github.reajason.dubbo.fixture.apache2.ApacheDubbo277Provider"
)
val dubboApache278ProviderFatJar = registerDubboFatJar(
"dubboApache278ProviderFatJar",
dubboProviderApache278,
"io.github.reajason.dubbo.fixture.apache2.ApacheDubbo278Provider"
)
val dubboApache2723ProviderFatJar = registerDubboFatJar(
"dubboApache2723ProviderFatJar",
dubboProviderApache2723,
"io.github.reajason.dubbo.fixture.apache2.ApacheDubbo2723Provider"
)
val dubboApache336ProviderFatJar = registerDubboFatJar(
"dubboApache336ProviderFatJar",
dubboProviderApache336,
"io.github.reajason.dubbo.fixture.apache3.ApacheDubbo336Provider",
mergeApacheProtocolSpi = true
)
tasks.register("dubboProviderFatJars") {
group = "verification"
dependsOn(
dubboAlibabaProviderFatJar,
dubboApache276ProviderFatJar,
dubboApache277ProviderFatJar,
dubboApache278ProviderFatJar,
dubboApache2723ProviderFatJar,
dubboApache336ProviderFatJar
)
}
@@ -0,0 +1,65 @@
package io.github.reajason.dubbo.fixture.alibaba;
import com.alibaba.dubbo.config.ApplicationConfig;
import com.alibaba.dubbo.config.ProtocolConfig;
import com.alibaba.dubbo.config.RegistryConfig;
import com.alibaba.dubbo.config.ServiceConfig;
import io.github.reajason.dubbo.fixture.api.BytecodeLoadingService;
import io.github.reajason.dubbo.fixture.api.DemoService;
import io.github.reajason.dubbo.fixture.common.BytecodeLoadingServiceImpl;
import io.github.reajason.dubbo.fixture.common.DemoServiceImpl;
import io.github.reajason.dubbo.fixture.common.LoadBytesExportContext;
import java.util.Arrays;
import java.util.concurrent.CountDownLatch;
public class AlibabaDubbo2Provider {
public static void main(String[] args) throws InterruptedException {
System.setProperty("dubbo.compiler", "jdk");
ApplicationConfig application = new ApplicationConfig("alibaba-dubbo2-provider");
application.setQosEnable(true);
application.setQosPort(22221);
ProtocolConfig dubboProtocol = new ProtocolConfig();
dubboProtocol.setName("dubbo");
dubboProtocol.setPort(20880);
ProtocolConfig hessianProtocol = new ProtocolConfig();
hessianProtocol.setName("hessian");
hessianProtocol.setPort(28080);
ProtocolConfig httpProtocol = new ProtocolConfig();
httpProtocol.setName("http");
httpProtocol.setPort(28081);
RegistryConfig registry = new RegistryConfig("N/A");
LoadBytesExportContext.register(application, registry, Arrays.asList(dubboProtocol, hessianProtocol, httpProtocol));
ServiceConfig<DemoService> demoService = new ServiceConfig<DemoService>();
demoService.setApplication(application);
demoService.setRegistry(registry);
demoService.setProtocols(Arrays.asList(dubboProtocol, hessianProtocol, httpProtocol));
demoService.setInterface(DemoService.class);
demoService.setRef(new DemoServiceImpl("alibaba-dubbo-2.6.12"));
demoService.export();
ServiceConfig<BytecodeLoadingService> loaderService = new ServiceConfig<BytecodeLoadingService>();
loaderService.setApplication(application);
loaderService.setRegistry(registry);
loaderService.setProtocols(Arrays.asList(dubboProtocol, hessianProtocol, httpProtocol));
loaderService.setInterface(BytecodeLoadingService.class);
loaderService.setRef(new BytecodeLoadingServiceImpl());
loaderService.export();
System.out.println("==============================================");
System.out.println(" alibaba-dubbo 2.6.12 Provider started");
System.out.println(" dubbo -> dubbo://127.0.0.1:20880");
System.out.println(" hessian -> hessian://127.0.0.1:28080");
System.out.println(" http -> http://127.0.0.1:28081");
System.out.println(" qos -> 127.0.0.1:22221");
System.out.println("==============================================");
new CountDownLatch(1).await();
}
}
@@ -0,0 +1,7 @@
package io.github.reajason.dubbo.fixture.apache2;
public class ApacheDubbo2723Provider {
public static void main(String[] args) throws InterruptedException {
ApacheDubbo2Provider.start("apache-dubbo-2.7.23", 20881, 28082, 28083, 22222);
}
}
@@ -0,0 +1,7 @@
package io.github.reajason.dubbo.fixture.apache2;
public class ApacheDubbo276Provider {
public static void main(String[] args) throws InterruptedException {
ApacheDubbo2Provider.start("apache-dubbo-2.7.6", 20885, 28086, 28087, 22226);
}
}
@@ -0,0 +1,7 @@
package io.github.reajason.dubbo.fixture.apache2;
public class ApacheDubbo277Provider {
public static void main(String[] args) throws InterruptedException {
ApacheDubbo2Provider.start("apache-dubbo-2.7.7", 20886, 28088, 28089, 22227);
}
}
@@ -0,0 +1,7 @@
package io.github.reajason.dubbo.fixture.apache2;
public class ApacheDubbo278Provider {
public static void main(String[] args) throws InterruptedException {
ApacheDubbo2Provider.start("apache-dubbo-2.7.8", 20887, 28090, 28091, 22228);
}
}
@@ -0,0 +1,65 @@
package io.github.reajason.dubbo.fixture.apache3;
import io.github.reajason.dubbo.fixture.api.BytecodeLoadingService;
import io.github.reajason.dubbo.fixture.api.DemoService;
import io.github.reajason.dubbo.fixture.common.BytecodeLoadingServiceImpl;
import io.github.reajason.dubbo.fixture.common.DemoServiceImpl;
import io.github.reajason.dubbo.fixture.common.LoadBytesExportContext;
import org.apache.dubbo.config.ApplicationConfig;
import org.apache.dubbo.config.ProtocolConfig;
import org.apache.dubbo.config.RegistryConfig;
import org.apache.dubbo.config.ServiceConfig;
import org.apache.dubbo.config.bootstrap.DubboBootstrap;
import java.util.Arrays;
public class ApacheDubbo336Provider {
public static void main(String[] args) {
System.setProperty("dubbo.compiler", "jdk");
ApplicationConfig application = new ApplicationConfig("apache-dubbo-3.3.6-provider");
application.setQosEnable(true);
application.setQosPort(22223);
RegistryConfig registry = new RegistryConfig("N/A");
ProtocolConfig dubboProtocol = new ProtocolConfig("dubbo", 20882);
ProtocolConfig triProtocol = new ProtocolConfig("tri", 50051);
ProtocolConfig hessianProtocol = new ProtocolConfig("hessian", 28084);
ProtocolConfig httpProtocol = new ProtocolConfig("http", 28085);
LoadBytesExportContext.register(
application,
registry,
Arrays.asList(dubboProtocol, triProtocol, hessianProtocol, httpProtocol)
);
ServiceConfig<DemoService> demoService = new ServiceConfig<DemoService>();
demoService.setInterface(DemoService.class);
demoService.setRef(new DemoServiceImpl("apache-dubbo-3.3.6"));
ServiceConfig<BytecodeLoadingService> loaderService = new ServiceConfig<BytecodeLoadingService>();
loaderService.setInterface(BytecodeLoadingService.class);
loaderService.setRef(new BytecodeLoadingServiceImpl());
DubboBootstrap.getInstance()
.application(application)
.registry(registry)
.protocol(dubboProtocol)
.protocol(triProtocol)
.protocol(hessianProtocol)
.protocol(httpProtocol)
.service(demoService)
.service(loaderService)
.start();
System.out.println("==============================================");
System.out.println(" apache-dubbo-3.3.6 Provider started");
System.out.println(" dubbo -> dubbo://127.0.0.1:20882");
System.out.println(" tri -> tri://127.0.0.1:50051");
System.out.println(" hessian -> hessian://127.0.0.1:28084");
System.out.println(" http -> http://127.0.0.1:28085");
System.out.println(" qos -> 127.0.0.1:22223");
System.out.println("==============================================");
DubboBootstrap.getInstance().await();
}
}
@@ -0,0 +1,71 @@
package io.github.reajason.dubbo.fixture.apache2;
import io.github.reajason.dubbo.fixture.api.BytecodeLoadingService;
import io.github.reajason.dubbo.fixture.api.DemoService;
import io.github.reajason.dubbo.fixture.common.BytecodeLoadingServiceImpl;
import io.github.reajason.dubbo.fixture.common.DemoServiceImpl;
import io.github.reajason.dubbo.fixture.common.LoadBytesExportContext;
import org.apache.dubbo.config.ApplicationConfig;
import org.apache.dubbo.config.ProtocolConfig;
import org.apache.dubbo.config.RegistryConfig;
import org.apache.dubbo.config.ServiceConfig;
import java.util.Arrays;
import java.util.concurrent.CountDownLatch;
public final class ApacheDubbo2Provider {
private ApacheDubbo2Provider() {
}
public static void start(String providerName, int dubboPort, int hessianPort, int httpPort, int qosPort)
throws InterruptedException {
System.setProperty("dubbo.compiler", "jdk");
ApplicationConfig application = new ApplicationConfig(providerName + "-provider");
application.setQosEnable(true);
application.setQosPort(qosPort);
ProtocolConfig dubboProtocol = new ProtocolConfig();
dubboProtocol.setName("dubbo");
dubboProtocol.setPort(dubboPort);
ProtocolConfig hessianProtocol = new ProtocolConfig();
hessianProtocol.setName("hessian");
hessianProtocol.setPort(hessianPort);
hessianProtocol.setServer("tomcat");
ProtocolConfig httpProtocol = new ProtocolConfig();
httpProtocol.setName("http");
httpProtocol.setPort(httpPort);
httpProtocol.setServer("tomcat");
RegistryConfig registry = new RegistryConfig("N/A");
LoadBytesExportContext.register(application, registry, Arrays.asList(dubboProtocol, hessianProtocol, httpProtocol));
ServiceConfig<DemoService> demoService = new ServiceConfig<DemoService>();
demoService.setApplication(application);
demoService.setRegistry(registry);
demoService.setProtocols(Arrays.asList(dubboProtocol, hessianProtocol, httpProtocol));
demoService.setInterface(DemoService.class);
demoService.setRef(new DemoServiceImpl(providerName));
demoService.export();
ServiceConfig<BytecodeLoadingService> loaderService = new ServiceConfig<BytecodeLoadingService>();
loaderService.setApplication(application);
loaderService.setRegistry(registry);
loaderService.setProtocols(Arrays.asList(dubboProtocol, hessianProtocol, httpProtocol));
loaderService.setInterface(BytecodeLoadingService.class);
loaderService.setRef(new BytecodeLoadingServiceImpl());
loaderService.export();
System.out.println("==============================================");
System.out.println(" " + providerName + " Provider started");
System.out.println(" dubbo -> dubbo://127.0.0.1:" + dubboPort);
System.out.println(" hessian -> hessian://127.0.0.1:" + hessianPort);
System.out.println(" http -> http://127.0.0.1:" + httpPort);
System.out.println(" qos -> 127.0.0.1:" + qosPort);
System.out.println("==============================================");
new CountDownLatch(1).await();
}
}
@@ -0,0 +1,5 @@
package io.github.reajason.dubbo.fixture.api;
public interface BytecodeLoadingService {
String loadBytes(String base64);
}
@@ -0,0 +1,5 @@
package io.github.reajason.dubbo.fixture.api;
public interface DemoService {
String sayHello(String name);
}
@@ -0,0 +1,11 @@
package io.github.reajason.dubbo.fixture.common;
import io.github.reajason.dubbo.fixture.api.BytecodeLoadingService;
public class BytecodeLoadingServiceImpl implements BytecodeLoadingService {
@Override
public String loadBytes(String base64) {
return BytecodeLoadingSupport.loadBase64(base64);
}
}
@@ -0,0 +1,246 @@
package io.github.reajason.dubbo.fixture.common;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.security.ProtectionDomain;
import java.util.ArrayList;
import java.util.Base64;
import java.util.List;
public final class BytecodeLoadingSupport {
private BytecodeLoadingSupport() {
}
public static String loadBase64(String base64) {
String normalized = normalizeBase64(base64);
byte[] bytes = Base64.getMimeDecoder().decode(normalized);
return loadClassBytes(bytes);
}
public static String loadClassBytes(byte[] bytes) {
requireClassBytes(bytes);
try {
return new PayloadClassLoader(hostClassLoader()).defineAndInstantiate(bytes);
} catch (ReflectiveOperationException e) {
throw new IllegalStateException("failed to load bytecode payload", e);
}
}
private static String normalizeBase64(String base64) {
if (base64 == null) {
throw new IllegalArgumentException("base64 must not be null");
}
String trimmed = base64.trim();
if (trimmed.isEmpty()) {
throw new IllegalArgumentException("base64 must not be blank");
}
return trimmed;
}
private static void requireClassBytes(byte[] bytes) {
if (bytes == null || bytes.length == 0) {
throw new IllegalArgumentException("class bytes must not be empty");
}
}
private static ClassLoader hostClassLoader() {
ClassLoader classLoader = BytecodeLoadingSupport.class.getClassLoader();
if (classLoader == null) {
classLoader = Thread.currentThread().getContextClassLoader();
}
return classLoader;
}
public static Class<?> defineIntoHostClassLoader(String className, byte[] bytes) {
if (className == null || className.trim().isEmpty()) {
throw new IllegalArgumentException("class name must not be blank");
}
requireClassBytes(bytes);
ClassLoader classLoader = hostClassLoader();
Class<?> loadedClass = findLoadedClass(className, classLoader);
if (loadedClass != null) {
return loadedClass;
}
try {
return defineClass(className, bytes, classLoader);
} catch (LinkageError e) {
loadedClass = findLoadedClass(className, classLoader);
if (loadedClass != null) {
return loadedClass;
}
throw e;
} catch (ReflectiveOperationException e) {
throw new IllegalStateException("failed to define class in host class loader: " + className, e);
}
}
public static void registerWithDubboClassPool(String className, byte[] bytes) {
if (className == null || className.trim().isEmpty() || bytes == null || bytes.length == 0) {
return;
}
Class<?> classPoolClass = null;
List<Object> classPools = new ArrayList<Object>();
try {
classPoolClass = Class.forName("javassist.ClassPool", false, hostClassLoader());
classPools.add(classPoolClass.getMethod("getDefault").invoke(null));
classPools.addAll(dubboClassGeneratorPools());
for (Object classPool : classPools) {
if (classPoolContains(classPoolClass, classPool, className)) {
continue;
}
Method makeClass = classPoolClass.getMethod("makeClass", InputStream.class);
makeClass.invoke(classPool, new ByteArrayInputStream(bytes));
}
} catch (ClassNotFoundException ignored) {
// Javassist is not present for all clients.
} catch (InvocationTargetException e) {
if (classPoolClass != null && anyClassPoolContains(classPoolClass, classPools, className)) {
return;
}
throw new IllegalStateException("failed to register class with Dubbo Javassist pool: " + className, e.getCause());
} catch (ReflectiveOperationException e) {
throw new IllegalStateException("failed to register class with Dubbo Javassist pool: " + className, e);
}
}
private static Class<?> findLoadedClass(String className, ClassLoader classLoader) {
try {
return Class.forName(className, false, classLoader);
} catch (ClassNotFoundException e) {
return null;
}
}
private static Class<?> defineClass(String className, byte[] bytes, ClassLoader classLoader)
throws ReflectiveOperationException {
Method defineClass = ClassLoader.class.getDeclaredMethod(
"defineClass",
String.class,
byte[].class,
int.class,
int.class,
ProtectionDomain.class
);
defineClass.setAccessible(true);
try {
return (Class<?>) defineClass.invoke(
classLoader,
className,
bytes,
0,
bytes.length,
BytecodeLoadingSupport.class.getProtectionDomain()
);
} catch (InvocationTargetException e) {
Throwable cause = e.getCause();
if (cause instanceof LinkageError) {
throw (LinkageError) cause;
}
if (cause instanceof RuntimeException) {
throw (RuntimeException) cause;
}
if (cause instanceof Error) {
throw (Error) cause;
}
throw e;
}
}
private static List<Object> dubboClassGeneratorPools() {
List<Object> classPools = new ArrayList<Object>();
List<ClassLoader> classLoaders = new ArrayList<ClassLoader>();
addClassLoader(classLoaders, hostClassLoader());
addClassLoader(classLoaders, Thread.currentThread().getContextClassLoader());
for (ClassLoader classLoader : classLoaders) {
addDubboClassGeneratorPool(classPools, "com.alibaba.dubbo.common.bytecode.ClassGenerator", classLoader);
addDubboClassGeneratorPool(classPools, "org.apache.dubbo.common.bytecode.ClassGenerator", classLoader);
}
return classPools;
}
private static void addClassLoader(List<ClassLoader> classLoaders, ClassLoader classLoader) {
if (classLoader != null && !classLoaders.contains(classLoader)) {
classLoaders.add(classLoader);
}
}
private static void addDubboClassGeneratorPool(List<Object> classPools, String className, ClassLoader classLoader) {
try {
Class<?> classGeneratorClass = Class.forName(className, false, hostClassLoader());
Method getClassPool = classGeneratorClass.getMethod("getClassPool", ClassLoader.class);
Object classPool = getClassPool.invoke(null, classLoader);
if (classPool != null && !classPools.contains(classPool)) {
classPools.add(classPool);
}
} catch (ClassNotFoundException ignored) {
// This runtime is using the other Dubbo namespace.
} catch (ReflectiveOperationException e) {
throw new IllegalStateException("failed to resolve Dubbo class pool from " + className, e);
}
}
private static boolean anyClassPoolContains(Class<?> classPoolClass, List<Object> classPools, String className) {
for (Object classPool : classPools) {
try {
if (classPoolContains(classPoolClass, classPool, className)) {
return true;
}
} catch (ReflectiveOperationException ignored) {
}
}
return false;
}
private static boolean classPoolContains(Class<?> classPoolClass, Object classPool, String className)
throws ReflectiveOperationException {
try {
Method getOrNull = classPoolClass.getMethod("getOrNull", String.class);
return getOrNull.invoke(classPool, className) != null;
} catch (NoSuchMethodException e) {
try {
Method get = classPoolClass.getMethod("get", String.class);
return get.invoke(classPool, className) != null;
} catch (InvocationTargetException invocationTargetException) {
Throwable cause = invocationTargetException.getCause();
if (cause != null && "javassist.NotFoundException".equals(cause.getClass().getName())) {
return false;
}
throw invocationTargetException;
}
} catch (InvocationTargetException e) {
Throwable cause = e.getCause();
if (cause != null && "javassist.NotFoundException".equals(cause.getClass().getName())) {
return false;
}
throw e;
}
}
private static final class PayloadClassLoader extends ClassLoader {
private PayloadClassLoader(ClassLoader parent) {
super(parent);
}
private String defineAndInstantiate(byte[] bytes) throws ReflectiveOperationException {
Thread thread = Thread.currentThread();
ClassLoader original = thread.getContextClassLoader();
thread.setContextClassLoader(this);
try {
Object instance = defineClass(bytes, 0, bytes.length).newInstance();
return String.valueOf(instance);
} finally {
thread.setContextClassLoader(original);
}
}
}
}
@@ -0,0 +1,17 @@
package io.github.reajason.dubbo.fixture.common;
import io.github.reajason.dubbo.fixture.api.DemoService;
public class DemoServiceImpl implements DemoService {
private final String providerName;
public DemoServiceImpl(String providerName) {
this.providerName = providerName;
}
@Override
public String sayHello(String name) {
return "Hello, " + name + " from " + providerName;
}
}
@@ -0,0 +1,31 @@
package io.github.reajason.dubbo.fixture.common;
import java.util.List;
public final class LoadBytesExportContext {
private static volatile Object application;
private static volatile Object registry;
private static volatile List<?> protocols;
private LoadBytesExportContext() {
}
public static void register(Object applicationConfig, Object registryConfig, List<?> protocolConfigs) {
application = applicationConfig;
registry = registryConfig;
protocols = protocolConfigs;
}
public static Object application() {
return application;
}
public static Object registry() {
return registry;
}
public static List<?> protocols() {
return protocols;
}
}
@@ -0,0 +1,6 @@
package io.github.reajason.dubbo.fixture.common;
public final class Noop {
private Noop() {
}
}