Skip to content

Commit d8bf968

Browse files
committed
JCBC-1622 Repository should search for meta-annotations
Motivation ---------- This completes the work originally associated with JCBC-1544, and satisfies the integration tests that expect meta-annotations to work. Modifications ------------- When scanning for repository annotations, also search for meta-annotations. Make RepositoryTest.MetaEntity public, since repository entities must have accessible constructors. Use a different ID for the document created by RepositoryTest.shouldUpsertMetaEntity() so it doesn't conflict with the id used by shouldUpsertExtendedEntity(). Change-Id: I859e07cc386a51f067e1f10333e1d85c622265f3 Reviewed-on: http://review.couchbase.org/c/couchbase-java-client/+/125081 Tested-by: David Nault <david.nault@couchbase.com> Reviewed-by: Michael Reiche <michael.reiche@couchbase.com>
1 parent 6443b3b commit d8bf968

File tree

2 files changed

+59
-14
lines changed

2 files changed

+59
-14
lines changed

src/integration/java/com/couchbase/client/java/repository/RepositoryTest.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -172,7 +172,7 @@ public void shouldUpsertExtendedEntity() {
172172

173173
@Test
174174
public void shouldUpsertMetaEntity() {
175-
MetaEntity entity = new MetaEntity("myid", "myname");
175+
MetaEntity entity = new MetaEntity("my-meta-id", "myname");
176176
EntityDocument<MetaEntity> document = EntityDocument.create(entity);
177177

178178
assertFalse(repository().exists(document));
@@ -246,7 +246,7 @@ public String getName() {
246246
@Field
247247
public @interface MetaField {}
248248

249-
static class MetaEntity {
249+
public static class MetaEntity {
250250
public @MetaId
251251
String id = null;
252252

src/main/java/com/couchbase/client/java/repository/mapping/ReflectionBasedPropertyMetadata.java

Lines changed: 57 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,15 @@
1818
import com.couchbase.client.java.repository.annotation.EncryptedField;
1919
import com.couchbase.client.java.repository.annotation.Id;
2020

21+
import java.lang.annotation.Annotation;
22+
import java.lang.reflect.AnnotatedElement;
2123
import java.lang.reflect.Field;
24+
import java.util.HashSet;
25+
import java.util.Set;
2226

2327
/**
2428
* The property metadata implementation based on java reflection.
2529
*
26-
* @author Michael Nitschinger
2730
* @since 2.2.0
2831
*/
2932
public class ReflectionBasedPropertyMetadata implements PropertyMetadata {
@@ -33,17 +36,16 @@ public class ReflectionBasedPropertyMetadata implements PropertyMetadata {
3336
private final boolean isField;
3437
private final String name;
3538
private final String realName;
36-
private String encryptionProviderName;
39+
private final String encryptionProviderName;
3740

3841
public ReflectionBasedPropertyMetadata(final Field fieldReference) {
3942
this.fieldReference = fieldReference;
4043

41-
isId = fieldReference.isAnnotationPresent(Id.class);
42-
isField = fieldReference.isAnnotationPresent(com.couchbase.client.java.repository.annotation.Field.class);
43-
if (fieldReference.isAnnotationPresent(EncryptedField.class)) {
44-
EncryptedField encryptedField = fieldReference.getAnnotation(EncryptedField.class);
45-
this.encryptionProviderName = encryptedField.provider();
46-
}
44+
isId = hasAnnotation(fieldReference, Id.class);
45+
isField = hasAnnotation(fieldReference, com.couchbase.client.java.repository.annotation.Field.class);
46+
EncryptedField encryptedField = findAnnotation(fieldReference, EncryptedField.class);
47+
this.encryptionProviderName = encryptedField != null ? encryptedField.provider() : null;
48+
4749
realName = fieldReference.getName();
4850
name = extractName(fieldReference);
4951

@@ -106,11 +108,54 @@ public void set(Object value, Object source) {
106108
*/
107109
private static String extractName(final Field fieldReference) {
108110
com.couchbase.client.java.repository.annotation.Field annotation =
109-
fieldReference.getAnnotation(com.couchbase.client.java.repository.annotation.Field.class);
110-
if (annotation == null || annotation.value() == null || annotation.value().isEmpty()) {
111+
findAnnotation(fieldReference, com.couchbase.client.java.repository.annotation.Field.class);
112+
if (annotation == null || annotation.value().isEmpty()) {
111113
return fieldReference.getName();
112-
} else {
113-
return annotation.value();
114114
}
115+
return annotation.value();
116+
}
117+
118+
private static boolean hasAnnotation(AnnotatedElement element, Class<? extends Annotation> annotationClass) {
119+
return findAnnotation(element, annotationClass) != null;
120+
}
121+
122+
/**
123+
* Searches the element for an annotation of the given class and returns the first match.
124+
* This is a recursive search that also considers meta-annotations.
125+
*
126+
* @param element the element to search
127+
* @param annotationClass the type of annotation to look for
128+
* @return Matching annotation, or null if not found.
129+
*/
130+
private static <T extends Annotation> T findAnnotation(AnnotatedElement element, Class<T> annotationClass) {
131+
for (Annotation a : element.getAnnotations()) {
132+
T meta = findAnnotationRecursive(a, annotationClass, new HashSet<Class>());
133+
if (meta != null) {
134+
return meta;
135+
}
136+
}
137+
return null;
138+
}
139+
140+
private static <T extends Annotation> T findAnnotationRecursive(Annotation annotation, Class<T> annotationClass, Set<Class> seen) {
141+
final Class c = annotation.annotationType();
142+
143+
// Annotations can be annotated with themselves (@Documented is common example)
144+
// in which case we need to bail out to avoid stack overflow.
145+
if (!seen.add(c)) {
146+
return null;
147+
}
148+
149+
if (c.equals(annotationClass)) {
150+
return annotationClass.cast(annotation);
151+
}
152+
153+
for (Annotation meta : c.getAnnotations()) {
154+
T found = findAnnotationRecursive(meta, annotationClass, seen);
155+
if (found != null) {
156+
return found;
157+
}
158+
}
159+
return null;
115160
}
116161
}

0 commit comments

Comments
 (0)