Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add targetPackage property to the @Introspected annotation #9105

Merged
merged 3 commits into from
Apr 17, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
Add introspectionPackage property to the @introspected annotation
  • Loading branch information
andriy-dmytruk committed Apr 14, 2023
commit 98b1ccecbf3b5fdc5c39fcf503c6079a12f86c32
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,13 @@
*/
String withPrefix() default "with";

/**
* @return The package to write introspections to. By default, uses the class package.
* @since 3.9.0
*/
@Experimental
String introspectionPackage() default "";
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

rename to targetPackage


/**
* Allow pre-computed indexes for property lookups based on an annotation and a member.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,41 @@ class Test extends Auditable {
bean.updatedAt != null
}

void "test introspection can written to different package"() {
when:
def classLoader = buildClassLoader("test.Test", '''
package test;

import io.micronaut.core.annotation.Introspected;

@Introspected(introspectionPackage = "test.introspections")
public class Test {
private String name;
public Test(String name) {
this.name = name;
}
public String getName() {
return name;
}
}

''')
def introspectionName = 'test.introspections.$Test$Introspection'
def introspection = classLoader.loadClass(introspectionName).newInstance() as BeanIntrospection

then:
introspection != null
introspection.getProperty("name").isPresent()

when:
def introspectionRefName = 'test.introspections.$Test$IntrospectionRef'
def introspectionRef = classLoader.loadClass(introspectionRefName).newInstance() as BeanIntrospectionReference

then:
introspectionRef != null
introspectionRef.load() != null
}

void "test generics in arrays don't stack overflow"() {
given:
def introspection = buildBeanIntrospection('arraygenerics.Test', '''
Expand Down Expand Up @@ -904,6 +939,37 @@ class Test {}
applicationContext.close()
}

void "test create bean introspection for external class with custom package"() {
given:
def classLoader = buildClassLoader('test.Test', '''
package test;

import io.micronaut.core.annotation.Introspected;
import io.micronaut.inject.visitor.beans.OuterBean;

@Introspected(classes=OuterBean.InnerBean.class, introspectionPackage="test.micronaut.intro")
class Test {}
''')

when:"the reference is loaded"
def reference = classLoader.loadClass('test.micronaut.intro.$Test$IntrospectionRef0').newInstance() as BeanIntrospectionReference

then:"the reference is valid"
notThrown(ClassNotFoundException)
reference.getBeanType() == OuterBean.InnerBean.class
reference.load() != null

print(classLoader)

when:"the introspection is loaded"
def introspectionName = 'test.micronaut.intro.$io_micronaut_inject_visitor_beans_OuterBean$InnerBean$Introspection'
def introspection = classLoader.loadClass(introspectionName).newInstance() as BeanIntrospection

then:"the introspection is valid"
notThrown(ClassNotFoundException)
introspection.getProperty("name").isPresent()
}

void "test create bean introspection for interface"() {
given:
def context = buildContext('itfcetest.MyInterface','''
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -128,12 +128,12 @@ final class BeanIntrospectionWriter extends AbstractAnnotationMetadataWriter {
* @param classElement The class element
* @param beanAnnotationMetadata The bean annotation metadata
*/
BeanIntrospectionWriter(ClassElement classElement, AnnotationMetadata beanAnnotationMetadata) {
super(computeReferenceName(classElement.getName()), classElement, beanAnnotationMetadata, true);
BeanIntrospectionWriter(String introspectionPackage, ClassElement classElement, AnnotationMetadata beanAnnotationMetadata) {
super(computeReferenceName(introspectionPackage, classElement.getName()), classElement, beanAnnotationMetadata, true);
final String name = classElement.getName();
this.classElement = classElement;
this.referenceWriter = new ClassWriter(ClassWriter.COMPUTE_MAXS);
this.introspectionName = computeIntrospectionName(name);
this.introspectionName = computeShortIntrospectionName(introspectionPackage, name);
this.introspectionType = getTypeReferenceForName(introspectionName);
this.beanType = getTypeReferenceForName(name);
this.dispatchWriter = new DispatchWriter(introspectionType, Type.getType(AbstractInitializableBeanIntrospection.class));
Expand All @@ -149,16 +149,17 @@ final class BeanIntrospectionWriter extends AbstractAnnotationMetadataWriter {
* @param beanAnnotationMetadata The bean annotation metadata
*/
BeanIntrospectionWriter(
String introspectionPackage,
String generatingType,
int index,
ClassElement originatingElement,
ClassElement classElement,
AnnotationMetadata beanAnnotationMetadata) {
super(computeReferenceName(generatingType) + index, originatingElement, beanAnnotationMetadata, true);
super(computeReferenceName(introspectionPackage, generatingType) + index, originatingElement, beanAnnotationMetadata, true);
final String className = classElement.getName();
this.classElement = classElement;
this.referenceWriter = new ClassWriter(ClassWriter.COMPUTE_MAXS);
this.introspectionName = computeIntrospectionName(generatingType, className);
this.introspectionName = computeIntrospectionName(introspectionPackage, className);
this.introspectionType = getTypeReferenceForName(introspectionName);
this.beanType = getTypeReferenceForName(className);
this.dispatchWriter = new DispatchWriter(introspectionType);
Expand Down Expand Up @@ -996,22 +997,19 @@ private void pushAnnotationMetadata(ClassWriter classWriter, GeneratorAdapter st
}

@NonNull
private static String computeReferenceName(String className) {
String packageName = NameUtils.getPackageName(className);
private static String computeReferenceName(String packageName, String className) {
final String shortName = NameUtils.getSimpleName(className);
return packageName + ".$" + shortName + REFERENCE_SUFFIX;
}

@NonNull
private static String computeIntrospectionName(String className) {
String packageName = NameUtils.getPackageName(className);
private static String computeShortIntrospectionName(String packageName, String className) {
final String shortName = NameUtils.getSimpleName(className);
return packageName + ".$" + shortName + INTROSPECTION_SUFFIX;
}

@NonNull
private static String computeIntrospectionName(String generatingName, String className) {
final String packageName = NameUtils.getPackageName(generatingName);
private static String computeIntrospectionName(String packageName, String className) {
return packageName + ".$" + className.replace('.', '_') + INTROSPECTION_SUFFIX;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,8 @@ private void processIntrospected(ClassElement element, VisitorContext context, A
Introspected.AccessKind[] accessKinds = introspected.enumValues("accessKind", Introspected.AccessKind.class);
Introspected.Visibility[] visibilities =
introspected.enumValues("visibility", Introspected.Visibility.class);
final String introspectionPackage = introspected.stringValue("introspectionPackage").orElse(element.getPackageName());
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If this package names differ we need to change the default visibility to only public members (see Visibility.PUBLIC) since package private and protected members will no longer be visible from different packages. In the 4.0.x branch we support reflection as a fall back but not here.


if (ArrayUtils.isEmpty(accessKinds)) {
accessKinds = DEFAULT_ACCESS_KIND;
}
Expand Down Expand Up @@ -217,6 +219,7 @@ private void processIntrospected(ClassElement element, VisitorContext context, A
? element.getAnnotationMetadata()
: new AnnotationMetadataHierarchy(element.getAnnotationMetadata(), typeMetadata);
final BeanIntrospectionWriter writer = new BeanIntrospectionWriter(
introspectionPackage,
element.getName(),
index.getAndIncrement(),
element,
Expand Down Expand Up @@ -251,6 +254,7 @@ private void processIntrospected(ClassElement element, VisitorContext context, A
continue;
}
final BeanIntrospectionWriter writer = new BeanIntrospectionWriter(
introspectionPackage,
element.getName(),
j++,
element,
Expand All @@ -275,6 +279,7 @@ private void processIntrospected(ClassElement element, VisitorContext context, A
} else {

final BeanIntrospectionWriter writer = new BeanIntrospectionWriter(
introspectionPackage,
element,
metadata ? element.getAnnotationMetadata() : null
);
Expand Down