Dynamically generate native image config (#11795)

* ADD dubbo-maven-plugin

* add dubbo-maven-plugin module

Signed-off-by: crazyhzm <crazyhzm@gmail.com>

* fix license and compiler error

Signed-off-by: crazyhzm <crazyhzm@gmail.com>

* fix license and ci error

Signed-off-by: crazyhzm <crazyhzm@gmail.com>

* fix ci error

Signed-off-by: crazyhzm <crazyhzm@gmail.com>

* Remove proxy-config, hard code

Signed-off-by: crazyhzm <crazyhzm@gmail.com>

* ignoredModulesInDubboAll

Signed-off-by: crazyhzm <crazyhzm@gmail.com>

* throw exception for generated adaptive class sources

Signed-off-by: crazyhzm <crazyhzm@gmail.com>

* remove useless reflect config

Signed-off-by: crazyhzm <crazyhzm@gmail.com>

* Dynamically generate native image config for graalvm, and remove the original native image config

Signed-off-by: crazyhzm <crazyhzm@gmail.com>

* fix conflict

Signed-off-by: crazyhzm <crazyhzm@gmail.com>

* remove .git.exec.error

Signed-off-by: crazyhzm <crazyhzm@gmail.com>

* remove .git.exec.error

Signed-off-by: crazyhzm <crazyhzm@gmail.com>

* remove useless import

Signed-off-by: crazyhzm <crazyhzm@gmail.com>

---------

Signed-off-by: crazyhzm <crazyhzm@gmail.com>
Co-authored-by: Albumen Kevin <jhq0812@gmail.com>
This commit is contained in:
huazhongming 2023-03-14 11:31:48 +08:00 committed by GitHub
parent e6ee6088bf
commit 69edf4ef8c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
36 changed files with 1827 additions and 3378 deletions

2
.gitignore vendored
View File

@ -48,3 +48,5 @@ dubbo-demo/dubbo-demo-triple/build/*
# global registry center
.tmp
.git.exec.error

View File

@ -95,6 +95,9 @@ header:
- 'dubbo-maven-plugin/src/main/java/org/apache/dubbo/maven/plugin/aot/MatchingGroupIdFilter.java'
- 'dubbo-maven-plugin/src/main/java/org/apache/dubbo/maven/plugin/aot/RunArguments.java'
- 'dubbo-maven-plugin/src/main/java/org/apache/dubbo/maven/plugin/aot/RunProcess.java'
- 'dubbo-native/src/main/java/org/apache/dubbo/aot/generate/BasicJsonWriter.java'
- 'dubbo-native/src/main/java/org/apache/dubbo/aot/generate/ExecutableMode.java'
- 'dubbo-native/src/main/java/org/apache/dubbo/aot/generate/MemberCategory.java'
comment: on-failure

View File

@ -53,6 +53,7 @@ public class ScopeBeanFactory {
private final List<BeanInfo> registeredBeanInfos = new CopyOnWriteArrayList<>();
private InstantiationStrategy instantiationStrategy;
private final AtomicBoolean destroyed = new AtomicBoolean();
private List<Class<?>> registeredClasses = new ArrayList<>();
public ScopeBeanFactory(ScopeBeanFactory parent, ExtensionAccessor extensionAccessor) {
this.parent = parent;
@ -131,6 +132,7 @@ public class ScopeBeanFactory {
}
}
}
registeredClasses.add(type);
return bean;
}
@ -281,4 +283,8 @@ public class ScopeBeanFactory {
this.instance = instance;
}
}
public List<Class<?>> getRegisteredClasses() {
return registeredClasses;
}
}

View File

@ -0,0 +1,17 @@
[
{
"name": "org.apache.zookeeper.ClientCnxnSocketNIO",
"methods": [
{
"name": "<init>",
"parameterTypes": [
"org.apache.zookeeper.client.ZKClientConfig"
]
},
{
"name": "<init>",
"parameterTypes": []
}
]
}
]

View File

@ -18,8 +18,10 @@
#
log4j.rootLogger=error, console
log4j.appender.console=org.apache.log4j.ConsoleAppender
log4j.appender.console.layout=org.apache.log4j.PatternLayout
log4j.appender.console.layout.ConversionPattern=[%-12d{HH\:mm\:ss.SS}] [%p] %l %m%n
###set log levels###
log4j.rootLogger=info, stdout
###output to the console###
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target=System.out
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=[%d{dd/MM/yy HH:mm:ss:SSS z}] %t %5p %c{2}: %m%n

View File

@ -0,0 +1,25 @@
[
{
"name": "org.apace.dubbo.graalvm.demo.DemoService",
"allPublicMethods": true
},
{
"name": "org.apache.dubbo.demo.graalvm.provider.DemoServiceImpl",
"allDeclaredMethods": true
},
{
"name": "org.apache.zookeeper.ClientCnxnSocketNIO",
"methods": [
{
"name": "<init>",
"parameterTypes": [
"org.apache.zookeeper.client.ZKClientConfig"
]
},
{
"name": "<init>",
"parameterTypes": []
}
]
}
]

View File

@ -0,0 +1,13 @@
{
"resources": {
"includes": [
{
"pattern": "\\Qdubbo.properties\\E"
},
{
"pattern": "\\Qlog4j.properties\\E"
}
]
},
"bundles": []
}

View File

@ -17,18 +17,14 @@
package org.apache.dubbo.maven.plugin.aot;
import org.apache.commons.io.FileUtils;
import org.apache.maven.plugins.annotations.LifecyclePhase;
import org.apache.maven.plugins.annotations.Mojo;
import org.apache.maven.plugins.annotations.Parameter;
import org.apache.maven.plugins.annotations.ResolutionScope;
import org.apache.maven.project.MavenProject;
import java.io.File;
import java.io.InputStream;
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
@Mojo(name = "dubbo-process-aot", defaultPhase = LifecyclePhase.PREPARE_PACKAGE, threadSafe = true,
@ -36,7 +32,7 @@ import java.util.List;
requiresDependencyCollection = ResolutionScope.COMPILE_PLUS_RUNTIME)
public class DubboProcessAotMojo extends AbstractAotMojo {
private static final String AOT_PROCESSOR_CLASS_NAME = "org.apache.dubbo.aot.generate.CodeGenerator";
private static final String AOT_PROCESSOR_CLASS_NAME = "org.apache.dubbo.aot.generate.AotProcessor";
/**
* Directory containing the classes and resource files that should be packaged into
@ -83,7 +79,6 @@ public class DubboProcessAotMojo extends AbstractAotMojo {
URL[] classPath = getClassPath().toArray(new URL[0]);
generateAotAssets(classPath, AOT_PROCESSOR_CLASS_NAME, getAotArguments(mainClass));
compileSourceFiles(classPath, this.generatedSources, this.classesDirectory);
copyNativeConfigFile(project);
copyAll(this.generatedResources.toPath(), this.classesDirectory.toPath());
copyAll(this.generatedClasses.toPath(), this.classesDirectory.toPath());
}
@ -104,35 +99,4 @@ public class DubboProcessAotMojo extends AbstractAotMojo {
File[] directories = new File[]{this.classesDirectory, this.generatedClasses};
return getClassPath(directories, new ExcludeTestScopeArtifactFilter());
}
private void copyNativeConfigFile(MavenProject project) {
String[] nativeFiles = {"META-INF/native-image/reflect-config.json",
"META-INF/native-image/jni-config.json",
"META-INF/native-image/proxy-config.json",
"META-INF/native-image/resource-config.json",
"META-INF/native-image/serialization-config.json"};
Arrays.stream(nativeFiles).forEach(nativeFile -> {
InputStream is = this.getClass().getClassLoader().getResourceAsStream(nativeFile);
project.getResources().stream().findFirst().ifPresent(resource -> {
try {
String path = generatedResources + File.separator + "META-INF" + File.separator + "native-image" + File.separator
+ File.separator + this.project.getGroupId() + File.separator + this.project.getArtifactId();
FileUtils.forceMkdir(new File(path));
String[] str = nativeFile.split("/");
File file = new File(path + File.separator + str[str.length - 1]);
if (!file.exists()) {
FileUtils.copyInputStreamToFile(is, file);
getLog().info("Copy native config file:" + file);
} else {
getLog().info("Skip copy config file:" + file);
}
} catch (Throwable ex) {
getLog().error("Copy native config file error:" + ex.getMessage());
}
});
});
}
}

View File

@ -1,169 +0,0 @@
{
"resources": {
"includes": [
{
"pattern": "\\QDENY_CLASS\\E"
},
{
"pattern": "\\QMETA-INF/dubbo/internal/org.apache.dubbo.common.compiler.Compiler\\E"
},
{
"pattern": "\\QMETA-INF/dubbo/internal/org.apache.dubbo.common.config.configcenter.DynamicConfigurationFactory\\E"
},
{
"pattern": "\\QMETA-INF/dubbo/internal/org.apache.dubbo.common.context.ApplicationExt\\E"
},
{
"pattern": "\\QMETA-INF/dubbo/internal/org.apache.dubbo.common.context.ModuleExt\\E"
},
{
"pattern": "\\QMETA-INF/dubbo/internal/org.apache.dubbo.common.extension.ExtensionInjector\\E"
},
{
"pattern": "\\QMETA-INF/dubbo/internal/org.apache.dubbo.common.infra.InfraAdapter\\E"
},
{
"pattern": "\\QMETA-INF/dubbo/internal/org.apache.dubbo.common.logger.LoggerAdapter\\E"
},
{
"pattern": "\\QMETA-INF/dubbo/internal/org.apache.dubbo.common.serialize.Serialization\\E"
},
{
"pattern": "\\QMETA-INF/dubbo/internal/org.apache.dubbo.common.threadpool.ThreadPool\\E"
},
{
"pattern": "\\QMETA-INF/dubbo/internal/org.apache.dubbo.common.threadpool.manager.ExecutorRepository\\E"
},
{
"pattern": "\\QMETA-INF/dubbo/internal/org.apache.dubbo.common.url.component.param.DynamicParamSource\\E"
},
{
"pattern": "\\QMETA-INF/dubbo/internal/org.apache.dubbo.metadata.MetadataServiceExporter\\E"
},
{
"pattern": "\\QMETA-INF/dubbo/internal/org.apache.dubbo.metadata.ServiceNameMapping\\E"
},
{
"pattern": "\\QMETA-INF/dubbo/internal/org.apache.dubbo.metadata.definition.builder.TypeBuilder\\E"
},
{
"pattern": "\\QMETA-INF/dubbo/internal/org.apache.dubbo.registry.AddressListener\\E"
},
{
"pattern": "\\QMETA-INF/dubbo/internal/org.apache.dubbo.registry.ProviderFirstParams\\E"
},
{
"pattern": "\\QMETA-INF/dubbo/internal/org.apache.dubbo.registry.RegistryFactory\\E"
},
{
"pattern": "\\QMETA-INF/dubbo/internal/org.apache.dubbo.registry.client.RegistryClusterIdentifier\\E"
},
{
"pattern": "\\QMETA-INF/dubbo/internal/org.apache.dubbo.registry.client.ServiceDiscoveryFactory\\E"
},
{
"pattern": "\\QMETA-INF/dubbo/internal/org.apache.dubbo.registry.client.ServiceInstanceCustomizer\\E"
},
{
"pattern": "\\QMETA-INF/dubbo/internal/org.apache.dubbo.registry.client.migration.MigrationAddressComparator\\E"
},
{
"pattern": "\\QMETA-INF/dubbo/internal/org.apache.dubbo.registry.integration.RegistryProtocolListener\\E"
},
{
"pattern": "\\QMETA-INF/dubbo/internal/org.apache.dubbo.remoting.Codec2\\E"
},
{
"pattern": "\\QMETA-INF/dubbo/internal/org.apache.dubbo.remoting.Dispatcher\\E"
},
{
"pattern": "\\QMETA-INF/dubbo/internal/org.apache.dubbo.remoting.Transporter\\E"
},
{
"pattern": "\\QMETA-INF/dubbo/internal/org.apache.dubbo.remoting.exchange.Exchanger\\E"
},
{
"pattern": "\\QMETA-INF/dubbo/internal/org.apache.dubbo.remoting.zookeeper.ZookeeperTransporter\\E"
},
{
"pattern": "\\QMETA-INF/dubbo/internal/org.apache.dubbo.rpc.Filter\\E"
},
{
"pattern": "\\QMETA-INF/dubbo/internal/org.apache.dubbo.rpc.InvokerListener\\E"
},
{
"pattern": "\\QMETA-INF/dubbo/internal/org.apache.dubbo.rpc.Protocol\\E"
},
{
"pattern": "\\QMETA-INF/dubbo/internal/org.apache.dubbo.rpc.ProxyFactory\\E"
},
{
"pattern": "\\QMETA-INF/dubbo/internal/org.apache.dubbo.rpc.cluster.Cluster\\E"
},
{
"pattern": "\\QMETA-INF/dubbo/internal/org.apache.dubbo.rpc.cluster.ConfiguratorFactory\\E"
},
{
"pattern": "\\QMETA-INF/dubbo/internal/org.apache.dubbo.rpc.cluster.LoadBalance\\E"
},
{
"pattern": "\\QMETA-INF/dubbo/internal/org.apache.dubbo.rpc.cluster.ProviderURLMergeProcessor\\E"
},
{
"pattern": "\\QMETA-INF/dubbo/internal/org.apache.dubbo.rpc.cluster.RouterFactory\\E"
},
{
"pattern": "\\QMETA-INF/dubbo/internal/org.apache.dubbo.rpc.cluster.filter.ClusterFilter\\E"
},
{
"pattern": "\\QMETA-INF/dubbo/internal/org.apache.dubbo.rpc.cluster.filter.FilterChainBuilder\\E"
},
{
"pattern": "\\QMETA-INF/dubbo/internal/org.apache.dubbo.rpc.cluster.governance.GovernanceRuleRepository\\E"
},
{
"pattern": "\\QMETA-INF/dubbo/internal/org.apache.dubbo.rpc.cluster.router.state.StateRouterFactory\\E"
},
{
"pattern": "\\QMETA-INF/dubbo/internal/org.apache.dubbo.rpc.model.BuiltinServiceDetector\\E"
},
{
"pattern": "\\QMETA-INF/dubbo/internal/org.apache.dubbo.rpc.model.ScopeModelInitializer\\E"
},
{
"pattern": "\\QMETA-INF/services/org.apache.dubbo.common.extension.LoadingStrategy\\E"
},
{
"pattern": "\\Qdubbo.properties\\E"
},
{
"pattern": "\\Qlog4j.properties\\E"
},
{
"pattern": "\\Qorg/apache/dubbo/common/Version.class\\E"
},
{
"pattern": "\\Qorg/apache/dubbo/remoting/RemotingException.class\\E"
},
{
"pattern": "\\Qorg/apache/dubbo/remoting/Transporters.class\\E"
},
{
"pattern": "\\Qorg/apache/dubbo/remoting/exchange/Exchangers.class\\E"
},
{
"pattern": "\\QMETA-INF/dubbo/internal/org.apache.dubbo.common.store.DataStore\\E"
},
{
"pattern": "\\QMETA-INF/dubbo/internal/org.apache.dubbo.metrics.service.MetricsServiceExporter\\E"
},
{
"pattern": "\\Qsecurity/serialize.blockedlist\\E"
},
{
"pattern": "\\Qsecurity/serialize.allowlist\\E"
}
]
},
"bundles": []
}

View File

@ -0,0 +1,54 @@
/*
* 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.dubbo.aot.generate;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
/**
* generate related self-adaptive code (native image does not support dynamic code generation. Therefore, code needs to be generated before compilation)
*/
public class AotProcessor {
public static void main(String[] args) {
String generatedSources = args[1];
List<Class<?>> classes = ClassSourceScanner.INSTANCE.spiClassesWithAdaptive();
NativeClassSourceWriter.INSTANCE.writeTo(classes, generatedSources);
NativeConfigurationWriter writer = new NativeConfigurationWriter(Paths.get(args[2]), args[4], args[5]);
ResourceConfigMetadataRepository resourceRepository = new ResourceConfigMetadataRepository();
resourceRepository.registerIncludesPatterns(ResourceScanner.INSTANCE.distinctSpiResource().toArray(new String[]{}));
resourceRepository.registerIncludesPatterns(ResourceScanner.INSTANCE.distinctSecurityResource().toArray(new String[]{}));
writer.writeResourceConfig(resourceRepository);
ReflectConfigMetadataRepository reflectRepository = new ReflectConfigMetadataRepository();
reflectRepository
.registerSpiExtensionType(new ArrayList<>(ClassSourceScanner.INSTANCE.distinctSpiExtensionClasses(ResourceScanner.INSTANCE.distinctSpiResource()).values()))
.registerAdaptiveType(new ArrayList<>(ClassSourceScanner.INSTANCE.adaptiveClasses().values()))
.registerBeanType(ClassSourceScanner.INSTANCE.scopeModelInitializer())
.registerConfigType(ClassSourceScanner.INSTANCE.configClasses());
writer.writeReflectionConfig(reflectRepository);
}
}

View File

@ -0,0 +1,288 @@
/*
* Copyright 2002-2023 the original author or authors.
*
* Licensed 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
*
* https://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.dubbo.aot.generate;
import java.io.IOException;
import java.io.Writer;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.function.Consumer;
/**
* Very basic json writer for the purposes of translating runtime hints to native
* configuration.
*
* @author Stephane Nicoll
*/
class BasicJsonWriter {
private final IndentingWriter writer;
/**
* Create a new instance with the specified indent value.
*
* @param writer the writer to use
* @param singleIndent the value of one indent
*/
public BasicJsonWriter(Writer writer, String singleIndent) {
this.writer = new IndentingWriter(writer, singleIndent);
}
/**
* Create a new instance using two whitespaces for the indent.
*
* @param writer the writer to use
*/
public BasicJsonWriter(Writer writer) {
this(writer, " ");
}
/**
* Write an object with the specified attributes. Each attribute is
* written according to its value type:
* <ul>
* <li>Map: write the value as a nested object</li>
* <li>List: write the value as a nested array</li>
* <li>Otherwise, write a single value</li>
* </ul>
*
* @param attributes the attributes of the object
*/
public void writeObject(Map<String, Object> attributes) {
writeObject(attributes, true);
}
/**
* Write an array with the specified items. Each item in the
* list is written either as a nested object or as an attribute
* depending on its type.
*
* @param items the items to write
* @see #writeObject(Map)
*/
public void writeArray(List<?> items) {
writeArray(items, true);
}
private void writeObject(Map<String, Object> attributes, boolean newLine) {
if (attributes.isEmpty()) {
this.writer.print("{ }");
} else {
this.writer.println("{").indented(writeAll(attributes.entrySet().iterator(),
entry -> writeAttribute(entry.getKey(), entry.getValue()))).print("}");
}
if (newLine) {
this.writer.println();
}
}
private void writeArray(List<?> items, boolean newLine) {
if (items.isEmpty()) {
this.writer.print("[ ]");
} else {
this.writer.println("[")
.indented(writeAll(items.iterator(), this::writeValue)).print("]");
}
if (newLine) {
this.writer.println();
}
}
private <T> Runnable writeAll(Iterator<T> it, Consumer<T> writer) {
return () -> {
while (it.hasNext()) {
writer.accept(it.next());
if (it.hasNext()) {
this.writer.println(",");
} else {
this.writer.println();
}
}
};
}
private void writeAttribute(String name, Object value) {
this.writer.print(quote(name) + ": ");
writeValue(value);
}
@SuppressWarnings("unchecked")
private void writeValue(Object value) {
if (value instanceof Map<?, ?>) {
writeObject((Map<String, Object>) value, false);
} else if (value instanceof List<?>) {
writeArray((List<?>) value, false);
} else if (value instanceof CharSequence) {
this.writer.print(quote(escape((CharSequence) value)));
} else if (value instanceof Boolean) {
this.writer.print(Boolean.toString((Boolean) value));
} else {
throw new IllegalStateException("unsupported type: " + value.getClass());
}
}
private String quote(String name) {
return "\"" + name + "\"";
}
private static String escape(CharSequence input) {
StringBuilder builder = new StringBuilder();
input.chars().forEach(c -> {
if (c == '"') {
builder.append("\\\"");
} else if (c == '\\') {
builder.append("\\\\");
} else if (c == '\b') {
builder.append("\\b");
} else if (c == '\f') {
builder.append("\\f");
} else if (c == '\n') {
builder.append("\\n");
} else if (c == '\r') {
builder.append("\\r");
} else if (c == '\t') {
builder.append("\\t");
} else if (c <= 0x1F) {
builder.append(String.format("\\u%04x", c));
} else {
builder.append((char) c);
}
});
return builder.toString();
}
static class IndentingWriter extends Writer {
private final Writer out;
private final String singleIndent;
private int level = 0;
private String currentIndent = "";
private boolean prependIndent = false;
IndentingWriter(Writer out, String singleIndent) {
this.out = out;
this.singleIndent = singleIndent;
}
/**
* Write the specified text.
*
* @param string the content to write
*/
public IndentingWriter print(String string) {
write(string.toCharArray(), 0, string.length());
return this;
}
/**
* Write the specified text and append a new line.
*
* @param string the content to write
*/
public IndentingWriter println(String string) {
write(string.toCharArray(), 0, string.length());
return println();
}
/**
* Write a new line.
*/
public IndentingWriter println() {
String separator = System.lineSeparator();
try {
this.out.write(separator.toCharArray(), 0, separator.length());
} catch (IOException ex) {
throw new IllegalStateException(ex);
}
this.prependIndent = true;
return this;
}
/**
* Increase the indentation level and execute the {@link Runnable}. Decrease the
* indentation level on completion.
*
* @param runnable the code to execute within an extra indentation level
*/
public IndentingWriter indented(Runnable runnable) {
indent();
runnable.run();
return outdent();
}
/**
* Increase the indentation level.
*/
private IndentingWriter indent() {
this.level++;
return refreshIndent();
}
/**
* Decrease the indentation level.
*/
private IndentingWriter outdent() {
this.level--;
return refreshIndent();
}
private IndentingWriter refreshIndent() {
int count = Math.max(0, this.level);
StringBuilder str = new StringBuilder();
for (int i = 0; i < count; i++) {
str.append(this.singleIndent);
}
this.currentIndent = str.toString();
return this;
}
@Override
public void write(char[] chars, int offset, int length) {
try {
if (this.prependIndent) {
this.out.write(this.currentIndent.toCharArray(), 0, this.currentIndent.length());
this.prependIndent = false;
}
this.out.write(chars, offset, length);
} catch (IOException ex) {
throw new IllegalStateException(ex);
}
}
@Override
public void flush() throws IOException {
this.out.flush();
}
@Override
public void close() throws IOException {
this.out.close();
}
}
}

View File

@ -1,92 +0,0 @@
/*
* 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.dubbo.aot.generate;
import java.io.File;
import java.net.JarURLConnection;
import java.net.URL;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Set;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
public class ClassSourceFinder {
public Set<String> findClassSet(String packageName) {
packageName = packageName.replace(".", "/");
try {
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
Enumeration resources = classLoader.getResources(packageName);
Set<String> result = new HashSet<>();
while (resources.hasMoreElements()) {
URL resource = (URL) resources.nextElement();
if (resource != null) {
String protocol = resource.getProtocol();
if ("file".equals(protocol)) {
findClassesByFile(packageName, resource.getPath(), result);
} else if ("jar".equals(protocol)) {
JarFile jar = ((JarURLConnection) resource.openConnection()).getJarFile();
System.out.println("findClassSet jar:" + jar.getName());
findClassesByJar(packageName, jar, result);
}
}
}
return result;
} catch (Throwable ex) {
throw new RuntimeException(ex);
}
}
private void findClassesByFile(String packageName, String resource, Set<String> result) {
File directory = new File(resource);
File[] listFiles = directory.listFiles();
if (listFiles != null) {
for (File file : listFiles) {
if (file.isDirectory()) {
findClassesByFile(packageName, file.getPath(), result);
} else {
String path = file.getPath();
if (path.endsWith(".class")) {
int packageIndex = path.indexOf(packageName.replace("/", File.separator));
String classPath = path.substring(packageIndex, path.length() - 6);
result.add(classPath.replace(File.separator, "."));
}
}
}
}
}
private static void findClassesByJar(String packageName, JarFile jar, Set<String> classes) {
Enumeration<JarEntry> entry = jar.entries();
JarEntry jarEntry;
String name;
while (entry.hasMoreElements()) {
jarEntry = entry.nextElement();
name = jarEntry.getName();
if (name.charAt(0) == '/') {
name = name.substring(1);
}
if (jarEntry.isDirectory() || !name.startsWith(packageName) || !name.endsWith(".class")) {
continue;
}
String className = name.substring(0, name.length() - 6);
classes.add(className.replace("/", "."));
}
}
}

View File

@ -0,0 +1,174 @@
/*
* 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.dubbo.aot.generate;
import org.apache.dubbo.common.extension.Adaptive;
import org.apache.dubbo.common.extension.SPI;
import org.apache.dubbo.common.utils.StringUtils;
import org.apache.dubbo.config.AbstractConfig;
import org.apache.dubbo.rpc.model.FrameworkModel;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
/**
* A scanner for processing and filtering specific types of classes
*/
public class ClassSourceScanner extends JarScanner {
public static final ClassSourceScanner INSTANCE = new ClassSourceScanner();
/**
* Filter out the spi classes with adaptive annotations
* from all the class collections that can be loaded.
* @return All spi classes with adaptive annotations
*/
public List<Class<?>> spiClassesWithAdaptive() {
Map<String, Class<?>> allClasses = getClasses();
List<Class<?>> spiClasses = new ArrayList<>(allClasses.values()).stream().filter(it -> {
if (null == it) {
return false;
}
Annotation anno = it.getAnnotation(SPI.class);
if (null == anno) {
return false;
}
Optional<Method> optional = Arrays.stream(it.getMethods()).filter(it2 -> it2.getAnnotation(Adaptive.class) != null).findAny();
return optional.isPresent();
}).collect(Collectors.toList());
return spiClasses;
}
/**
* The required adaptive class.
* For example: LoadBalance$Adaptive.class
* @return adaptive class
*/
public Map<String, Class<?>> adaptiveClasses() {
List<String> res = spiClassesWithAdaptive().stream().map((c) -> c.getName() + "$Adaptive").collect(Collectors.toList());
return forNames(res);
}
/**
* The required configuration class, which is a subclass of AbstractConfig,
* but which excludes abstract classes.
* @return configuration class
*/
public List<Class<?>> configClasses() {
return getClasses().values().stream().filter(c -> AbstractConfig.class.isAssignableFrom(c) && !Modifier.isAbstract(c.getModifiers())).collect(Collectors.toList());
}
public Map<String, Class<?>> distinctSpiExtensionClasses(Set<String> spiResource) {
Map<String, Class<?>> extensionClasses = new HashMap<>();
spiResource.forEach((fileName) -> {
Enumeration<URL> resources;
try {
resources = ClassLoader.getSystemResources(fileName);
if (resources != null) {
while (resources.hasMoreElements()) {
extensionClasses.putAll(loadResource(resources.nextElement()));
}
}
} catch (IOException e) {
throw new RuntimeException(e);
}
});
return extensionClasses;
}
/**
* Beans that need to be injected in advance in different ScopeModels.
* For example, the RouterSnapshotSwitcher that needs to be injected when ClusterScopeModelInitializer executes initializeFrameworkModel
* @return Beans that need to be injected in advance
*/
public List<Class<?>> scopeModelInitializer(){
List<Class<?>> classes = new ArrayList<>();
classes.addAll(FrameworkModel.defaultModel().getBeanFactory().getRegisteredClasses());
classes.addAll(FrameworkModel.defaultModel().defaultApplication().getBeanFactory().getRegisteredClasses());
classes.addAll(FrameworkModel.defaultModel().defaultApplication().getDefaultModule().getBeanFactory().getRegisteredClasses());
return classes.stream().distinct().collect(Collectors.toList());
}
private Map<String, Class<?>> loadResource(URL resourceUrl) {
Map<String, Class<?>> extensionClasses = new HashMap<>();
try {
List<String> newContentList = getResourceContent(resourceUrl);
String clazz;
for (String line : newContentList) {
try {
int i = line.indexOf('=');
if (i > 0) {
clazz = line.substring(i + 1).trim();
} else {
clazz = line;
}
if (StringUtils.isNotEmpty(clazz)) {
extensionClasses.put(clazz, getClasses().get(clazz));
}
} catch (Throwable t) {
}
}
} catch (Throwable t) {
}
return extensionClasses;
}
private List<String> getResourceContent(URL resourceUrl) throws IOException {
List<String> newContentList = new ArrayList<>();
try (BufferedReader reader = new BufferedReader(
new InputStreamReader(resourceUrl.openStream(), StandardCharsets.UTF_8))) {
String line;
while ((line = reader.readLine()) != null) {
final int ci = line.indexOf('#');
if (ci >= 0) {
line = line.substring(0, ci);
}
line = line.trim();
if (line.length() > 0) {
newContentList.add(line);
}
}
} catch (IOException e) {
throw new RuntimeException(e.getMessage(), e);
}
return newContentList;
}
}

View File

@ -0,0 +1,26 @@
/*
* 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.dubbo.aot.generate;
/**
* A describer that describes the conditions for the configuration to take effect.
*/
public interface ConditionalDescriber {
String getReachableType();
}

View File

@ -0,0 +1,72 @@
/*
* 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.dubbo.aot.generate;
import java.lang.reflect.Constructor;
import java.lang.reflect.Executable;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
/**
* A describer that describes the need for reflection on a {@link Executable}.
*/
public class ExecutableDescriber extends MemberDescriber {
private final List<String> parameterTypes;
private final ExecutableMode mode;
public ExecutableDescriber(Constructor<?> constructor, ExecutableMode mode) {
this("<init>", Arrays.stream(constructor.getParameterTypes()).map(Class::getName).collect(Collectors.toList()),mode);
}
public ExecutableDescriber(String name, List<String> parameterTypes, ExecutableMode mode) {
super(name);
this.parameterTypes = parameterTypes;
this.mode = mode;
}
public List<String> getParameterTypes() {
return parameterTypes;
}
public ExecutableMode getMode() {
return mode;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
ExecutableDescriber that = (ExecutableDescriber) o;
return Objects.equals(parameterTypes, that.parameterTypes) && mode == that.mode;
}
@Override
public int hashCode() {
return Objects.hash(parameterTypes, mode);
}
}

View File

@ -0,0 +1,48 @@
/*
* Copyright 2002-2022 the original author or authors.
*
* Licensed 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
*
* https://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.dubbo.aot.generate;
import java.lang.reflect.Executable;
/**
* Represent the need of reflection for a given {@link Executable}.
*
* @author Stephane Nicoll
*/
public enum ExecutableMode {
/**
* Only retrieving the {@link Executable} and its metadata is required.
*/
INTROSPECT,
/**
* Full reflection support is required, including the ability to invoke
* the {@link Executable}.
*/
INVOKE;
/**
* Specify if this mode already includes the specified {@code other} mode.
* @param other the other mode to check
* @return {@code true} if this mode includes the other mode
*/
boolean includes(ExecutableMode other) {
return (other == null || this.ordinal() >= other.ordinal());
}
}

View File

@ -0,0 +1,38 @@
/*
* 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.dubbo.aot.generate;
import java.lang.reflect.Field;
/**
* A describer that describes the need for reflection on a {@link Field}.
*/
public class FieldDescriber extends MemberDescriber {
protected FieldDescriber(String name) {
super(name);
}
@Override
public int hashCode() {
return super.hashCode();
}
@Override
public boolean equals(Object obj) {
return super.equals(obj);
}
}

View File

@ -0,0 +1,146 @@
/*
* 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.dubbo.aot.generate;
import java.io.File;
import java.net.JarURLConnection;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
/**
* A scanner that scan the dependent jar packages
* to obtain the classes source and resources in them.
*/
public class JarScanner {
private static final String PACKAGE_NAME_PREFIX = "org/apache/dubbo";
private final Map<String, String> classNameCache;
private Map<String, Class<?>> classesCache;
private final List<String> resourcePathCache;
protected Map<String, Class<?>> getClasses() {
if (classesCache == null || classesCache.size() == 0) {
this.classesCache = forNames(classNameCache.values());
}
return classesCache;
}
public JarScanner() {
classNameCache = new HashMap<>();
resourcePathCache = new ArrayList<>();
scanURL(PACKAGE_NAME_PREFIX);
}
protected Map<String, Class<?>> forNames(Collection<String> classNames) {
Map<String, Class<?>> classes = new HashMap<>();
classNames.forEach((it) -> {
try {
Class<?> c = Class.forName(it);
classes.put(it, c);
} catch (Throwable ignored) {
}
});
return classes;
}
private void scanURL(String prefixName) {
try {
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
Enumeration<URL> resources = classLoader.getResources(prefixName);
while (resources.hasMoreElements()) {
URL resource = resources.nextElement();
String protocol = resource.getProtocol();
if ("file".equals(protocol)) {
scanFile(resource.getPath());
} else if ("jar".equals(protocol)) {
JarFile jar = ((JarURLConnection) resource.openConnection()).getJarFile();
scanJar(jar);
}
}
} catch (Throwable ex) {
throw new RuntimeException(ex);
}
}
private void scanFile(String resource) {
File directory = new File(resource);
File[] listFiles = directory.listFiles();
if (listFiles != null) {
for (File file : listFiles) {
System.out.println("scanFile: " + file.getPath());
if (file.isDirectory()) {
scanFile(file.getPath());
} else {
String path = file.getPath();
if (matchedDubboClasses(path)) {
classNameCache.put(path, toClassName(path));
}
}
}
}
}
private void scanJar(JarFile jar) {
Enumeration<JarEntry> entry = jar.entries();
JarEntry jarEntry;
String name;
while (entry.hasMoreElements()) {
jarEntry = entry.nextElement();
name = jarEntry.getName();
if (name.charAt(0) == '/') {
name = name.substring(1);
}
if (jarEntry.isDirectory()) {
continue;
}
if (matchedDubboClasses(name)) {
classNameCache.put(name, toClassName(name));
} else {
resourcePathCache.add(name);
}
}
}
protected List<String> getResourcePath() {
return resourcePathCache;
}
private boolean matchedDubboClasses(String path) {
return path.startsWith(PACKAGE_NAME_PREFIX) && path.endsWith(".class");
}
private String toClassName(String path) {
return path.substring(0, path.length() - 6).replace(File.separator, ".");
}
}

View File

@ -0,0 +1,125 @@
/*
* Copyright 2002-2022 the original author or authors.
*
* Licensed 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
*
* https://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.dubbo.aot.generate;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Member;
import java.lang.reflect.Method;
/**
* Represent predefined {@linkplain Member members} groups.
*
* @author Andy Clement
* @author Sebastien Deleuze
* @author Stephane Nicoll
*/
public enum MemberCategory {
/**
* A category that represents public {@linkplain Field fields}.
* @see Class#getFields()
*/
PUBLIC_FIELDS,
/**
* A category that represents {@linkplain Class#getDeclaredFields() declared
* fields}, that is all fields defined by the class, but not inherited ones.
* @see Class#getDeclaredFields()
*/
DECLARED_FIELDS,
/**
* A category that defines public {@linkplain Constructor constructors} can
* be introspected, but not invoked.
* @see Class#getConstructors()
* @see ExecutableMode#INTROSPECT
*/
INTROSPECT_PUBLIC_CONSTRUCTORS,
/**
* A category that defines {@linkplain Class#getDeclaredConstructors() all
* constructors} can be introspected, but not invoked.
* @see Class#getDeclaredConstructors()
* @see ExecutableMode#INTROSPECT
*/
INTROSPECT_DECLARED_CONSTRUCTORS,
/**
* A category that defines public {@linkplain Constructor constructors} can
* be invoked.
* @see Class#getConstructors()
* @see ExecutableMode#INVOKE
*/
INVOKE_PUBLIC_CONSTRUCTORS,
/**
* A category that defines {@linkplain Class#getDeclaredConstructors() all
* constructors} can be invoked.
* @see Class#getDeclaredConstructors()
* @see ExecutableMode#INVOKE
*/
INVOKE_DECLARED_CONSTRUCTORS,
/**
* A category that defines public {@linkplain Method methods}, including
* inherited ones can be introspect, but not invoked.
* @see Class#getMethods()
* @see ExecutableMode#INTROSPECT
*/
INTROSPECT_PUBLIC_METHODS,
/**
* A category that defines {@linkplain Class#getDeclaredMethods() all
* methods}, excluding inherited ones can be introspected, but not invoked.
* @see Class#getDeclaredMethods()
* @see ExecutableMode#INTROSPECT
*/
INTROSPECT_DECLARED_METHODS,
/**
* A category that defines public {@linkplain Method methods}, including
* inherited ones can be invoked.
* @see Class#getMethods()
* @see ExecutableMode#INVOKE
*/
INVOKE_PUBLIC_METHODS,
/**
* A category that defines {@linkplain Class#getDeclaredMethods() all
* methods}, excluding inherited ones can be invoked.
* @see Class#getDeclaredMethods()
* @see ExecutableMode#INVOKE
*/
INVOKE_DECLARED_METHODS,
/**
* A category that represents public {@linkplain Class#getClasses() inner
* classes}. Contrary to other categories, this does not register any
* particular reflection for them but rather make sure they are available
* via a call to {@link Class#getClasses}.
*/
PUBLIC_CLASSES,
/**
* A category that represents all {@linkplain Class#getDeclaredClasses()
* inner classes}. Contrary to other categories, this does not register any
* particular reflection for them but rather make sure they are available
* via a call to {@link Class#getDeclaredClasses}.
*/
DECLARED_CLASSES;
}

View File

@ -0,0 +1,40 @@
/*
* 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.dubbo.aot.generate;
import java.lang.reflect.Member;
/**
* Base describer that describes the need for reflection on a {@link Member}.
*
*/
public class MemberDescriber {
private final String name;
protected MemberDescriber(String name) {
this.name = name;
}
/**
* Return the name of the member.
* @return the name
*/
public String getName() {
return this.name;
}
}

View File

@ -16,54 +16,27 @@
*/
package org.apache.dubbo.aot.generate;
import org.apache.dubbo.common.extension.Adaptive;
import org.apache.commons.io.FileUtils;
import org.apache.dubbo.common.extension.AdaptiveClassCodeGenerator;
import org.apache.dubbo.common.extension.SPI;
import org.apache.dubbo.common.utils.StringUtils;
import org.apache.commons.io.FileUtils;
import java.io.File;
import java.io.IOException;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.nio.charset.Charset;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import java.util.regex.Matcher;
import java.util.stream.Collectors;
/**
* generate related self-adaptive code (native image does not support dynamic code generation. Therefore, code needs to be generated before compilation)
* Write the Adaptive bytecode class dynamically generated.
*/
public class CodeGenerator {
public class NativeClassSourceWriter {
private static final String PACKAGE_NAME_PREFIX = "org.apache.dubbo";
public static final NativeClassSourceWriter INSTANCE = new NativeClassSourceWriter();
public static void main(String[] args) {
String generatedSources = args[1];
List<Class<?>> classes = new ClassSourceFinder().findClassSet(PACKAGE_NAME_PREFIX).stream().map(it -> {
try {
return Class.forName(it);
} catch (Throwable e) {
}
return null;
}).collect(Collectors.toList());
new ArrayList<>(classes).stream().filter(it -> {
if (null == it) {
return false;
}
Annotation anno = it.getAnnotation(SPI.class);
if (null == anno) {
return false;
}
Optional<Method> optional = Arrays.stream(it.getMethods()).filter(it2 -> it2.getAnnotation(Adaptive.class) != null).findAny();
return optional.isPresent();
}).forEach(it -> {
public void writeTo(List<Class<?>> classes, String generatedSources) {
classes.forEach(it -> {
SPI spi = it.getAnnotation(SPI.class);
String value = spi.value();
if (StringUtils.isEmpty(value)) {
@ -76,14 +49,14 @@ public class CodeGenerator {
String dir = Paths.get(file).getParent().toString();
FileUtils.forceMkdir(new File(dir));
code = LICENSED_STR + code + "\n";
FileUtils.write(new File(file + "$Adaptive.java"), code, Charset.defaultCharset());
String fileName = file + "$Adaptive.java";
FileUtils.write(new File(fileName), code, Charset.defaultCharset());
} catch (IOException e) {
throw new IllegalStateException("Failed to generated adaptive class sources", e);
}
});
}
private static final String LICENSED_STR = "/*\n" +
" * Licensed to the Apache Software Foundation (ASF) under one or more\n" +
" * contributor license agreements. See the NOTICE file distributed with\n" +
@ -100,5 +73,4 @@ public class CodeGenerator {
" * See the License for the specific language governing permissions and\n" +
" * limitations under the License.\n" +
" */\n";
}

View File

@ -0,0 +1,85 @@
/*
* 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.dubbo.aot.generate;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Writer;
import java.nio.file.Path;
import java.util.function.Consumer;
/**
* Write Write configuration metadata information in
* {@link ResourceConfigMetadataRepository} and {@link ReflectConfigMetadataRepository}
* as GraalVM native configuration.
* @see <a href="https://www.graalvm.org/latest/reference-manual/native-image/overview/BuildConfiguration/">Native Image Build Configuration</a>
*/
public class NativeConfigurationWriter {
private final Path basePath;
private final String groupId;
private final String artifactId;
public NativeConfigurationWriter(Path basePath, String groupId, String artifactId) {
this.basePath = basePath;
this.groupId = groupId;
this.artifactId = artifactId;
}
protected void writeTo(String fileName, Consumer<BasicJsonWriter> writer) {
try {
File file = createIfNecessary(fileName);
try (FileWriter out = new FileWriter(file)) {
writer.accept(createJsonWriter(out));
}
}
catch (IOException ex) {
throw new IllegalStateException("Failed to write native configuration for " + fileName, ex);
}
}
private File createIfNecessary(String filename) throws IOException {
Path outputDirectory = this.basePath.resolve("META-INF").resolve("native-image");
if (this.groupId != null && this.artifactId != null) {
outputDirectory = outputDirectory.resolve(this.groupId).resolve(this.artifactId);
}
outputDirectory.toFile().mkdirs();
File file = outputDirectory.resolve(filename).toFile();
file.createNewFile();
return file;
}
public void writeReflectionConfig(ReflectConfigMetadataRepository repository) {
writeTo("reflect-config.json", writer ->
ReflectionConfigWriter.INSTANCE.write(writer, repository));
}
public void writeResourceConfig(ResourceConfigMetadataRepository repository) {
writeTo("resource-config.json", writer ->
ResourceConfigWriter.INSTANCE.write(writer, repository));
}
private BasicJsonWriter createJsonWriter(Writer out) {
return new BasicJsonWriter(out);
}
}

View File

@ -0,0 +1,66 @@
/*
* 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.dubbo.aot.generate;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import static org.apache.dubbo.aot.generate.ExecutableMode.INVOKE;
public class ReflectConfigMetadataRepository {
List<TypeDescriber> types;
public ReflectConfigMetadataRepository() {
this.types = new ArrayList<>();
}
public ReflectConfigMetadataRepository registerSpiExtensionType(List<Class<?>> classes) {
types.addAll(classes.stream().map(this::buildTypeDescriberWithConstructor).collect(Collectors.toList()));
return this;
}
public ReflectConfigMetadataRepository registerAdaptiveType(List<Class<?>> classes) {
types.addAll(classes.stream().map(this::buildTypeDescriberWithConstructor).collect(Collectors.toList()));
return this;
}
public ReflectConfigMetadataRepository registerBeanType(List<Class<?>> classes) {
types.addAll(classes.stream().map(this::buildTypeDescriberWithConstructor).collect(Collectors.toList()));
return this;
}
public ReflectConfigMetadataRepository registerConfigType(List<Class<?>> classes) {
types.addAll(classes.stream().map(this::buildTypeDescriberWithConstructor).collect(Collectors.toList()));
return this;
}
private TypeDescriber buildTypeDescriberWithConstructor(Class<?> c) {
Set<ExecutableDescriber> constructors = Arrays.stream(c.getConstructors()).map((constructor) -> new ExecutableDescriber(constructor, INVOKE)).collect(Collectors.toSet());
Set<MemberCategory> memberCategories = new HashSet<>();
memberCategories.add(MemberCategory.INVOKE_PUBLIC_METHODS);
return new TypeDescriber(c.getName(), null, new HashSet<>(), constructors, new HashSet<>(), memberCategories);
}
public List<TypeDescriber> getTypes() {
return types;
}
}

View File

@ -0,0 +1,134 @@
/*
* 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.dubbo.aot.generate;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
/**
* Write {@link ReflectConfigMetadataRepository} to the JSON output expected by the GraalVM
* {@code native-image} compiler, typically named {@code reflect-config.json}
* or {@code jni-config.json}.
*/
public class ReflectionConfigWriter {
public static final ReflectionConfigWriter INSTANCE = new ReflectionConfigWriter();
public void write(BasicJsonWriter writer, ReflectConfigMetadataRepository repository) {
writer.writeArray(repository.getTypes().stream().map(this::toAttributes).collect(Collectors.toList()));
}
private Map<String, Object> toAttributes(TypeDescriber describer) {
Map<String, Object> attributes = new LinkedHashMap<>();
attributes.put("name", describer.getName());
handleCondition(attributes, describer);
handleCategories(attributes, describer.getMemberCategories());
handleFields(attributes, describer.getFields());
handleExecutables(attributes, describer.getConstructors());
handleExecutables(attributes, describer.getMethods());
return attributes;
}
private void handleCondition(Map<String, Object> attributes, TypeDescriber describer) {
if (describer.getReachableType() != null) {
Map<String, Object> conditionAttributes = new LinkedHashMap<>();
conditionAttributes.put("typeReachable", describer.getReachableType());
attributes.put("condition", conditionAttributes);
}
}
private void handleFields(Map<String, Object> attributes, Set<FieldDescriber> fieldDescribers) {
addIfNotEmpty(attributes, "fields", fieldDescribers.stream().map(this::toAttributes).collect(Collectors.toList()));
}
private Map<String, Object> toAttributes(FieldDescriber describer) {
Map<String, Object> attributes = new LinkedHashMap<>();
attributes.put("name", describer.getName());
return attributes;
}
private void handleExecutables(Map<String, Object> attributes, Set<ExecutableDescriber> executableDescribers) {
addIfNotEmpty(attributes, "methods", executableDescribers.stream()
.filter(h -> h.getMode().equals(ExecutableMode.INVOKE))
.map(this::toAttributes).collect(Collectors.toList()));
addIfNotEmpty(attributes, "queriedMethods", executableDescribers.stream()
.filter(h -> h.getMode().equals(ExecutableMode.INTROSPECT))
.map(this::toAttributes).collect(Collectors.toList()));
}
private Map<String, Object> toAttributes(ExecutableDescriber describer) {
Map<String, Object> attributes = new LinkedHashMap<>();
attributes.put("name", describer.getName());
attributes.put("parameterTypes", describer.getParameterTypes());
return attributes;
}
private void handleCategories(Map<String, Object> attributes, Set<MemberCategory> categories) {
categories.forEach(category -> {
switch (category) {
case PUBLIC_FIELDS:
attributes.put("allPublicFields", true);
break;
case DECLARED_FIELDS:
attributes.put("allDeclaredFields", true);
break;
case INTROSPECT_PUBLIC_CONSTRUCTORS:
attributes.put("queryAllPublicConstructors", true);
break;
case INTROSPECT_DECLARED_CONSTRUCTORS:
attributes.put("queryAllDeclaredConstructors", true);
break;
case INVOKE_PUBLIC_CONSTRUCTORS:
attributes.put("allPublicConstructors", true);
break;
case INVOKE_DECLARED_CONSTRUCTORS:
attributes.put("allDeclaredConstructors", true);
break;
case INTROSPECT_PUBLIC_METHODS:
attributes.put("queryAllPublicMethods", true);
break;
case INTROSPECT_DECLARED_METHODS:
attributes.put("queryAllDeclaredMethods", true);
break;
case INVOKE_PUBLIC_METHODS:
attributes.put("allPublicMethods", true);
break;
case INVOKE_DECLARED_METHODS:
attributes.put("allDeclaredMethods", true);
break;
case PUBLIC_CLASSES:
attributes.put("allPublicClasses", true);
break;
case DECLARED_CLASSES:
attributes.put("allDeclaredClasses", true);
break;
default:
break;
}
}
);
}
private void addIfNotEmpty(Map<String, Object> attributes, String name, Object value) {
if ((value instanceof Collection<?> && ((Collection<?>) value).size() != 0)) {
attributes.put(name, value);
}
}
}

View File

@ -0,0 +1,70 @@
/*
* 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.dubbo.aot.generate;
import java.util.List;
import java.util.Objects;
import java.util.ResourceBundle;
/**
* A describer that describes the need to access a {@link ResourceBundle}.
*/
public class ResourceBundleDescriber implements ConditionalDescriber {
private final String name;
private final List<String> locales;
private final String reachableType;
public ResourceBundleDescriber(String name, List<String> locales, String reachableType) {
this.name = name;
this.locales = locales;
this.reachableType = reachableType;
}
public String getName() {
return name;
}
public List<String> getLocales() {
return locales;
}
@Override
public String getReachableType() {
return reachableType;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
ResourceBundleDescriber that = (ResourceBundleDescriber) o;
return name.equals(that.name) && locales.equals(that.locales) && reachableType.equals(that.reachableType);
}
@Override
public int hashCode() {
return Objects.hash(name, locales, reachableType);
}
}

View File

@ -0,0 +1,72 @@
/*
* 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.dubbo.aot.generate;
import java.util.ArrayList;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
public class ResourceConfigMetadataRepository {
private final List<ResourcePatternDescriber> includes;
private final List<ResourcePatternDescriber> excludes;
private final Set<ResourceBundleDescriber> resourceBundles;
public ResourceConfigMetadataRepository() {
this.includes = new ArrayList<>();
this.excludes = new ArrayList<>();
this.resourceBundles = new LinkedHashSet<>();
}
public ResourceConfigMetadataRepository registerIncludesPatterns(String... patterns) {
for (String pattern : patterns) {
registerIncludesPattern(new ResourcePatternDescriber(pattern,null));
}
return this;
}
public ResourceConfigMetadataRepository registerIncludesPattern(ResourcePatternDescriber describer) {
this.includes.add(describer);
return this;
}
public ResourceConfigMetadataRepository registerExcludesPattern(ResourcePatternDescriber describer) {
this.excludes.add(describer);
return this;
}
public ResourceConfigMetadataRepository registerBundles(ResourceBundleDescriber describer) {
this.resourceBundles.add(describer);
return this;
}
public List<ResourcePatternDescriber> getIncludes() {
return includes;
}
public List<ResourcePatternDescriber> getExcludes() {
return excludes;
}
public Set<ResourceBundleDescriber> getResourceBundles() {
return resourceBundles;
}
}

View File

@ -0,0 +1,87 @@
/*
* 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.dubbo.aot.generate;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
/**
* Write a {@link ResourceConfigMetadataRepository} to the JSON output expected by the GraalVM
* {@code native-image} compiler, typically named {@code resource-config.json}.
*/
public class ResourceConfigWriter {
public static final ResourceConfigWriter INSTANCE = new ResourceConfigWriter();
public void write(BasicJsonWriter writer, ResourceConfigMetadataRepository repository) {
Map<String, Object> attributes = new LinkedHashMap<>();
addIfNotEmpty(attributes, "resources", toAttributes(repository.getIncludes(), repository.getExcludes()));
handleResourceBundles(attributes, repository.getResourceBundles());
writer.writeObject(attributes);
}
private Map<String, Object> toAttributes(List<ResourcePatternDescriber> includes, List<ResourcePatternDescriber> excludes) {
Map<String, Object> attributes = new LinkedHashMap<>();
addIfNotEmpty(attributes, "includes", includes.stream().distinct().map(this::toAttributes).collect(Collectors.toList()));
addIfNotEmpty(attributes, "excludes", excludes.stream().distinct().map(this::toAttributes).collect(Collectors.toList()));
return attributes;
}
private void handleResourceBundles(Map<String, Object> attributes, Set<ResourceBundleDescriber> resourceBundleDescribers) {
addIfNotEmpty(attributes, "bundles", resourceBundleDescribers.stream().map(this::toAttributes).collect(Collectors.toList()));
}
private Map<String, Object> toAttributes(ResourceBundleDescriber describer) {
Map<String, Object> attributes = new LinkedHashMap<>();
handleCondition(attributes, describer);
attributes.put("name", describer.getName());
return attributes;
}
private Map<String, Object> toAttributes(ResourcePatternDescriber describer) {
Map<String, Object> attributes = new LinkedHashMap<>();
handleCondition(attributes, describer);
attributes.put("pattern", describer.toRegex().toString());
return attributes;
}
private void addIfNotEmpty(Map<String, Object> attributes, String name, Object value) {
if (value instanceof Collection<?>) {
if (!((Collection<?>) value).isEmpty()) {
attributes.put(name, value);
}
} else if (value instanceof Map<?, ?>) {
if (!((Map<?, ?>) value).isEmpty()) {
attributes.put(name, value);
}
} else if (value != null) {
attributes.put(name, value);
}
}
private void handleCondition(Map<String, Object> attributes, ConditionalDescriber conditionalDescriber) {
if (conditionalDescriber.getReachableType() != null) {
Map<String, Object> conditionAttributes = new LinkedHashMap<>();
conditionAttributes.put("typeReachable", conditionalDescriber.getReachableType());
attributes.put("condition", conditionAttributes);
}
}
}

View File

@ -0,0 +1,58 @@
/*
* 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.dubbo.aot.generate;
import java.util.Arrays;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
/**
* A describer that describes resources that should be made available at runtime.
*/
public class ResourcePatternDescriber implements ConditionalDescriber {
private final String pattern;
private final String reachableType;
public ResourcePatternDescriber(String pattern, String reachableType) {
this.pattern = pattern;
this.reachableType = reachableType;
}
public String getPattern() {
return pattern;
}
@Override
public String getReachableType() {
return reachableType;
}
public Pattern toRegex() {
String prefix = (this.pattern.startsWith("*") ? ".*" : "");
String suffix = (this.pattern.endsWith("*") ? ".*" : "");
String regex = Arrays.stream(this.pattern.split("\\*"))
.filter(s -> !s.isEmpty())
.map(Pattern::quote)
.collect(Collectors.joining(".*", prefix, suffix));
return Pattern.compile(regex);
}
}

View File

@ -0,0 +1,57 @@
/*
* 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.dubbo.aot.generate;
import java.util.Set;
import java.util.stream.Collectors;
/**
* A scanner for processing and filtering specific resource.
*/
public class ResourceScanner extends JarScanner {
private static final String DUBBO_INTERNAL_RESOURCE_DIRECTORY = "META-INF/dubbo/internal/";
private static final String DUBBO_RESOURCE_DIRECTORY = "META-INF/dubbo/";
private static final String SERVICES_RESOURCE_DIRECTORY = "META-INF/services/";
private static final String SECURITY_RESOURCE_DIRECTORY = "security/";
public static final ResourceScanner INSTANCE = new ResourceScanner();
public Set<String> distinctSpiResource() {
return getResourcePath().stream().distinct().filter(this::matchedSpiResource).collect(Collectors.toSet());
}
public Set<String> distinctSecurityResource() {
return getResourcePath().stream().distinct().filter(this::matchedSecurityResource).collect(Collectors.toSet());
}
private boolean matchedSecurityResource(String path) {
return path.startsWith(SECURITY_RESOURCE_DIRECTORY);
}
private boolean matchedSpiResource(String path) {
return path.startsWith(DUBBO_INTERNAL_RESOURCE_DIRECTORY)
|| path.startsWith(DUBBO_RESOURCE_DIRECTORY)
|| path.startsWith(SERVICES_RESOURCE_DIRECTORY);
}
}

View File

@ -0,0 +1,74 @@
/*
* 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.dubbo.aot.generate;
import java.util.Set;
/**
* A describer that describes the need for reflection on a type.
*/
public class TypeDescriber implements ConditionalDescriber {
private final String name;
private final String reachableType;
private final Set<FieldDescriber> fields;
private final Set<ExecutableDescriber> constructors;
private final Set<ExecutableDescriber> methods;
private final Set<MemberCategory> memberCategories;
public TypeDescriber(String name, String reachableType, Set<FieldDescriber> fields,
Set<ExecutableDescriber> constructors, Set<ExecutableDescriber> methods,
Set<MemberCategory> memberCategories) {
this.name = name;
this.reachableType = reachableType;
this.fields = fields;
this.constructors = constructors;
this.methods = methods;
this.memberCategories = memberCategories;
}
public String getName() {
return name;
}
public Set<MemberCategory> getMemberCategories() {
return memberCategories;
}
public Set<FieldDescriber> getFields() {
return fields;
}
public Set<ExecutableDescriber> getConstructors() {
return constructors;
}
public Set<ExecutableDescriber> getMethods() {
return methods;
}
@Override
public String getReachableType() {
return reachableType;
}
}

View File

@ -0,0 +1,28 @@
/*
* 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.dubbo.aot.generate;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
public class ResourcePatternDescriberTest {
@Test
public void testToRegex() {
ResourcePatternDescriber describer = new ResourcePatternDescriber("META-INF/dubbo/internal/org.apache.dubbo.common.extension.ExtensionInjector", null);
Assertions.assertEquals("\\QMETA-INF/dubbo/internal/org.apache.dubbo.common.extension.ExtensionInjector\\E",describer.toRegex().toString());
}
}

View File

@ -334,6 +334,9 @@
**/org/apache/dubbo/maven/plugin/aot/MatchingGroupIdFilter.java,
**/org/apache/dubbo/maven/plugin/aot/RunArguments.java,
**/org/apache/dubbo/maven/plugin/aot/RunProcess.java,
**/org/apache/dubbo/aot/generate/BasicJsonWriter.java,
**/org/apache/dubbo/aot/generate/ExecutableMode.java,
**/org/apache/dubbo/aot/generate/MemberCategory.java,
**/org/apache/dubbo/common/threadpool/serial/SerializingExecutor.java,
**/org/apache/dubbo/common/threadlocal/InternalThreadLocal.java,
**/org/apache/dubbo/common/threadlocal/InternalThreadLocalMap.java,