Skip to content

Commit 42ca4a9

Browse files
committed
Revert classpath scanning change, use the same strategy as grails-core
1 parent c8f7b0d commit 42ca4a9

13 files changed

+1245
-27
lines changed

grails-datastore-gorm-test/src/test/groovy/grails/gorm/services/multitenancy/partitioned/PartitionMultiTenancySpec.groovy

Lines changed: 0 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ import org.grails.datastore.mapping.multitenancy.exceptions.TenantNotFoundExcept
1313
import org.grails.datastore.mapping.multitenancy.resolvers.SystemPropertyTenantResolver
1414
import org.grails.datastore.mapping.simple.SimpleMapDatastore
1515
import spock.lang.AutoCleanup
16-
import spock.lang.PendingFeature
1716
import spock.lang.Shared
1817
import spock.lang.Specification
1918

@@ -27,18 +26,6 @@ class PartitionMultiTenancySpec extends Specification {
2726
)
2827
@Shared IBookService bookDataService = datastore.getService(IBookService)
2928

30-
@PendingFeature(reason='''Expected exception of type 'org.grails.datastore.mapping.multitenancy.exceptions.TenantNotFoundException', but got 'java.lang.IllegalStateException\'
31-
at app//org.spockframework.lang.SpecInternals.checkExceptionThrown(SpecInternals.java:84)
32-
at app//org.spockframework.lang.SpecInternals.thrownImpl(SpecInternals.java:71)
33-
at grails.gorm.services.multitenancy.partitioned.PartitionMultiTenancySpec.Test partitioned multi-tenancy with GORM services(PartitionMultiTenancySpec.groovy:42)
34-
Caused by: java.lang.IllegalStateException: Either class [grails.gorm.services.multitenancy.partitioned.Book] is not a domain class or GORM has not been initialized correctly or has already been shutdown. Ensure GORM is loaded and configured correctly before calling any methods on a GORM entity.
35-
at org.grails.datastore.gorm.GormEnhancer.stateException(GormEnhancer.groovy:467)
36-
org.grails.datastore.gorm.GormEnhancer.findDatastore(GormEnhancer.groovy:349)
37-
at org.grails.datastore.gorm.GormEnhancer.findTenantId(GormEnhancer.groovy:263)
38-
at org.grails.datastore.gorm.GormEnhancer.findStaticApi(GormEnhancer.groovy:294)
39-
at org.grails.datastore.gorm.GormEntity$Trait$Helper.currentGormStaticApi(GormEntity.groovy:1370)
40-
at org.grails.datastore.gorm.GormEntity$Trait$Helper.count(GormEntity.groovy:649)
41-
at grails.gorm.services.multitenancy.partitioned.PartitionMultiTenancySpec.Test partitioned multi-tenancy with GORM services(PartitionMultiTenancySpec.groovy:39)''')
4229
void 'Test partitioned multi-tenancy with GORM services'() {
4330
setup:
4431
BookService bookService = new BookService()

grails-datastore-gorm-test/src/test/groovy/grails/gorm/services/multitenancy/schema/SchemaPerTenantSpec.groovy

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ import org.grails.datastore.mapping.multitenancy.exceptions.TenantNotFoundExcept
1212
import org.grails.datastore.mapping.multitenancy.resolvers.SystemPropertyTenantResolver
1313
import org.grails.datastore.mapping.simple.SimpleMapDatastore
1414
import spock.lang.AutoCleanup
15-
import spock.lang.PendingFeature
1615
import spock.lang.Shared
1716
import spock.lang.Specification
1817

@@ -30,7 +29,6 @@ class SchemaPerTenantSpec extends Specification {
3029
System.setProperty(SystemPropertyTenantResolver.PROPERTY_NAME, "")
3130
}
3231

33-
@PendingFeature(reason="java.lang.IllegalStateException: Either class [grails.gorm.services.multitenancy.schema.Book] is not a domain class or GORM has not been initialized correctly or has already been shutdown. Ensure GORM is loaded and configured correctly before calling any methods on a GORM entity.")
3432
void 'Test schema per tenant'() {
3533
when:"When there is no tenant"
3634
Book.count()
Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
/*
2+
* Copyright 2002-2024 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+
package org.grails.datastore.gorm.utils;
17+
18+
import org.apache.commons.logging.Log;
19+
import org.apache.commons.logging.LogFactory;
20+
import org.springframework.asm.AnnotationVisitor;
21+
import org.springframework.asm.SpringAsmInfo;
22+
import org.springframework.asm.Type;
23+
import org.springframework.core.annotation.AnnotationAttributes;
24+
import org.springframework.lang.Nullable;
25+
import org.springframework.util.ClassUtils;
26+
import org.springframework.util.ReflectionUtils;
27+
28+
import java.lang.reflect.Field;
29+
import java.security.AccessControlException;
30+
31+
/**
32+
* {@link AnnotationVisitor} to recursively visit annotations.
33+
34+
* <p>Note: This class was ported to Grails 7 from Spring Framework 5.3 as it was
35+
* removed in Spring 6 without a public replacement.
36+
*
37+
* @author Chris Beams
38+
* @author Juergen Hoeller
39+
* @author Phillip Webb
40+
* @author Sam Brannen
41+
* @since 3.1.1
42+
* @deprecated As of Spring Framework 5.2, this class and related classes in this
43+
* package have been replaced by SimpleAnnotationMetadataReadingVisitor
44+
* and related classes for internal use within the framework.
45+
*/
46+
@Deprecated
47+
abstract class AbstractRecursiveAnnotationVisitor extends AnnotationVisitor {
48+
49+
protected final Log logger = LogFactory.getLog(getClass());
50+
51+
protected final AnnotationAttributes attributes;
52+
53+
@Nullable
54+
protected final ClassLoader classLoader;
55+
56+
57+
public AbstractRecursiveAnnotationVisitor(@Nullable ClassLoader classLoader, AnnotationAttributes attributes) {
58+
super(SpringAsmInfo.ASM_VERSION);
59+
this.classLoader = classLoader;
60+
this.attributes = attributes;
61+
}
62+
63+
64+
@Override
65+
public void visit(String attributeName, Object attributeValue) {
66+
this.attributes.put(attributeName, attributeValue);
67+
}
68+
69+
@Override
70+
public AnnotationVisitor visitAnnotation(String attributeName, String asmTypeDescriptor) {
71+
String annotationType = Type.getType(asmTypeDescriptor).getClassName();
72+
AnnotationAttributes nestedAttributes = new AnnotationAttributes(annotationType, this.classLoader);
73+
this.attributes.put(attributeName, nestedAttributes);
74+
return new RecursiveAnnotationAttributesVisitor(annotationType, nestedAttributes, this.classLoader);
75+
}
76+
77+
@Override
78+
public AnnotationVisitor visitArray(String attributeName) {
79+
return new RecursiveAnnotationArrayVisitor(attributeName, this.attributes, this.classLoader);
80+
}
81+
82+
@Override
83+
public void visitEnum(String attributeName, String asmTypeDescriptor, String attributeValue) {
84+
Object newValue = getEnumValue(asmTypeDescriptor, attributeValue);
85+
visit(attributeName, newValue);
86+
}
87+
88+
protected Object getEnumValue(String asmTypeDescriptor, String attributeValue) {
89+
Object valueToUse = attributeValue;
90+
try {
91+
Class<?> enumType = ClassUtils.forName(Type.getType(asmTypeDescriptor).getClassName(), this.classLoader);
92+
Field enumConstant = ReflectionUtils.findField(enumType, attributeValue);
93+
if (enumConstant != null) {
94+
ReflectionUtils.makeAccessible(enumConstant);
95+
valueToUse = enumConstant.get(null);
96+
}
97+
}
98+
catch (ClassNotFoundException | NoClassDefFoundError ex) {
99+
logger.debug("Failed to classload enum type while reading annotation metadata", ex);
100+
}
101+
catch (IllegalAccessException | AccessControlException ex) {
102+
logger.debug("Could not access enum value while reading annotation metadata", ex);
103+
}
104+
return valueToUse;
105+
}
106+
107+
}
Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
/*
2+
* Copyright 2002-2024 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+
package org.grails.datastore.gorm.utils;
17+
18+
import org.springframework.core.annotation.AnnotationAttributes;
19+
import org.springframework.core.annotation.AnnotationUtils;
20+
import org.springframework.lang.Nullable;
21+
import org.springframework.util.MultiValueMap;
22+
import org.springframework.util.ObjectUtils;
23+
24+
import java.lang.annotation.Annotation;
25+
import java.lang.reflect.Modifier;
26+
import java.util.LinkedHashSet;
27+
import java.util.List;
28+
import java.util.Map;
29+
import java.util.Set;
30+
31+
/**
32+
* ASM visitor which looks for annotations defined on a class or method,
33+
* including meta-annotations.
34+
*
35+
* <p>This visitor is fully recursive, taking into account any nested
36+
* annotations or nested annotation arrays.
37+
*
38+
* <p>Note: This class was ported to Grails 7 from Spring Framework 5.3 as it was
39+
* removed in Spring 6 without a public replacement.
40+
*
41+
* @author Juergen Hoeller
42+
* @author Chris Beams
43+
* @author Phillip Webb
44+
* @author Sam Brannen
45+
* @since 3.0
46+
* @deprecated As of Spring Framework 5.2, this class and related classes in this
47+
* package have been replaced by SimpleAnnotationMetadataReadingVisitor
48+
* and related classes for internal use within the framework.
49+
*/
50+
@Deprecated
51+
final class AnnotationAttributesReadingVisitor extends RecursiveAnnotationAttributesVisitor {
52+
53+
private final MultiValueMap<String, AnnotationAttributes> attributesMap;
54+
55+
private final Map<String, Set<String>> metaAnnotationMap;
56+
57+
58+
public AnnotationAttributesReadingVisitor(String annotationType,
59+
MultiValueMap<String, AnnotationAttributes> attributesMap, Map<String, Set<String>> metaAnnotationMap,
60+
@Nullable ClassLoader classLoader) {
61+
62+
super(annotationType, new AnnotationAttributes(annotationType, classLoader), classLoader);
63+
this.attributesMap = attributesMap;
64+
this.metaAnnotationMap = metaAnnotationMap;
65+
}
66+
67+
68+
@Override
69+
public void visitEnd() {
70+
super.visitEnd();
71+
72+
Class<? extends Annotation> annotationClass = this.attributes.annotationType();
73+
if (annotationClass != null) {
74+
List<AnnotationAttributes> attributeList = this.attributesMap.get(this.annotationType);
75+
if (attributeList == null) {
76+
this.attributesMap.add(this.annotationType, this.attributes);
77+
}
78+
else {
79+
attributeList.add(0, this.attributes);
80+
}
81+
if (!AnnotationUtils.isInJavaLangAnnotationPackage(annotationClass.getName())) {
82+
try {
83+
Annotation[] metaAnnotations = annotationClass.getAnnotations();
84+
if (!ObjectUtils.isEmpty(metaAnnotations)) {
85+
Set<Annotation> visited = new LinkedHashSet<>();
86+
for (Annotation metaAnnotation : metaAnnotations) {
87+
recursivelyCollectMetaAnnotations(visited, metaAnnotation);
88+
}
89+
if (!visited.isEmpty()) {
90+
Set<String> metaAnnotationTypeNames = new LinkedHashSet<>(visited.size());
91+
for (Annotation ann : visited) {
92+
metaAnnotationTypeNames.add(ann.annotationType().getName());
93+
}
94+
this.metaAnnotationMap.put(annotationClass.getName(), metaAnnotationTypeNames);
95+
}
96+
}
97+
}
98+
catch (Throwable ex) {
99+
if (logger.isDebugEnabled()) {
100+
logger.debug("Failed to introspect meta-annotations on " + annotationClass + ": " + ex);
101+
}
102+
}
103+
}
104+
}
105+
}
106+
107+
private void recursivelyCollectMetaAnnotations(Set<Annotation> visited, Annotation annotation) {
108+
Class<? extends Annotation> annotationType = annotation.annotationType();
109+
String annotationName = annotationType.getName();
110+
if (!AnnotationUtils.isInJavaLangAnnotationPackage(annotationName) && visited.add(annotation)) {
111+
try {
112+
// Only do attribute scanning for public annotations; we'd run into
113+
// IllegalAccessExceptions otherwise, and we don't want to mess with
114+
// accessibility in a SecurityManager environment.
115+
if (Modifier.isPublic(annotationType.getModifiers())) {
116+
this.attributesMap.add(annotationName,
117+
AnnotationUtils.getAnnotationAttributes(annotation, false, true));
118+
}
119+
for (Annotation metaMetaAnnotation : annotationType.getAnnotations()) {
120+
recursivelyCollectMetaAnnotations(visited, metaMetaAnnotation);
121+
}
122+
}
123+
catch (Throwable ex) {
124+
if (logger.isDebugEnabled()) {
125+
logger.debug("Failed to introspect meta-annotations on " + annotation + ": " + ex);
126+
}
127+
}
128+
}
129+
}
130+
131+
}

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

Lines changed: 44 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
package org.grails.datastore.gorm.utils;
21
/*
32
* Copyright 2016-2024 original authors
43
*
@@ -15,15 +14,19 @@
1514
* limitations under the License.
1615
*/
1716

17+
package org.grails.datastore.gorm.utils;
18+
1819
import org.springframework.asm.AnnotationVisitor;
1920
import org.springframework.asm.SpringAsmInfo;
20-
import org.springframework.core.annotation.AnnotationFilter;
21+
import org.springframework.asm.Type;
2122
import org.springframework.core.io.Resource;
2223
import org.springframework.core.type.AnnotationMetadata;
2324
import org.springframework.core.type.ClassMetadata;
2425
import org.springframework.core.type.classreading.MetadataReader;
2526

27+
import java.io.BufferedInputStream;
2628
import java.io.IOException;
29+
import java.io.InputStream;
2730

2831
/**
2932
* A more limited version of Spring's annotation reader that only reads annotations on classes
@@ -39,18 +42,52 @@ public class AnnotationMetadataReader implements MetadataReader {
3942
private final AnnotationMetadata annotationMetadata;
4043

4144
/**
42-
* Constructs a new annotation metadata reader with the attributes
45+
* Constructs a new annotation metadata reader
4346
*
4447
* @param resource The resource
48+
* @param classLoader The classloader
49+
* @param readAttributeValues Whether to read the attributes in addition or just the annotation class names
4550
* @throws IOException
4651
*/
47-
AnnotationMetadataReader(Resource resource, AnnotationFilter filter) throws IOException {
48-
this.annotationMetadata = new FilteredAnnotationMetadata(resource.getClass(), filter);
49-
// since AnnotationMetadata extends ClassMetadata
50-
this.classMetadata = this.annotationMetadata;
52+
public AnnotationMetadataReader(Resource resource, ClassLoader classLoader, boolean readAttributeValues) throws IOException {
53+
InputStream is = new BufferedInputStream(resource.getInputStream());
54+
ClassReader classReader;
55+
try {
56+
classReader = new ClassReader(is);
57+
}
58+
catch (IllegalArgumentException ex) {
59+
throw new IOException("ASM ClassReader failed to parse class file - " +
60+
"probably due to a new Java class file version that isn't supported yet: " + resource, ex);
61+
}
62+
finally {
63+
is.close();
64+
}
65+
66+
67+
AnnotationMetadataReadingVisitor visitor;
68+
69+
if(readAttributeValues) {
70+
visitor = new AnnotationMetadataReadingVisitor(classLoader);
71+
}
72+
else {
73+
visitor = new AnnotationMetadataReadingVisitor(classLoader) {
74+
@Override
75+
public AnnotationVisitor visitAnnotation(final String desc, boolean visible) {
76+
String className = Type.getType(desc).getClassName();
77+
this.annotationSet.add(className);
78+
return new EmptyAnnotationVisitor();
79+
}
80+
};
81+
}
82+
classReader.accept(visitor, ClassReader.SKIP_DEBUG);
83+
84+
this.annotationMetadata = visitor;
85+
// (since AnnotationMetadataReadingVisitor extends ClassMetadataReadingVisitor)
86+
this.classMetadata = visitor;
5187
this.resource = resource;
5288
}
5389

90+
5491
@Override
5592
public Resource getResource() {
5693
return this.resource;
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
package org.grails.datastore.gorm.utils;
22

3-
import org.springframework.core.annotation.AnnotationFilter;
43
import org.springframework.core.io.Resource;
54
import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
65
import org.springframework.core.type.classreading.MetadataReader;
@@ -10,13 +9,13 @@
109
/**
1110
* A {@link CachingMetadataReaderFactory} that only reads annotations and not the whole class body
1211
*/
13-
class EntityAnnotationMetadataReaderFactory extends CachingMetadataReaderFactory {
14-
public EntityAnnotationMetadataReaderFactory(ClassLoader classLoader) {
12+
class AnnotationMetadataReaderFactory extends CachingMetadataReaderFactory {
13+
public AnnotationMetadataReaderFactory(ClassLoader classLoader) {
1514
super(classLoader);
1615
}
1716

1817
@Override
1918
public MetadataReader getMetadataReader(Resource resource) throws IOException {
20-
return new AnnotationMetadataReader(resource, AnnotationFilter.packages("grails.gorm.annotation", "grails.persistence", "jakarta.persistence"));
19+
return new AnnotationMetadataReader(resource, getResourceLoader().getClassLoader(), false);
2120
}
2221
}

0 commit comments

Comments
 (0)