Skip to content

Commit c8f7b0d

Browse files
committed
Cleanup & fix annotation reading for classpath scanning
1 parent 678b4b9 commit c8f7b0d

File tree

10 files changed

+153
-43
lines changed

10 files changed

+153
-43
lines changed

grails-datastore-gorm-tck/src/main/groovy/grails/gorm/tests/InheritanceSpec.groovy

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
package grails.gorm.tests
22

3-
import spock.lang.Ignore
4-
53
import grails.persistence.Entity
64

75
/**

grails-datastore-gorm-test/src/test/groovy/grails/gorm/services/ServiceImplSpec.groovy

Lines changed: 5 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
11
package grails.gorm.services
22

3-
import spock.lang.Ignore
4-
53
import grails.gorm.annotation.Entity
64
import grails.gorm.validation.PersistentEntityValidator
75
import grails.validation.ValidationException
8-
import groovy.json.JsonOutput
6+
import groovy.json.DefaultJsonGenerator
7+
import groovy.json.JsonGenerator
98
import org.grails.datastore.gorm.validation.constraints.eval.DefaultConstraintEvaluator
109
import org.grails.datastore.gorm.validation.constraints.registry.DefaultConstraintRegistry
1110
import org.grails.datastore.gorm.validation.constraints.registry.DefaultValidatorRegistry
@@ -309,27 +308,6 @@ class ServiceImplSpec extends Specification {
309308

310309
}
311310

312-
@Ignore('''
313-
java.lang.StackOverflowError
314-
at groovy.lang.Closure.call(Closure.java:435)
315-
at org.grails.datastore.mapping.core.DatastoreUtils.execute(DatastoreUtils.java:319)
316-
at org.grails.datastore.gorm.AbstractDatastoreApi.execute(AbstractDatastoreApi.groovy:40)
317-
at org.grails.datastore.gorm.GormInstanceApi.isAttached(GormInstanceApi.groovy:227)
318-
at org.grails.datastore.gorm.GormEntity$Trait$Helper.isAttached(GormEntity.groovy:176)
319-
at groovy.lang.MetaBeanProperty.getProperty(MetaBeanProperty.java:60)
320-
at groovy.json.DefaultJsonGenerator.getObjectProperties(DefaultJsonGenerator.java:257)
321-
at groovy.json.DefaultJsonGenerator.writeObject(DefaultJsonGenerator.java:234)
322-
at groovy.json.DefaultJsonGenerator.writeMapEntry(DefaultJsonGenerator.java:401)
323-
at groovy.json.DefaultJsonGenerator.writeMap(DefaultJsonGenerator.java:389)
324-
at groovy.json.DefaultJsonGenerator.writeObject(DefaultJsonGenerator.java:204)
325-
at groovy.json.DefaultJsonGenerator.writeMapEntry(DefaultJsonGenerator.java:401)
326-
at groovy.json.DefaultJsonGenerator.writeMap(DefaultJsonGenerator.java:389)
327-
at groovy.json.DefaultJsonGenerator.writeObject(DefaultJsonGenerator.java:235)
328-
at groovy.json.DefaultJsonGenerator.writeMapEntry(DefaultJsonGenerator.java:401)
329-
at groovy.json.DefaultJsonGenerator.writeMap(DefaultJsonGenerator.java:389)
330-
at groovy.json.DefaultJsonGenerator.writeObject(DefaultJsonGenerator.java:235)
331-
at groovy.json.DefaultJsonGenerator.writeMapEntry(DefaultJsonGenerator.java:401)
332-
at groovy.json.DefaultJsonGenerator.writeMap(DefaultJsonGenerator.java:389)''')
333311
void "test interface projection"() {
334312
given:
335313
ProductService productService = datastore.getService(ProductService)
@@ -341,7 +319,9 @@ class ServiceImplSpec extends Specification {
341319

342320
ProductInfo info = productService.findProductInfo("Pumpkin", "Vegetable")
343321
List<ProductInfo> infos = productService.findProductInfos( "Vegetable")
344-
def result = JsonOutput.toJson(info)
322+
323+
// groovy4 will include the generated methods in output of json, which recursively refer to themselves
324+
def result = new DefaultJsonGenerator(new JsonGenerator.Options().excludeFieldsByName("\$target")).toJson(info)
345325
then:
346326
infos.size() == 2
347327
infos.first().name == "Carrot"

grails-datastore-gorm-test/src/test/groovy/org/grails/datastore/gorm/BidirectionalOneToManyWithInheritanceSpec.groovy

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
package org.grails.datastore.gorm
22

3-
import spock.lang.Ignore
4-
53
import grails.gorm.tests.GormDatastoreSpec
64
import grails.persistence.Entity
75

grails-datastore-gorm-test/src/test/groovy/org/grails/datastore/gorm/InheritanceWithOneToManySpec.groovy

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
package org.grails.datastore.gorm
22

3-
import spock.lang.Ignore
4-
53
import grails.gorm.tests.GormDatastoreSpec
64
import grails.persistence.Entity
75

grails-datastore-gorm-test/src/test/groovy/org/grails/datastore/gorm/validation/UniqueConstraintSpec.groovy

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
package org.grails.datastore.gorm.validation
22

3-
import spock.lang.Ignore
4-
53
import grails.gorm.annotation.Entity
64
import grails.gorm.transactions.Transactional
75
import org.grails.datastore.gorm.validation.constraints.MappingContextAwareConstraintFactory

grails-datastore-gorm/src/main/groovy/org/grails/datastore/gorm/utils/AnnotationMetadataReader.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717

1818
import org.springframework.asm.AnnotationVisitor;
1919
import org.springframework.asm.SpringAsmInfo;
20+
import org.springframework.core.annotation.AnnotationFilter;
2021
import org.springframework.core.io.Resource;
2122
import org.springframework.core.type.AnnotationMetadata;
2223
import org.springframework.core.type.ClassMetadata;
@@ -43,8 +44,8 @@ public class AnnotationMetadataReader implements MetadataReader {
4344
* @param resource The resource
4445
* @throws IOException
4546
*/
46-
AnnotationMetadataReader(Resource resource) throws IOException {
47-
this.annotationMetadata = AnnotationMetadata.introspect(resource.getClass());
47+
AnnotationMetadataReader(Resource resource, AnnotationFilter filter) throws IOException {
48+
this.annotationMetadata = new FilteredAnnotationMetadata(resource.getClass(), filter);
4849
// since AnnotationMetadata extends ClassMetadata
4950
this.classMetadata = this.annotationMetadata;
5051
this.resource = resource;

grails-datastore-gorm/src/main/groovy/org/grails/datastore/gorm/utils/ClasspathEntityScanner.groovy

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
/* Copyright 2016 the original author or authors.
1+
/*
2+
* Copyright 2016-2024 original authors
23
*
34
* Licensed under the Apache License, Version 2.0 (the "License");
45
* you may not use this file except in compliance with the License.
@@ -66,7 +67,7 @@ class ClasspathEntityScanner {
6667
*/
6768
Class[] scan(Package... packages) {
6869
ClassPathScanningCandidateComponentProvider componentProvider = new ClassPathScanningCandidateComponentProvider(false)
69-
componentProvider.setMetadataReaderFactory(new AnnotationMetadataReaderFactory(classLoader))
70+
componentProvider.setMetadataReaderFactory(new EntityAnnotationMetadataReaderFactory(classLoader))
7071
for(ann in annotations) {
7172
componentProvider.addIncludeFilter(new AnnotationTypeFilter(ann))
7273
}
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package org.grails.datastore.gorm.utils;
22

3+
import org.springframework.core.annotation.AnnotationFilter;
34
import org.springframework.core.io.Resource;
45
import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
56
import org.springframework.core.type.classreading.MetadataReader;
@@ -9,13 +10,13 @@
910
/**
1011
* A {@link CachingMetadataReaderFactory} that only reads annotations and not the whole class body
1112
*/
12-
class AnnotationMetadataReaderFactory extends CachingMetadataReaderFactory {
13-
public AnnotationMetadataReaderFactory(ClassLoader classLoader) {
13+
class EntityAnnotationMetadataReaderFactory extends CachingMetadataReaderFactory {
14+
public EntityAnnotationMetadataReaderFactory(ClassLoader classLoader) {
1415
super(classLoader);
1516
}
1617

1718
@Override
1819
public MetadataReader getMetadataReader(Resource resource) throws IOException {
19-
return new AnnotationMetadataReader(resource);
20+
return new AnnotationMetadataReader(resource, AnnotationFilter.packages("grails.gorm.annotation", "grails.persistence", "jakarta.persistence"));
2021
}
2122
}
Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
/*
2+
* Copyright 2002-2021 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.grails.datastore.gorm.utils;
18+
19+
import org.springframework.core.annotation.*;
20+
import org.springframework.core.type.*;
21+
import org.springframework.lang.Nullable;
22+
import org.springframework.util.MultiValueMap;
23+
import org.springframework.util.ReflectionUtils;
24+
25+
import java.lang.reflect.Method;
26+
import java.util.Collections;
27+
import java.util.LinkedHashSet;
28+
import java.util.Map;
29+
import java.util.Set;
30+
31+
/**
32+
* {@link AnnotationMetadata} implementation that uses standard reflection
33+
* to introspect a given {@link Class}.
34+
*
35+
* <p>Note: this class was ported to GORM 9 from Spring Framework 6.1 since the package filter can't be passed to the spring version.</p>
36+
*
37+
* @author Juergen Hoeller
38+
* @author Mark Fisher
39+
* @author Chris Beams
40+
* @author Phillip Webb
41+
* @author Sam Brannen
42+
* @since 2.5
43+
* @deprecated As of Spring Framework 6.1, this class does not allow specifying the annotation filter. An upstream pull request should be opened so this can be removed.
44+
*/
45+
@Deprecated
46+
public class FilteredAnnotationMetadata extends StandardClassMetadata implements AnnotationMetadata {
47+
private final MergedAnnotations mergedAnnotations;
48+
49+
@Nullable
50+
private Set<String> annotationTypes;
51+
52+
53+
/**
54+
* Create a new {@code StandardAnnotationMetadata} wrapper for the given Class.
55+
* @param introspectedClass the Class to introspect
56+
*/
57+
public FilteredAnnotationMetadata(Class<?> introspectedClass, AnnotationFilter filter) {
58+
super(introspectedClass);
59+
this.mergedAnnotations = MergedAnnotations.from(introspectedClass,
60+
MergedAnnotations.SearchStrategy.INHERITED_ANNOTATIONS, RepeatableContainers.none(), filter);
61+
}
62+
63+
64+
@Override
65+
public MergedAnnotations getAnnotations() {
66+
return this.mergedAnnotations;
67+
}
68+
69+
@Override
70+
public Set<String> getAnnotationTypes() {
71+
Set<String> annotationTypes = this.annotationTypes;
72+
if (annotationTypes == null) {
73+
annotationTypes = Collections.unmodifiableSet(AnnotationMetadata.super.getAnnotationTypes());
74+
this.annotationTypes = annotationTypes;
75+
}
76+
return annotationTypes;
77+
}
78+
79+
@Override
80+
@Nullable
81+
public Map<String, Object> getAnnotationAttributes(String annotationName, boolean classValuesAsString) {
82+
return AnnotatedElementUtils.getMergedAnnotationAttributes(
83+
getIntrospectedClass(), annotationName, classValuesAsString, false);
84+
}
85+
86+
@Override
87+
@Nullable
88+
public MultiValueMap<String, Object> getAllAnnotationAttributes(String annotationName, boolean classValuesAsString) {
89+
return AnnotatedElementUtils.getAllAnnotationAttributes(
90+
getIntrospectedClass(), annotationName, classValuesAsString, false);
91+
}
92+
93+
@Override
94+
public boolean hasAnnotatedMethods(String annotationName) {
95+
if (AnnotationUtils.isCandidateClass(getIntrospectedClass(), annotationName)) {
96+
try {
97+
Method[] methods = org.springframework.util.ReflectionUtils.getDeclaredMethods(getIntrospectedClass());
98+
for (Method method : methods) {
99+
if (isAnnotatedMethod(method, annotationName)) {
100+
return true;
101+
}
102+
}
103+
}
104+
catch (Throwable ex) {
105+
throw new IllegalStateException("Failed to introspect annotated methods on " + getIntrospectedClass(), ex);
106+
}
107+
}
108+
return false;
109+
}
110+
111+
@Override
112+
public Set<MethodMetadata> getAnnotatedMethods(String annotationName) {
113+
Set<MethodMetadata> result = new LinkedHashSet<>(4);
114+
if (AnnotationUtils.isCandidateClass(getIntrospectedClass(), annotationName)) {
115+
org.springframework.util.ReflectionUtils.doWithLocalMethods(getIntrospectedClass(), method -> {
116+
if (isAnnotatedMethod(method, annotationName)) {
117+
result.add(new StandardMethodMetadata(method));
118+
}
119+
});
120+
}
121+
return result;
122+
}
123+
124+
@Override
125+
public Set<MethodMetadata> getDeclaredMethods() {
126+
Set<MethodMetadata> result = new LinkedHashSet<>(16);
127+
ReflectionUtils.doWithLocalMethods(getIntrospectedClass(), method ->
128+
result.add(new StandardMethodMetadata(method)));
129+
return result;
130+
}
131+
132+
133+
private static boolean isAnnotatedMethod(Method method, String annotationName) {
134+
return !method.isBridge() && method.getAnnotations().length > 0 &&
135+
AnnotatedElementUtils.isAnnotated(method, annotationName);
136+
}
137+
}

grails-datastore-gorm/src/test/groovy/org/grails/compiler/gorm/EntityWithGenericSignaturesSpec.groovy

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
package org.grails.compiler.gorm
22

3-
import spock.lang.Ignore
4-
53
import org.grails.datastore.mapping.keyvalue.mapping.config.KeyValueMappingContext
64
import org.grails.datastore.mapping.keyvalue.mapping.config.KeyValuePersistentEntity
75
import org.grails.datastore.mapping.model.MappingContext
@@ -54,4 +52,4 @@ class HotWidgetSetting extends WidgetSetting {
5452
entity.getPropertyByName("setting")
5553
entity.getPropertyByName("setting").type.name == 'test.WidgetSetting'
5654
}
57-
}
55+
}

0 commit comments

Comments
 (0)