Add Dubbo Annotation Processor (Add deprecated method warn when called by user, #10850) (#11513)

This commit is contained in:
Andy Cheung 2023-03-24 09:48:08 +08:00 committed by GitHub
parent 414785c05b
commit 0b6f2e4a8d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
36 changed files with 2228 additions and 7 deletions

View File

@ -98,6 +98,7 @@ header:
- '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'
- 'dubbo-annotation-processor/src/main/java/org/apache/dubbo/annotation/permit/**'
comment: on-failure

2
NOTICE
View File

@ -4,7 +4,7 @@ Copyright 2018-2023 The Apache Software Foundation
This product includes software developed at
The Apache Software Foundation (http://www.apache.org/).
This product contains code form the Netty Project:
This product contains code from the Netty Project:
The Netty Project
=================

View File

@ -0,0 +1,165 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-annotation-processor</artifactId>
<version>1.0.0</version>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<!-- System dependencies may cause problems in checkstyle. -->
<checkstyle.skip>true</checkstyle.skip>
<checkstyle_unix.skip>true</checkstyle_unix.skip>
<rat.skip>true</rat.skip>
<jacoco.skip>true</jacoco.skip>
<maven.deploy.skip>true</maven.deploy.skip>
<junit_jupiter_version>5.9.0</junit_jupiter_version>
<javassist_version>3.29.2-GA</javassist_version>
</properties>
<packaging>jar</packaging>
<dependencies>
<!-- JUnit Jupiter Engine to depend on the JUnit5 engine and JUnit 5 API -->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>${junit_jupiter_version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>${junit_jupiter_version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-params</artifactId>
<version>${junit_jupiter_version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.javassist</groupId>
<artifactId>javassist</artifactId>
<version>${javassist_version}</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.10.1</version>
<configuration>
<proc>none</proc>
<fork>true</fork>
</configuration>
</plugin>
</plugins>
</build>
<profiles>
<profile>
<id>default-profile-in-jdk-8</id>
<activation>
<file>
<exists>${java.home}/../lib/tools.jar</exists>
</file>
<jdk>(, 9)</jdk>
</activation>
<properties>
<toolsjar>${java.home}/../lib/tools.jar</toolsjar>
</properties>
<dependencies>
<dependency>
<groupId>javac</groupId>
<artifactId>javac</artifactId>
<version>1.0</version>
<scope>system</scope>
<systemPath>${toolsjar}</systemPath>
</dependency>
</dependencies>
</profile>
<profile>
<id>legacy-apple-jdk-profile</id>
<activation>
<activeByDefault>false</activeByDefault>
<file>
<exists>${java.home}/../Classes/classes.jar</exists>
</file>
<jdk>(, 9)</jdk>
</activation>
<properties>
<toolsjar>${java.home}/../Classes/classes.jar</toolsjar>
</properties>
<dependencies>
<dependency>
<groupId>javac</groupId>
<artifactId>javac</artifactId>
<version>1.0</version>
<scope>system</scope>
<systemPath>${toolsjar}</systemPath>
</dependency>
</dependencies>
</profile>
<profile>
<id>add-export-greater-than-9</id>
<activation>
<activeByDefault>false</activeByDefault>
<jdk>[9, )</jdk>
</activation>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.10.1</version>
<configuration>
<fork>true</fork>
<proc>none</proc>
</configuration>
</plugin>
</plugins>
</build>
</profile>
</profiles>
</project>

View File

@ -0,0 +1,44 @@
/*
* 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.annotation;
import javax.lang.model.element.Element;
import java.lang.annotation.Annotation;
import java.util.Set;
/**
* Represents an annotation processing handler, which is invoked by DispatchingAnnotationProcessor.
*/
public interface AnnotationProcessingHandler {
/**
* Set the annotations that this handler will handle.
*
* @return annotations to handle
*/
Set<Class<? extends Annotation>> getAnnotationsToHandle();
/**
* Formal processing method.
*
* @param elements the elements that annotated by the annotations of getAnnotationsToHandle()
* @param annotationProcessorContext the annotation processor context object (Javac object encapsulation)
*/
void process(Set<Element> elements,
AnnotationProcessorContext annotationProcessorContext);
}

View File

@ -0,0 +1,139 @@
/*
* 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.annotation;
import com.sun.source.util.Trees;
import com.sun.tools.javac.api.JavacTrees;
import com.sun.tools.javac.processing.JavacProcessingEnvironment;
import com.sun.tools.javac.tree.TreeMaker;
import com.sun.tools.javac.util.Context;
import com.sun.tools.javac.util.Names;
import javax.annotation.processing.ProcessingEnvironment;
import java.lang.reflect.Method;
import java.util.Objects;
/**
* The Context Object of Annotation Processor, which stores objects related to javac.
*/
public class AnnotationProcessorContext {
private JavacProcessingEnvironment javacProcessingEnvironment;
private JavacTrees javacTrees;
private TreeMaker treeMaker;
private Names names;
private Context javacContext;
private Trees trees;
private AnnotationProcessorContext() { }
private static <T> T jbUnwrap(Class<? extends T> iface, T wrapper) {
T unwrapped = null;
try {
final Class<?> apiWrappers = wrapper.getClass().getClassLoader().loadClass("org.jetbrains.jps.javac.APIWrappers");
final Method unwrapMethod = apiWrappers.getDeclaredMethod("unwrap", Class.class, Object.class);
unwrapped = iface.cast(unwrapMethod.invoke(null, iface, wrapper));
} catch (Throwable ignored) {
}
return unwrapped != null ? unwrapped : wrapper;
}
public static AnnotationProcessorContext fromProcessingEnvironment(ProcessingEnvironment processingEnv) {
AnnotationProcessorContext apContext = new AnnotationProcessorContext();
Object procEnvToUnwrap = processingEnv.getClass() == JavacProcessingEnvironment.class ?
processingEnv : jbUnwrap(JavacProcessingEnvironment.class, processingEnv);
JavacProcessingEnvironment jcProcessingEnvironment = (JavacProcessingEnvironment) procEnvToUnwrap;
Context context = jcProcessingEnvironment.getContext();
apContext.javacProcessingEnvironment = jcProcessingEnvironment;
apContext.javacContext = context;
apContext.javacTrees = JavacTrees.instance(jcProcessingEnvironment);
apContext.treeMaker = TreeMaker.instance(context);
apContext.names = Names.instance(context);
apContext.trees = Trees.instance(jcProcessingEnvironment);
return apContext;
}
// Auto-generated methods.
public JavacTrees getJavacTrees() {
return javacTrees;
}
public TreeMaker getTreeMaker() {
return treeMaker;
}
public Names getNames() {
return names;
}
public Context getJavacContext() {
return javacContext;
}
public Trees getTrees() {
return trees;
}
public JavacProcessingEnvironment getJavacProcessingEnvironment() {
return javacProcessingEnvironment;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
AnnotationProcessorContext context = (AnnotationProcessorContext) o;
if (!Objects.equals(javacTrees, context.javacTrees)) return false;
if (!Objects.equals(treeMaker, context.treeMaker)) return false;
if (!Objects.equals(names, context.names)) return false;
if (!Objects.equals(javacContext, context.javacContext))
return false;
return Objects.equals(trees, context.trees);
}
@Override
public int hashCode() {
int result = javacTrees != null ? javacTrees.hashCode() : 0;
result = 31 * result + (treeMaker != null ? treeMaker.hashCode() : 0);
result = 31 * result + (names != null ? names.hashCode() : 0);
result = 31 * result + (javacContext != null ? javacContext.hashCode() : 0);
result = 31 * result + (trees != null ? trees.hashCode() : 0);
return result;
}
@Override
public String toString() {
return "AnnotationProcessorContext{" +
"javacTrees=" + javacTrees +
", treeMaker=" + treeMaker +
", names=" + names +
", javacContext=" + javacContext +
", trees=" + trees +
'}';
}
}

View File

@ -0,0 +1,162 @@
/*
* 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.annotation;
import org.apache.dubbo.annotation.permit.Permit;
import org.apache.dubbo.annotation.util.FileUtils;
import com.sun.tools.javac.code.Symbol;
import com.sun.tools.javac.processing.JavacRoundEnvironment;
import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.ProcessingEnvironment;
import javax.annotation.processing.RoundEnvironment;
import javax.annotation.processing.SupportedAnnotationTypes;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.Element;
import javax.lang.model.element.TypeElement;
import javax.tools.Diagnostic;
import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
/**
* Dispatching annotation processor, which uses a file to locate handlers and invoke them.
*/
@SupportedAnnotationTypes("*")
public class DispatchingAnnotationProcessor extends AbstractProcessor {
private static Set<AnnotationProcessingHandler> handlers;
private static Set<AnnotationProcessingHandler> loadHandlers() {
List<String> classNames = FileUtils.loadConfigurationFileInResources("handlers.cfg");
Set<AnnotationProcessingHandler> tempHandlers = new HashSet<>();
for (String clsName : classNames) {
try {
Class<? extends AnnotationProcessingHandler> cls = (Class<? extends AnnotationProcessingHandler>) Class.forName(clsName);
AnnotationProcessingHandler r = cls.getConstructor().newInstance();
tempHandlers.add(r);
} catch (InstantiationException | NoSuchMethodException | InvocationTargetException |
IllegalAccessException | ClassNotFoundException e) {
throw new RuntimeException(e);
}
}
return Collections.unmodifiableSet(tempHandlers);
}
private AnnotationProcessorContext apContext;
@Override
public synchronized void init(ProcessingEnvironment processingEnv) {
Permit.addOpens();
super.init(processingEnv);
if (processingEnv.getClass().getName().startsWith("org.eclipse.jdt.")) {
// Don't run on ECJ, since this processor is javac based.
processingEnv
.getMessager()
.printMessage(Diagnostic.Kind.WARNING, "The Annotation processor doesn't support ECJ.");
return;
}
if (handlers == null) {
handlers = loadHandlers();
}
apContext = AnnotationProcessorContext.fromProcessingEnvironment(processingEnv);
}
@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
if (processingEnv.getClass().getName().startsWith("org.eclipse.jdt.")) {
// Don't run on ECJ, since this processor is javac based.
return false;
}
for (AnnotationProcessingHandler i : handlers) {
Set<Element> elements = new HashSet<>(16);
for (Class<? extends Annotation> annotationClass : i.getAnnotationsToHandle()) {
elements.addAll(getElementsAnnotatedWith(annotationClass, roundEnv));
}
i.process(elements, apContext);
}
return false;
}
/**
* A hack to make comment only .java file pass the compilation by skipping package symbol scan.
*
* @param annotationClass the annotation class to find
* @param roundEnvironment the javac's round environment
* @return the elements annotated with specified annotation
*/
private Set<? extends Element> getElementsAnnotatedWith(Class<? extends Annotation> annotationClass, RoundEnvironment roundEnvironment) {
TypeElement a = processingEnv.getElementUtils().getTypeElement(annotationClass.getCanonicalName());
// Detect the Javac's version.
if (Integer.parseInt(SourceVersion.latest().name().split("_")[1]) > 8) {
return roundEnvironment.getElementsAnnotatedWith(a);
}
Set<Element> result = Collections.emptySet();
try {
Class annotationSetScannerClass = Class.forName("com.sun.tools.javac.processing.JavacRoundEnvironment$AnnotationSetScanner");
Constructor scannerConstructor = Permit.getConstructor(annotationSetScannerClass, JavacRoundEnvironment.class, Set.class);
Object scanner = scannerConstructor.newInstance(roundEnvironment, result);
Set<? extends Element> rootElements1 = roundEnvironment
.getRootElements()
.stream()
.filter(x -> x.getClass() != Symbol.PackageSymbol.class)
.collect(Collectors.toSet());
for (Element element : rootElements1) {
Method scanningMethod = Permit.getMethod(annotationSetScannerClass, "scan", Element.class, TypeElement.class);
result = (Set<Element>) scanningMethod.invoke(scanner, element, a);
}
} catch (InstantiationException | NoSuchMethodException | ClassNotFoundException | InvocationTargetException |
IllegalAccessException e) {
throw new RuntimeException(e);
}
return result;
}
@Override
public SourceVersion getSupportedSourceVersion() {
return SourceVersion.latestSupported();
}
}

View File

@ -0,0 +1,110 @@
/*
* 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.annotation.handler;
import org.apache.dubbo.annotation.AnnotationProcessingHandler;
import org.apache.dubbo.annotation.AnnotationProcessorContext;
import org.apache.dubbo.annotation.util.ASTUtils;
import com.sun.tools.javac.code.Symbol;
import com.sun.tools.javac.tree.JCTree;
import com.sun.tools.javac.tree.TreeTranslator;
import javax.lang.model.element.Element;
import java.lang.annotation.Annotation;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
/**
* Handles @Deprecated annotation and adds logger warn call to the methods that are annotated with it.
*/
public class DeprecatedHandler implements AnnotationProcessingHandler {
@Override
public Set<Class<? extends Annotation>> getAnnotationsToHandle() {
return new HashSet<>(
Collections.singletonList(Deprecated.class)
);
}
@Override
public void process(Set<Element> elements, AnnotationProcessorContext apContext) {
for (Element element : elements) {
// Only interested in methods.
if (!(element instanceof Symbol.MethodSymbol)) {
continue;
}
Symbol.ClassSymbol classSymbol = (Symbol.ClassSymbol) element.getEnclosingElement();
ASTUtils.addImportStatement(apContext, classSymbol, "org.apache.dubbo.common", "DeprecatedMethodInvocationCounter");
JCTree methodTree = apContext.getJavacTrees().getTree(element);
methodTree.accept(new TreeTranslator() {
@Override
public void visitMethodDef(JCTree.JCMethodDecl jcMethodDecl) {
JCTree.JCBlock block = jcMethodDecl.body;
if (block == null) {
// No method body. (i.e. interface method declaration.)
return;
}
ASTUtils.insertStatementToHeadOfMethod(block, jcMethodDecl, generateCounterStatement(apContext, classSymbol, jcMethodDecl));
}
});
}
}
/**
* Generate an expression statement like this:
* <code>DeprecatedMethodInvocationCounter.onDeprecatedMethodCalled("....");
*
* @param originalMethodDecl the method declaration that will add logger statement
* @param apContext annotation processor context
* @param classSymbol the enclosing class that will be the logger's name
* @return generated expression statement
*/
private JCTree.JCExpressionStatement generateCounterStatement(AnnotationProcessorContext apContext,
Symbol.ClassSymbol classSymbol,
JCTree.JCMethodDecl originalMethodDecl) {
JCTree.JCExpression fullStatement = apContext.getTreeMaker().Apply(
com.sun.tools.javac.util.List.nil(),
apContext.getTreeMaker().Select(
apContext.getTreeMaker().Ident(apContext.getNames().fromString("DeprecatedMethodInvocationCounter")),
apContext.getNames().fromString("onDeprecatedMethodCalled")
),
com.sun.tools.javac.util.List.of(
apContext.getTreeMaker().Literal(getMethodDefinition(classSymbol, originalMethodDecl))
)
);
return apContext.getTreeMaker().Exec(fullStatement);
}
private String getMethodDefinition(Symbol.ClassSymbol classSymbol, JCTree.JCMethodDecl originalMethodDecl) {
return classSymbol.getQualifiedName() + "."
+ originalMethodDecl.name.toString() + "(" + originalMethodDecl.params.toString() + ")";
}
}

View File

@ -0,0 +1,429 @@
/*
* Authored by Project Lombok and licensed by MIT License, which is attached below:
*
* Copyright (C) 2018-2021 The Project Lombok Authors.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package org.apache.dubbo.annotation.permit;
import org.apache.dubbo.annotation.permit.dummy.Parent;
import sun.misc.Unsafe;
import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
// sunapi suppresses javac's warning about using Unsafe; 'all' suppresses eclipse's warning about the unspecified 'sunapi' key. Leave them both.
// Yes, javac's definition of the word 'all' is quite contrary to what the dictionary says it means. 'all' does NOT include 'sunapi' according to javac.
@SuppressWarnings({"sunapi", "all"})
public class Permit {
private Permit() {
}
private static final long ACCESSIBLE_OVERRIDE_FIELD_OFFSET;
private static final IllegalAccessException INIT_ERROR;
private static final sun.misc.Unsafe UNSAFE = (sun.misc.Unsafe) reflectiveStaticFieldAccess(sun.misc.Unsafe.class, "theUnsafe");
static {
Field f;
long g;
Throwable ex;
try {
g = getOverrideFieldOffset();
ex = null;
} catch (Throwable t) {
f = null;
g = -1L;
ex = t;
}
ACCESSIBLE_OVERRIDE_FIELD_OFFSET = g;
if (ex == null) INIT_ERROR = null;
else if (ex instanceof IllegalAccessException) INIT_ERROR = (IllegalAccessException) ex;
else {
INIT_ERROR = new IllegalAccessException("Cannot initialize Unsafe-based permit");
INIT_ERROR.initCause(ex);
}
}
public static <T extends AccessibleObject> T setAccessible(T accessor) {
if (INIT_ERROR == null) {
UNSAFE.putBoolean(accessor, ACCESSIBLE_OVERRIDE_FIELD_OFFSET, true);
} else {
accessor.setAccessible(true);
}
return accessor;
}
private static long getOverrideFieldOffset() throws Throwable {
Field f = null;
Throwable saved = null;
try {
f = AccessibleObject.class.getDeclaredField("override");
} catch (Throwable t) {
saved = t;
}
if (f != null) {
return UNSAFE.objectFieldOffset(f);
}
// The below seems very risky, but for all AccessibleObjects in java today it does work, and starting with JDK12, making the field accessible is no longer possible.
try {
return UNSAFE.objectFieldOffset(Fake.class.getDeclaredField("override"));
} catch (Throwable t) {
throw saved;
}
}
static class Fake {
boolean override;
Object accessCheckCache;
}
public static Method getMethod(Class<?> c, String mName, Class<?>... parameterTypes) throws NoSuchMethodException {
Method m = null;
Class<?> oc = c;
while (c != null) {
try {
m = c.getDeclaredMethod(mName, parameterTypes);
break;
} catch (NoSuchMethodException e) {
}
c = c.getSuperclass();
}
if (m == null) throw new NoSuchMethodException(oc.getName() + " :: " + mName + "(args)");
return setAccessible(m);
}
public static Method permissiveGetMethod(Class<?> c, String mName, Class<?>... parameterTypes) {
try {
return getMethod(c, mName, parameterTypes);
} catch (Exception ignore) {
return null;
}
}
public static Field getField(Class<?> c, String fName) throws NoSuchFieldException {
Field f = null;
Class<?> oc = c;
while (c != null) {
try {
f = c.getDeclaredField(fName);
break;
} catch (NoSuchFieldException e) {
}
c = c.getSuperclass();
}
if (f == null) throw new NoSuchFieldException(oc.getName() + " :: " + fName);
return setAccessible(f);
}
public static Field permissiveGetField(Class<?> c, String fName) {
try {
return getField(c, fName);
} catch (Exception ignore) {
return null;
}
}
public static <T> T permissiveReadField(Class<T> type, Field f, Object instance) {
try {
return type.cast(f.get(instance));
} catch (Exception ignore) {
return null;
}
}
public static <T> Constructor<T> getConstructor(Class<T> c, Class<?>... parameterTypes) throws NoSuchMethodException {
return setAccessible(c.getDeclaredConstructor(parameterTypes));
}
private static Object reflectiveStaticFieldAccess(Class<?> c, String fName) {
try {
Field f = c.getDeclaredField(fName);
f.setAccessible(true);
return f.get(null);
} catch (Exception e) {
return null;
}
}
public static boolean isDebugReflection() {
return !"false".equals(System.getProperty("lombok.debug.reflection", "false"));
}
public static void handleReflectionDebug(Throwable t, Throwable initError) {
if (!isDebugReflection()) return;
System.err.println("** LOMBOK REFLECTION exception: " + t.getClass() + ": " + (t.getMessage() == null ? "(no message)" : t.getMessage()));
t.printStackTrace(System.err);
if (initError != null) {
System.err.println("*** ADDITIONALLY, exception occurred setting up reflection: ");
initError.printStackTrace(System.err);
}
}
public static Object invoke(Method m, Object receiver, Object... args) throws IllegalAccessException, InvocationTargetException {
return invoke(null, m, receiver, args);
}
public static Object invoke(Throwable initError, Method m, Object receiver, Object... args) throws IllegalAccessException, InvocationTargetException {
try {
return m.invoke(receiver, args);
} catch (IllegalAccessException e) {
handleReflectionDebug(e, initError);
throw e;
} catch (RuntimeException e) {
handleReflectionDebug(e, initError);
throw e;
} catch (Error e) {
handleReflectionDebug(e, initError);
throw e;
}
}
public static Object invokeSneaky(Method m, Object receiver, Object... args) {
return invokeSneaky(null, m, receiver, args);
}
public static Object invokeSneaky(Throwable initError, Method m, Object receiver, Object... args) {
try {
return m.invoke(receiver, args);
} catch (NoClassDefFoundError e) {
handleReflectionDebug(e, initError);
//ignore, we don't have access to the correct ECJ classes, so lombok can't possibly
//do anything useful here.
return null;
} catch (NullPointerException e) {
handleReflectionDebug(e, initError);
//ignore, we don't have access to the correct ECJ classes, so lombok can't possibly
//do anything useful here.
return null;
} catch (IllegalAccessException e) {
handleReflectionDebug(e, initError);
throw sneakyThrow(e);
} catch (InvocationTargetException e) {
throw sneakyThrow(e.getCause());
} catch (RuntimeException e) {
handleReflectionDebug(e, initError);
throw e;
} catch (Error e) {
handleReflectionDebug(e, initError);
throw e;
}
}
public static <T> T newInstance(Constructor<T> c, Object... args) throws IllegalAccessException, InvocationTargetException, InstantiationException {
return newInstance(null, c, args);
}
public static <T> T newInstance(Throwable initError, Constructor<T> c, Object... args) throws IllegalAccessException, InvocationTargetException, InstantiationException {
try {
return c.newInstance(args);
} catch (IllegalAccessException e) {
handleReflectionDebug(e, initError);
throw e;
} catch (InstantiationException e) {
handleReflectionDebug(e, initError);
throw e;
} catch (RuntimeException e) {
handleReflectionDebug(e, initError);
throw e;
} catch (Error e) {
handleReflectionDebug(e, initError);
throw e;
}
}
public static <T> T newInstanceSneaky(Constructor<T> c, Object... args) {
return newInstanceSneaky(null, c, args);
}
public static <T> T newInstanceSneaky(Throwable initError, Constructor<T> c, Object... args) {
try {
return c.newInstance(args);
} catch (NoClassDefFoundError e) {
handleReflectionDebug(e, initError);
//ignore, we don't have access to the correct ECJ classes, so lombok can't possibly
//do anything useful here.
return null;
} catch (NullPointerException e) {
handleReflectionDebug(e, initError);
//ignore, we don't have access to the correct ECJ classes, so lombok can't possibly
//do anything useful here.
return null;
} catch (IllegalAccessException e) {
handleReflectionDebug(e, initError);
throw sneakyThrow(e);
} catch (InstantiationException e) {
handleReflectionDebug(e, initError);
throw sneakyThrow(e);
} catch (InvocationTargetException e) {
throw sneakyThrow(e.getCause());
} catch (RuntimeException e) {
handleReflectionDebug(e, initError);
throw e;
} catch (Error e) {
handleReflectionDebug(e, initError);
throw e;
}
}
public static Object get(Field f, Object receiver) throws IllegalAccessException {
try {
return f.get(receiver);
} catch (IllegalAccessException e) {
handleReflectionDebug(e, null);
throw e;
} catch (RuntimeException e) {
handleReflectionDebug(e, null);
throw e;
} catch (Error e) {
handleReflectionDebug(e, null);
throw e;
}
}
public static void set(Field f, Object receiver, Object newValue) throws IllegalAccessException {
try {
f.set(receiver, newValue);
} catch (IllegalAccessException e) {
handleReflectionDebug(e, null);
throw e;
} catch (RuntimeException e) {
handleReflectionDebug(e, null);
throw e;
} catch (Error e) {
handleReflectionDebug(e, null);
throw e;
}
}
public static void reportReflectionProblem(Throwable initError, String msg) {
if (!isDebugReflection()) return;
System.err.println("** LOMBOK REFLECTION issue: " + msg);
if (initError != null) {
System.err.println("*** ADDITIONALLY, exception occurred setting up reflection: ");
initError.printStackTrace(System.err);
}
}
public static RuntimeException sneakyThrow(Throwable t) {
if (t == null) throw new NullPointerException("t");
return Permit.<RuntimeException>sneakyThrow0(t);
}
@SuppressWarnings("unchecked")
private static <T extends Throwable> T sneakyThrow0(Throwable t) throws T {
throw (T) t;
}
private static Object getJdkCompilerModule() {
/* call public api: ModuleLayer.boot().findModule("jdk.compiler").get();
but use reflection because we don't want this code to crash on jdk1.7 and below.
In that case, none of this stuff was needed in the first place, so we just exit via
the catch block and do nothing.
*/
try {
Class<?> cModuleLayer = Class.forName("java.lang.ModuleLayer");
Method mBoot = cModuleLayer.getDeclaredMethod("boot");
Object bootLayer = mBoot.invoke(null);
Class<?> cOptional = Class.forName("java.util.Optional");
Method mFindModule = cModuleLayer.getDeclaredMethod("findModule", String.class);
Object oCompilerO = mFindModule.invoke(bootLayer, "jdk.compiler");
return cOptional.getDeclaredMethod("get").invoke(oCompilerO);
} catch (Exception e) {
return null;
}
}
private static Object getOwnModule() {
try {
Method m = Permit.getMethod(Class.class, "getModule");
return m.invoke(Permit.class);
} catch (Exception e) {
return null;
}
}
private static long getFirstFieldOffset(Unsafe unsafe) {
try {
return unsafe.objectFieldOffset(Parent.class.getDeclaredField("first"));
} catch (NoSuchFieldException e) {
// can't happen.
throw new RuntimeException(e);
} catch (SecurityException e) {
// can't happen
throw new RuntimeException(e);
}
}
public static void addOpens() {
Class<?> cModule;
try {
cModule = Class.forName("java.lang.Module");
} catch (ClassNotFoundException e) {
return; //jdk8-; this is not needed.
}
Unsafe unsafe = UNSAFE;
Object jdkCompilerModule = getJdkCompilerModule();
Object ownModule = getOwnModule();
String[] allPkgs = {
"com.sun.tools.javac.code",
"com.sun.tools.javac.comp",
"com.sun.tools.javac.file",
"com.sun.tools.javac.main",
"com.sun.tools.javac.model",
"com.sun.tools.javac.parser",
"com.sun.tools.javac.processing",
"com.sun.tools.javac.tree",
"com.sun.tools.javac.util",
"com.sun.tools.javac.jvm",
"com.sun.tools.javac.api"
};
try {
Method m = cModule.getDeclaredMethod("implAddOpens", String.class, cModule);
long firstFieldOffset = getFirstFieldOffset(unsafe);
unsafe.putBooleanVolatile(m, firstFieldOffset, true);
for (String p : allPkgs) m.invoke(jdkCompilerModule, p, ownModule);
} catch (Exception ignore) {}
try {
Method m = cModule.getDeclaredMethod("implAddExports", String.class, cModule);
long firstFieldOffset = getFirstFieldOffset(unsafe);
unsafe.putBooleanVolatile(m, firstFieldOffset, true);
for (String p : allPkgs) m.invoke(jdkCompilerModule, p, ownModule);
} catch (Exception ignore) {}
}
}

View File

@ -0,0 +1,33 @@
/*
* Authored by Project Lombok and licensed by MIT License, which is attached below:
*
* Copyright (C) 2018-2021 The Project Lombok Authors.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package org.apache.dubbo.annotation.permit.dummy;
@SuppressWarnings("all")
public abstract class Child extends Parent {
private transient volatile boolean foo;
private transient volatile Object[] bar;
private transient volatile Object baz;
}

View File

@ -0,0 +1,43 @@
/*
* Authored by Project Lombok and licensed by MIT License, which is attached below:
*
* Copyright (C) 2018-2021 The Project Lombok Authors.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package org.apache.dubbo.annotation.permit.dummy;
@SuppressWarnings("all")
public final class GrandChild extends Child {
private Class<?> a;
private int b;
private String c;
private Class<?> d;
private Class<?>[] e;
private Class<?>[] f;
private int g;
private transient String h;
private transient Object i;
private byte[] j;
private byte[] k;
private byte[] l;
private volatile Object m;
private Object n;
}

View File

@ -0,0 +1,36 @@
/*
* Authored by Project Lombok and licensed by MIT License, which is attached below:
*
* Copyright (C) 2018-2021 The Project Lombok Authors.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package org.apache.dubbo.annotation.permit.dummy;
import java.io.OutputStream;
@SuppressWarnings("all")
public class Parent {
boolean first;
static final Object staticObj = OutputStream.class;
volatile Object second;
private static volatile boolean staticSecond;
private static volatile boolean staticThird;
}

View File

@ -0,0 +1,32 @@
/*
* Authored by Project Lombok and licensed by MIT License, which is attached below:
*
* Copyright (C) 2018-2021 The Project Lombok Authors.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
/**
* This package recreates the type hierarchy of {@code java.lang.reflect.AccessibleObject} and friends (such as {@code java.lang.reflect.Method});
* its purpose is to allow us to ask {@code sun.misc.internal.Unsafe} about the exact offset of the {@code override} field of {@code AccessibleObject};
* asking about that field directly doesn't work after jdk14, presumably because the fields of AO are expressly hidden somehow.
* <p>
* NB: It's usually 12, on the vast majority of OS, VM, and architecture combos.
*/
package org.apache.dubbo.annotation.permit.dummy;

View File

@ -0,0 +1,32 @@
/*
* Authored by Project Lombok and licensed by MIT License, which is attached below:
*
* Copyright (C) 2018-2021 The Project Lombok Authors.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
/**
* This is a reduced copy of the nqzero Permit-reflect project.
* https://github.com/nqzero/permit-reflect
* <p>
* Many thanks to nqzero. The permit-reflect project is, like lombok itself, licensed under the MIT license.
* See https://github.com/nqzero/permit-reflect/blob/master/License for license info.
*/
package org.apache.dubbo.annotation.permit;

View File

@ -0,0 +1,111 @@
/*
* 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.annotation.util;
import org.apache.dubbo.annotation.AnnotationProcessorContext;
import com.sun.source.util.TreePath;
import com.sun.tools.javac.code.Symbol;
import com.sun.tools.javac.tree.JCTree;
import com.sun.tools.javac.util.ListBuffer;
import java.util.List;
/**
* Some utils about AST manipulating.
*/
public final class ASTUtils {
private ASTUtils() {
throw new UnsupportedOperationException("No instance of 'ASTUtils' for you! ");
}
public static void addImportStatement(AnnotationProcessorContext apContext,
Symbol.ClassSymbol classSymbol,
String packageName,
String className) {
JCTree.JCImport jcImport = apContext.getTreeMaker().Import(
apContext.getTreeMaker().Select(
apContext.getTreeMaker().Ident(apContext.getNames().fromString(packageName)),
apContext.getNames().fromString(className)
), false);
TreePath treePath = apContext.getTrees().getPath(classSymbol);
TreePath parentPath = treePath.getParentPath();
JCTree.JCCompilationUnit compilationUnit = (JCTree.JCCompilationUnit) parentPath.getCompilationUnit();
List<JCTree.JCImport> imports = compilationUnit.getImports();
if (imports.stream().noneMatch(x -> x.qualid.toString().contains(packageName + "." + className))) {
compilationUnit.accept(new JCTree.Visitor() {
@Override
public void visitTopLevel(JCTree.JCCompilationUnit that) {
List<JCTree> defs = compilationUnit.defs;
ListBuffer<JCTree> newDefs = new ListBuffer<>();
newDefs.add(defs.get(0));
newDefs.add(jcImport);
newDefs.addAll(defs.subList(1, defs.size()));
compilationUnit.defs = newDefs.toList();
}
});
}
}
/**
* Insert statement to head of the method.
*
* @param block the method body
* @param originalMethodDecl the method declaration that will add logger statement
* @param fullExpressionStatement the statement to insert.
*/
public static void insertStatementToHeadOfMethod(JCTree.JCBlock block,
JCTree.JCMethodDecl originalMethodDecl,
JCTree.JCStatement fullExpressionStatement) {
boolean isConstructor = originalMethodDecl.name.toString().equals("<init>");
ListBuffer<JCTree.JCStatement> statements = new ListBuffer<>();
// In constructor, super(...) or this(...) should be the first statement.
if (isConstructor && !block.stats.isEmpty()) {
boolean startsWithSuper = block.stats.get(0).toString().startsWith("super(");
boolean startsWithThis = block.stats.get(0).toString().startsWith("this(");
if (startsWithSuper || startsWithThis) {
statements.add(block.stats.get(0));
statements.add(fullExpressionStatement);
statements.addAll(block.stats.subList(1, block.stats.size()));
} else {
statements.add(fullExpressionStatement);
statements.addAll(block.stats);
}
} else {
statements.add(fullExpressionStatement);
statements.addAll(block.stats);
}
block.stats = statements.toList();
}
}

View File

@ -0,0 +1,155 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.dubbo.annotation.util;
import java.io.File;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.Channels;
import java.nio.channels.FileChannel;
import java.nio.channels.ReadableByteChannel;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.Scanner;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/**
* Utilities of iterating file.
*/
public final class FileUtils {
private static final Pattern WINDOWS_PATH_PATTERN = Pattern.compile("file:/\\w:/.*");
private FileUtils() {
throw new UnsupportedOperationException("No instance of FileUtils for you! ");
}
public static List<Path> getAllClassFilePaths(String rootPath) {
List<Path> targetFolders;
try (Stream<Path> filesStream = Files.walk(Paths.get(rootPath))) {
targetFolders = filesStream.filter(x -> !x.toFile().isFile())
.filter(x -> x.toString().contains("classes") && !x.toString().contains("test-classes"))
.filter(x -> x.toString().contains("\\org\\apache\\dubbo".replace('\\', File.separatorChar)))
.collect(Collectors.toList());
return targetFolders;
} catch (IOException e) {
throw new RuntimeException(e);
}
}
public static byte[] openFileAsByteArray(String filePath) {
try (FileChannel fileChannel = FileChannel.open(Paths.get(filePath))) {
ByteBuffer byteBuffer = ByteBuffer.allocate((int) fileChannel.size());
fileChannel.read(byteBuffer);
return byteBuffer.array();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
public static String openFileAsString(String filePath) {
return new String(openFileAsByteArray(filePath));
}
public static String getSourceFilePathFromClassFilePath(String classFilePath) {
String classesPathString = "\\target\\classes\\".replace("\\", File.separator);
String sourcesPathString = "\\src\\main\\java\\".replace("\\", File.separator);
String sourceFilePathByReplace = classFilePath.replace(classesPathString, sourcesPathString)
.replace(".class", ".java");
// Inner classes.
if (sourceFilePathByReplace.lastIndexOf('$') != -1) {
int dollarCharIndex = sourceFilePathByReplace.lastIndexOf('$');
String outerClassPath = sourceFilePathByReplace.substring(0, dollarCharIndex);
return outerClassPath + ".java";
}
return sourceFilePathByReplace;
}
public static List<String> loadConfigurationFileInResources(String path) {
ReadableByteChannel resourceReadableByteChannel = Channels.newChannel(
Objects.requireNonNull(FileUtils.class.getClassLoader().getResourceAsStream(path)));
List<String> lines = new ArrayList<>();
try (Scanner scanner = new Scanner(resourceReadableByteChannel)) {
while (scanner.hasNextLine()) {
String line = scanner.nextLine().trim();
if (!line.startsWith("#") && !line.isEmpty()) {
lines.add(line);
}
}
return lines;
}
}
/**
* <p>Get absolute path of resource.
*
* <p>Retained for testing. It won't work in JAR.
*
* @param path relative path of resources folder.
* @return absolute path of resource
*/
public static String getResourceFilePath(String path) {
String resourceFilePath = FileUtils.class.getClassLoader().getResource(path).toString();
if (WINDOWS_PATH_PATTERN.matcher(resourceFilePath).matches()) {
resourceFilePath = resourceFilePath.replace("file:/", "");
} else {
resourceFilePath = resourceFilePath.replace("file:", "");
}
return resourceFilePath;
}
public static List<Path> getAllFilesInDirectory(Path targetFolder) {
try (Stream<Path> classFilesStream = Files.walk(targetFolder)) {
return classFilesStream
.filter(x -> x.toFile().isFile())
.collect(Collectors.toList());
} catch (IOException e) {
return Collections.emptyList();
}
}
}

View File

@ -0,0 +1 @@
org.apache.dubbo.annotation.DispatchingAnnotationProcessor

View File

@ -0,0 +1,17 @@
#
# 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.
org.apache.dubbo.annotation.handler.DeprecatedHandler

View File

@ -0,0 +1,64 @@
/*
* 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.annotation;
import org.apache.dubbo.annotation.util.FileUtils;
import org.apache.dubbo.eci.extractor.JavassistUtils;
import javassist.bytecode.ClassFile;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* Real invocation test of DispatchingAnnotationProcessor (and DeprecatedHandler).
*/
class RealInvocationTest {
/**
* [File name, Should the counter exist in the class file? ]
*/
private static final Map<String, Boolean> FILES = new HashMap<>(5, 1);
static {
FILES.put("TestConstructorMethod.java", true);
FILES.put("TestDeprecatedMethod.java", true);
FILES.put("TestInterfaceDeprecatedMethod.java", false);
FILES.put("TestConstructorMethodParentClass.java", false);
FILES.put("TestConstructorMethodSubClass.java", true);
}
@Test
void test() {
for (Map.Entry<String, Boolean> i : FILES.entrySet()) {
String filePath = FileUtils.getResourceFilePath("org/testing/dm/" + i.getKey());
Assertions.assertTrue(TestingCommons.compileTheSource(filePath), "Compile failed! ");
String classFilePath = filePath.replace(".java", ".class");
ClassFile classFile = JavassistUtils.openClassFile(classFilePath);
List<String> stringItems = JavassistUtils.getConstPoolStringItems(classFile.getConstPool());
Assertions.assertEquals(i.getValue(), stringItems.contains("org/apache/dubbo/common/DeprecatedMethodInvocationCounter"));
}
}
}

View File

@ -0,0 +1,71 @@
/*
* 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.annotation;
import javax.tools.JavaCompiler;
import javax.tools.JavaFileObject;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;
import java.nio.charset.StandardCharsets;
import java.util.Collections;
import java.util.Locale;
import static java.util.Arrays.asList;
/**
* Common code of annotation processor testing.
*/
public final class TestingCommons {
private TestingCommons() {
throw new UnsupportedOperationException("No instance of TestingCommons for you! ");
}
private static class ObjectHolders {
static final JavaCompiler javaCompiler = ToolProvider.getSystemJavaCompiler();
static final StandardJavaFileManager javaFileManager = javaCompiler.getStandardFileManager(
null,
Locale.ROOT,
StandardCharsets.UTF_8
);
}
public static boolean compileTheSource(String filePath) {
JavaCompiler.CompilationTask compilationTask = ObjectHolders.javaCompiler.getTask(
null,
ObjectHolders.javaFileManager,
null,
asList("-parameters", "-Xlint:unchecked", "-nowarn", "-Xlint:deprecation"),
null,
getSourceFileJavaFileObject(filePath)
);
compilationTask.setProcessors(
Collections.singletonList(new DispatchingAnnotationProcessor())
);
return compilationTask.call();
}
private static Iterable<? extends JavaFileObject> getSourceFileJavaFileObject(String filePath) {
return ObjectHolders.javaFileManager.getJavaFileObjects(filePath);
}
}

View File

@ -0,0 +1,36 @@
/*
* 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.common;
/**
* Reduced mock of Deprecated method invocation counter.
*/
public final class DeprecatedMethodInvocationCounter {
private DeprecatedMethodInvocationCounter() {
throw new UnsupportedOperationException("No instance of DeprecatedMethodInvocationCounter for you! ");
}
/**
* Invoked by (modified) deprecated method.
*
* @param methodDefinition filled by annotation processor. (like 'org.apache.dubbo.common.URL.getServiceName()')
*/
public static void onDeprecatedMethodCalled(String methodDefinition) {
// Intended to be empty.
}
}

View File

@ -0,0 +1,151 @@
/*
* 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.eci.extractor;
import org.apache.dubbo.annotation.util.FileUtils;
import javassist.bytecode.ClassFile;
import javassist.bytecode.ConstPool;
import java.io.ByteArrayInputStream;
import java.io.DataInputStream;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* Utilities of Javassist.
*/
public class JavassistUtils {
private static final Map<Class, Field> stringFieldCache = new HashMap<>(2, 1);
private static Method getItemMethodCache = null;
private JavassistUtils() {
throw new UnsupportedOperationException("No instance of JavassistUtils for you! ");
}
public static ClassFile openClassFile(String classFilePath) {
try {
byte[] clsB = FileUtils.openFileAsByteArray(classFilePath);
return new ClassFile(new DataInputStream(new ByteArrayInputStream(clsB)));
} catch (IOException e) {
throw new RuntimeException(e);
}
}
static List<Object> getConstPoolItems(ConstPool cp) {
List<Object> objects = new ArrayList<>(cp.getSize());
for (int i = 0; i < cp.getSize(); i++) {
objects.add(getConstPoolItem(cp, i));
}
return objects;
}
/**
* Calls ConstPool.getItem() method reflectively.
*
* @param cp The ConstPool object.
* @param index The index of items.
* @return The XXXInfo Object. Since it's invisible, return Object instead.
*/
static Object getConstPoolItem(ConstPool cp, int index) {
if (getItemMethodCache == null) {
Class<ConstPool> cpc = ConstPool.class;
Method getItemMethod;
try {
getItemMethod = cpc.getDeclaredMethod("getItem", int.class);
getItemMethod.setAccessible(true);
getItemMethodCache = getItemMethod;
} catch (NoSuchMethodException e) {
throw new RuntimeException("Javassist internal method changed.", e);
}
}
try {
return getItemMethodCache.invoke(cp, index);
} catch (IllegalAccessException | InvocationTargetException e) {
throw new RuntimeException("Javassist internal method changed.", e);
}
}
public static List<String> getConstPoolStringItems(ConstPool cp) {
List<Object> objects = getConstPoolItems(cp);
List<String> stringItems = new ArrayList<>(cp.getSize());
for (Object item : objects) {
Field stringField;
if (item != null) {
stringField = getStringFieldInConstPoolItems(item);
if (stringField == null) {
continue;
}
Object fieldData;
try {
fieldData = stringField.get(item);
} catch (IllegalAccessException e) {
throw new RuntimeException("Javassist internal field changed.", e);
}
if (fieldData.getClass() == String.class) {
stringItems.add((String) fieldData);
}
}
}
return stringItems;
}
/**
* Obtain the 'string' field in Utf8Info and StringInfo.
*
* @param item The instance of Utf8Info and StringInfo.
* @return 'string' field's value
*/
static Field getStringFieldInConstPoolItems(Object item) {
if (stringFieldCache.containsKey(item.getClass())) {
return stringFieldCache.get(item.getClass());
} else {
try {
Field stringField = item.getClass().getDeclaredField("string");
stringField.setAccessible(true);
stringFieldCache.put(item.getClass(), stringField);
return stringField;
} catch (NoSuchFieldException ignored) {
}
}
return null;
}
}

View File

@ -0,0 +1,51 @@
/*
* 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.eci.util;
import java.lang.reflect.Field;
/**
* Tools of reflective operations.
*/
public final class ReflectUtils {
private ReflectUtils() {
throw new UnsupportedOperationException("No instance of ReflectUtils for you! ");
}
/**
* Searches (a private) field in super classes recursively.
*
* @param cls the actual type
* @param name the field name
* @return the corresponding Field object, or null if the field does not exist.
*/
public static Field getDeclaredFieldRecursively(Class cls, String name) {
try {
Field indexField = cls.getDeclaredField(name);
indexField.setAccessible(true);
return indexField;
} catch (NoSuchFieldException e) {
if (cls == Object.class) {
return null;
}
return getDeclaredFieldRecursively(cls.getSuperclass(), name);
}
}
}

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.testing.dm;
public class TestConstructorMethod {
@Deprecated
TestConstructorMethod() {
this("1");
}
TestConstructorMethod(String s) {
}
}

View File

@ -0,0 +1,27 @@
/*
* 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.testing.dm;
public class TestConstructorMethodParentClass {
public TestConstructorMethodParentClass() {
}
public TestConstructorMethodParentClass(String s) {
}
}

View File

@ -0,0 +1,30 @@
/*
* 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.testing.dm;
public class TestConstructorMethodSubClass extends TestConstructorMethodParentClass {
@Deprecated
public TestConstructorMethodSubClass(int i) {
super();
}
public TestConstructorMethodSubClass(String s) {
super(s);
}
}

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.testing.dm;
public class TestDeprecatedMethod {
@Deprecated
public void m1() {
System.out.println("M1! ");
}
}

View File

@ -0,0 +1,24 @@
/*
* 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.testing.dm;
public interface TestInterfaceDeprecatedMethod {
@Deprecated
public void m1();
}

View File

@ -13,8 +13,8 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
@ -24,7 +24,7 @@
<packaging>jar</packaging>
<properties>
<maven.deploy.skip>true</maven.deploy.skip>
<maven.deploy.skip>true</maven.deploy.skip>
</properties>
</project>
</project>

View File

@ -0,0 +1,83 @@
/*
* 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.common;
import org.apache.dubbo.common.constants.DeprecatedMethodInvocationCounterConstants;
import org.apache.dubbo.common.logger.ErrorTypeAwareLogger;
import org.apache.dubbo.common.logger.LoggerFactory;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.LongAdder;
/**
* Deprecated method invocation counter, which is used by annotation processor.
* <p>
* If an IDE says it is unused, just ignore it.
*/
public final class DeprecatedMethodInvocationCounter {
private DeprecatedMethodInvocationCounter() {
throw new UnsupportedOperationException("No instance of DeprecatedMethodInvocationCounter for you! ");
}
private static final ConcurrentHashMap<String, LongAdder> COUNTERS = new ConcurrentHashMap<>();
private static final ErrorTypeAwareLogger LOGGER = LoggerFactory.getErrorTypeAwareLogger(DeprecatedMethodInvocationCounter.class);
/**
* Invoked by (modified) deprecated method.
*
* @param methodDefinition filled by annotation processor. (like 'org.apache.dubbo.common.URL.getServiceName()')
*/
public static void onDeprecatedMethodCalled(String methodDefinition) {
if (!hasThisMethodInvoked(methodDefinition)) {
LOGGER.warn(
DeprecatedMethodInvocationCounterConstants.ERROR_CODE,
DeprecatedMethodInvocationCounterConstants.POSSIBLE_CAUSE,
DeprecatedMethodInvocationCounterConstants.EXTENDED_MESSAGE,
DeprecatedMethodInvocationCounterConstants.LOGGER_MESSAGE_PREFIX + methodDefinition
);
}
increaseInvocationCount(methodDefinition);
}
private static void increaseInvocationCount(String methodDefinition) {
COUNTERS.computeIfAbsent(methodDefinition, k -> new LongAdder());
LongAdder adder = COUNTERS.get(methodDefinition);
adder.increment();
}
public static boolean hasThisMethodInvoked(String methodDefinition) {
return COUNTERS.containsKey(methodDefinition);
}
public static Map<String, Integer> getInvocationRecord() {
// Perform a deep-copy to avoid concurrent issues.
HashMap<String, Integer> copyOfCounters = new HashMap<>();
for (Map.Entry<String, LongAdder> entry : COUNTERS.entrySet()) {
copyOfCounters.put(entry.getKey(), entry.getValue().intValue());
}
return Collections.unmodifiableMap(copyOfCounters);
}
}

View File

@ -0,0 +1,35 @@
/*
* 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.common.constants;
/**
* Constants of Deprecated Method Invocation Counter.
*/
public final class DeprecatedMethodInvocationCounterConstants {
private DeprecatedMethodInvocationCounterConstants() {
throw new UnsupportedOperationException("No instance of DeprecatedMethodInvocationCounterConstants for you! ");
}
public static final String ERROR_CODE = LoggerCodeConstants.COMMON_DEPRECATED_METHOD_INVOKED;
public static final String POSSIBLE_CAUSE = "invocation of deprecated method";
public static final String EXTENDED_MESSAGE = "";
public static final String LOGGER_MESSAGE_PREFIX = "Deprecated method invoked. The method is ";
}

View File

@ -92,6 +92,10 @@ public interface LoggerCodeConstants {
String VULNERABILITY_WARNING = "0-28";
/**
* Used in annotation processor to indicate a deprecated method is invoked.
*/
String COMMON_DEPRECATED_METHOD_INVOKED = "0-99";
// Registry module

View File

@ -0,0 +1,59 @@
/*
* 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.common;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Map;
/**
* Tests of DeprecatedMethodInvocationCounter.
*/
class DeprecatedMethodInvocationCounterTest {
private static final String METHOD_DEFINITION = "org.apache.dubbo.common.URL.getServiceName()";
@Test
void testRealInvocation() {
Assertions.assertFalse(DeprecatedMethodInvocationCounter.hasThisMethodInvoked(METHOD_DEFINITION));
// Invoke a deprecated method.
URL url = URL.valueOf("dubbo://admin:hello1234@10.20.130.230:20880/context/path#index?version=1.0.0&id=org.apache.dubbo.config.RegistryConfig#0");
// Not a typo, intentionally invoke twice.
invokeDeprecatedMethod(url);
invokeDeprecatedMethod(url);
Assertions.assertTrue(DeprecatedMethodInvocationCounter.hasThisMethodInvoked(METHOD_DEFINITION));
Map<String, Integer> record = DeprecatedMethodInvocationCounter.getInvocationRecord();
Assertions.assertEquals(2, record.get(METHOD_DEFINITION));
}
private void invokeDeprecatedMethod(URL url) {
try {
Method m = URL.class.getMethod("getServiceName");
m.invoke(url);
} catch (InvocationTargetException | IllegalAccessException | NoSuchMethodException e) {
throw new RuntimeException(e);
}
}
}

View File

@ -226,6 +226,5 @@
<version>1.17.6</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>

View File

@ -161,4 +161,17 @@
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<fork>true</fork>
<proc>none</proc>
</configuration>
</plugin>
</plugins>
</build>
</project>

View File

@ -48,6 +48,7 @@ class FileTest {
ignoredModules.add(Pattern.compile("dubbo-parent"));
ignoredModules.add(Pattern.compile("dubbo-core-spi"));
ignoredModules.add(Pattern.compile("dubbo-demo.*"));
ignoredModules.add(Pattern.compile("dubbo-annotation-processor"));
ignoredModulesInDubboAll.add(Pattern.compile("dubbo"));
ignoredModulesInDubboAll.add(Pattern.compile("dubbo-bom"));
@ -57,6 +58,7 @@ class FileTest {
ignoredModulesInDubboAll.add(Pattern.compile("dubbo-metadata-processor"));
ignoredModulesInDubboAll.add(Pattern.compile("dubbo-native.*"));
ignoredModulesInDubboAll.add(Pattern.compile("dubbo-spring-boot.*"));
ignoredModulesInDubboAll.add(Pattern.compile("dubbo-annotation-processor.*"));
ignoredModulesInDubboAll.add(Pattern.compile("dubbo-maven-plugin"));
}

10
pom.xml
View File

@ -125,6 +125,7 @@
<maven_flatten_version>1.3.0</maven_flatten_version>
<maven_enforce_version>3.2.1</maven_enforce_version>
<maven_antrun_version>3.1.0</maven_antrun_version>
<dubbo_annotation_processor_version>1.0.0</dubbo_annotation_processor_version>
<arguments />
<checkstyle.skip>true</checkstyle.skip>
<checkstyle_unix.skip>true</checkstyle_unix.skip>
@ -134,6 +135,7 @@
</properties>
<modules>
<module>dubbo-annotation-processor</module>
<module>dubbo-common</module>
<module>dubbo-container</module>
<module>dubbo-compiler</module>
@ -177,6 +179,13 @@
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-annotation-processor</artifactId>
<version>${dubbo_annotation_processor_version}</version>
</dependency>
<!--JUnit Jupiter Engine to depend on the JUnit5 engine and JUnit 5 API -->
<dependency>
<groupId>org.junit.jupiter</groupId>
@ -710,7 +719,6 @@
<version>${maven_compiler_version}</version>
<configuration>
<compilerArgs>
<compilerArg>-proc:none</compilerArg>
<compilerArg>-parameters</compilerArg>
</compilerArgs>
<fork>true</fork>