Skip to content

Commit

Permalink
Introduce a way to use custom annotations in bytecode recording
Browse files Browse the repository at this point in the history
  • Loading branch information
geoand committed Dec 7, 2022
1 parent 034e473 commit 6b01e5a
Show file tree
Hide file tree
Showing 8 changed files with 101 additions and 6 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -1219,7 +1219,7 @@ public void prepare(MethodContext context) {
for (Property i : desc) {
if (!i.getDeclaringClass().getPackageName().startsWith("java.")) {
// check if the getter is ignored
if ((i.getReadMethod() != null) && (i.getReadMethod().getAnnotation(IgnoreProperty.class) != null)) {
if ((i.getReadMethod() != null) && RecordingAnnotationsUtil.isIgnored(i.getReadMethod())) {
continue;
}
// check if the matching field is ignored
Expand Down Expand Up @@ -1556,7 +1556,10 @@ ResultHandle createValue(MethodContext context, MethodCreator method, ResultHand
* Returns {@code true} iff the field is annotated {@link IgnoreProperty} or the field is marked as {@code transient}
*/
private static boolean ignoreField(Field field) {
return (field.getAnnotation(IgnoreProperty.class) != null) || Modifier.isTransient(field.getModifiers());
if (Modifier.isTransient(field.getModifiers())) {
return true;
}
return RecordingAnnotationsUtil.isIgnored(field);
}

private DeferredParameter findLoaded(final Object param) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package io.quarkus.deployment.recording;

import java.lang.annotation.Annotation;

public interface RecordingAnnotationsProvider {

Class<? extends Annotation> ignoredProperty();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package io.quarkus.deployment.recording;

import java.lang.annotation.Annotation;
import java.lang.reflect.AccessibleObject;
import java.util.HashSet;
import java.util.List;
import java.util.ServiceLoader;
import java.util.Set;

import io.quarkus.runtime.annotations.IgnoreProperty;

final class RecordingAnnotationsUtil {

static final List<Class<? extends Annotation>> IGNORED_PROPERTY_ANNOTATIONS;

static {
Set<Class<? extends Annotation>> ignoredPropertyAnnotations = new HashSet<>();
ignoredPropertyAnnotations.add(IgnoreProperty.class);
for (RecordingAnnotationsProvider provider : ServiceLoader.load(RecordingAnnotationsProvider.class)) {
Class<? extends Annotation> ignoredProperty = provider.ignoredProperty();
if (ignoredProperty != null) {
ignoredPropertyAnnotations.add(ignoredProperty);
}
}
IGNORED_PROPERTY_ANNOTATIONS = List.copyOf(ignoredPropertyAnnotations);
}

private RecordingAnnotationsUtil() {
}

static boolean isIgnored(AccessibleObject object) {
for (int i = 0; i < IGNORED_PROPERTY_ANNOTATIONS.size(); i++) {
Class<? extends Annotation> annotation = IGNORED_PROPERTY_ANNOTATIONS.get(i);
if (object.isAnnotationPresent(annotation)) {
return true;
}
}
return false;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -167,8 +167,9 @@ public void testIgnoredProperties() throws Exception {
ignoredProperties.setNotIgnored("Shows up");
ignoredProperties.setIgnoredField("Does not show up");
ignoredProperties.setAnotherIgnoredField("Does not show up either");
ignoredProperties.setCustomIgnoredField("Does not show up either");
recorder.ignoredProperties(ignoredProperties);
}, new IgnoredProperties("Shows up", null, null));
}, new IgnoredProperties("Shows up", null, null, null));
}

@Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,17 @@ public class IgnoredProperties {

private transient String anotherIgnoredField;

@TestRecordingAnnotationsProvider.TestIgnoreProperty
private String customIgnoredField;

public IgnoredProperties() {
}

public IgnoredProperties(String notIgnored, String ignoredField, String anotherIgnoredField) {
public IgnoredProperties(String notIgnored, String ignoredField, String anotherIgnoredField, String customIgnoredField) {
this.notIgnored = notIgnored;
this.ignoredField = ignoredField;
this.anotherIgnoredField = anotherIgnoredField;
this.customIgnoredField = customIgnoredField;
}

public String getNotIgnored() {
Expand All @@ -45,6 +49,14 @@ public void setAnotherIgnoredField(String anotherIgnoredField) {
this.anotherIgnoredField = anotherIgnoredField;
}

public String getCustomIgnoredField() {
return customIgnoredField;
}

public void setCustomIgnoredField(String customIgnoredField) {
this.customIgnoredField = customIgnoredField;
}

@IgnoreProperty
public String getSomethingElse() {
throw new IllegalStateException("This should not have been called");
Expand All @@ -59,19 +71,22 @@ public boolean equals(Object o) {
IgnoredProperties that = (IgnoredProperties) o;
return Objects.equals(notIgnored, that.notIgnored) &&
Objects.equals(ignoredField, that.ignoredField) &&
Objects.equals(anotherIgnoredField, that.anotherIgnoredField);
Objects.equals(anotherIgnoredField, that.anotherIgnoredField) &&
Objects.equals(customIgnoredField, that.customIgnoredField);
}

@Override
public int hashCode() {
return Objects.hash(notIgnored, ignoredField, anotherIgnoredField);
return Objects.hash(notIgnored, ignoredField, anotherIgnoredField, customIgnoredField);
}

@Override
public String toString() {
return "IgnoredProperties{" +
"notIgnored='" + notIgnored + '\'' +
", ignoredField='" + ignoredField + '\'' +
", anotherIgnoredField='" + anotherIgnoredField + '\'' +
", customIgnoredField='" + customIgnoredField + '\'' +
'}';
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package io.quarkus.deployment.recording;

import java.lang.annotation.Annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

public class TestRecordingAnnotationsProvider implements RecordingAnnotationsProvider {

@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.METHOD, ElementType.FIELD })
public @interface TestIgnoreProperty {
}

@Override
public Class<? extends Annotation> ignoredProperty() {
return TestIgnoreProperty.class;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
io.quarkus.deployment.recording.TestRecordingAnnotationsProvider
7 changes: 7 additions & 0 deletions docs/src/main/asciidoc/writing-extensions.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -1522,6 +1522,13 @@ The following objects can be passed to recorders:
- Any arbitrary object via the `io.quarkus.deployment.recording.RecorderContext#registerSubstitution(Class, Class, Class)` mechanism
- Arrays, Lists and Maps of the above

[NOTE]
====
In cases where some fields of an object to be recorded should be ignored (i.e. the value that being at build time should not be reflected at runtime), the `@IgnoreProperty` can be placed on the field.
If the class cannot depend on Quarkus, then Quarkus can use any custom annotation, as long as the extension implements the `io.quarkus.deployment.recording.RecordingAnnotationsProvider` SPI.
====

==== Injecting Configuration into Recorders

Configuration objects with phase `RUNTIME` or `BUILD_AND_RUNTIME_FIXED` can be injected into recorders via constructor
Expand Down

0 comments on commit 6b01e5a

Please sign in to comment.