diff --git a/grails-datastore-gorm-tck/src/main/groovy/grails/gorm/tests/InheritanceSpec.groovy b/grails-datastore-gorm-tck/src/main/groovy/grails/gorm/tests/InheritanceSpec.groovy index 912608e71d9..25baee0b190 100644 --- a/grails-datastore-gorm-tck/src/main/groovy/grails/gorm/tests/InheritanceSpec.groovy +++ b/grails-datastore-gorm-tck/src/main/groovy/grails/gorm/tests/InheritanceSpec.groovy @@ -1,7 +1,5 @@ package grails.gorm.tests -import spock.lang.Ignore - import grails.persistence.Entity /** diff --git a/grails-datastore-gorm-test/src/test/groovy/grails/gorm/services/ServiceImplSpec.groovy b/grails-datastore-gorm-test/src/test/groovy/grails/gorm/services/ServiceImplSpec.groovy index 0bdad7af596..956c577b983 100644 --- a/grails-datastore-gorm-test/src/test/groovy/grails/gorm/services/ServiceImplSpec.groovy +++ b/grails-datastore-gorm-test/src/test/groovy/grails/gorm/services/ServiceImplSpec.groovy @@ -1,11 +1,10 @@ package grails.gorm.services -import spock.lang.Ignore - import grails.gorm.annotation.Entity import grails.gorm.validation.PersistentEntityValidator import grails.validation.ValidationException -import groovy.json.JsonOutput +import groovy.json.DefaultJsonGenerator +import groovy.json.JsonGenerator import org.grails.datastore.gorm.validation.constraints.eval.DefaultConstraintEvaluator import org.grails.datastore.gorm.validation.constraints.registry.DefaultConstraintRegistry import org.grails.datastore.gorm.validation.constraints.registry.DefaultValidatorRegistry @@ -309,27 +308,6 @@ class ServiceImplSpec extends Specification { } - @Ignore(''' - java.lang.StackOverflowError - at groovy.lang.Closure.call(Closure.java:435) - at org.grails.datastore.mapping.core.DatastoreUtils.execute(DatastoreUtils.java:319) - at org.grails.datastore.gorm.AbstractDatastoreApi.execute(AbstractDatastoreApi.groovy:40) - at org.grails.datastore.gorm.GormInstanceApi.isAttached(GormInstanceApi.groovy:227) - at org.grails.datastore.gorm.GormEntity$Trait$Helper.isAttached(GormEntity.groovy:176) - at groovy.lang.MetaBeanProperty.getProperty(MetaBeanProperty.java:60) - at groovy.json.DefaultJsonGenerator.getObjectProperties(DefaultJsonGenerator.java:257) - at groovy.json.DefaultJsonGenerator.writeObject(DefaultJsonGenerator.java:234) - at groovy.json.DefaultJsonGenerator.writeMapEntry(DefaultJsonGenerator.java:401) - at groovy.json.DefaultJsonGenerator.writeMap(DefaultJsonGenerator.java:389) - at groovy.json.DefaultJsonGenerator.writeObject(DefaultJsonGenerator.java:204) - at groovy.json.DefaultJsonGenerator.writeMapEntry(DefaultJsonGenerator.java:401) - at groovy.json.DefaultJsonGenerator.writeMap(DefaultJsonGenerator.java:389) - at groovy.json.DefaultJsonGenerator.writeObject(DefaultJsonGenerator.java:235) - at groovy.json.DefaultJsonGenerator.writeMapEntry(DefaultJsonGenerator.java:401) - at groovy.json.DefaultJsonGenerator.writeMap(DefaultJsonGenerator.java:389) - at groovy.json.DefaultJsonGenerator.writeObject(DefaultJsonGenerator.java:235) - at groovy.json.DefaultJsonGenerator.writeMapEntry(DefaultJsonGenerator.java:401) - at groovy.json.DefaultJsonGenerator.writeMap(DefaultJsonGenerator.java:389)''') void "test interface projection"() { given: ProductService productService = datastore.getService(ProductService) @@ -341,7 +319,9 @@ class ServiceImplSpec extends Specification { ProductInfo info = productService.findProductInfo("Pumpkin", "Vegetable") List infos = productService.findProductInfos( "Vegetable") - def result = JsonOutput.toJson(info) + + // groovy4 will include the generated methods in output of json, which recursively refer to themselves + def result = new DefaultJsonGenerator(new JsonGenerator.Options().excludeFieldsByName("\$target")).toJson(info) then: infos.size() == 2 infos.first().name == "Carrot" diff --git a/grails-datastore-gorm-test/src/test/groovy/org/grails/datastore/gorm/BidirectionalOneToManyWithInheritanceSpec.groovy b/grails-datastore-gorm-test/src/test/groovy/org/grails/datastore/gorm/BidirectionalOneToManyWithInheritanceSpec.groovy index 1369578b073..f7f7a3e8c13 100644 --- a/grails-datastore-gorm-test/src/test/groovy/org/grails/datastore/gorm/BidirectionalOneToManyWithInheritanceSpec.groovy +++ b/grails-datastore-gorm-test/src/test/groovy/org/grails/datastore/gorm/BidirectionalOneToManyWithInheritanceSpec.groovy @@ -1,7 +1,5 @@ package org.grails.datastore.gorm -import spock.lang.Ignore - import grails.gorm.tests.GormDatastoreSpec import grails.persistence.Entity diff --git a/grails-datastore-gorm-test/src/test/groovy/org/grails/datastore/gorm/InheritanceWithOneToManySpec.groovy b/grails-datastore-gorm-test/src/test/groovy/org/grails/datastore/gorm/InheritanceWithOneToManySpec.groovy index 734adf75add..3cfc5e6a0c9 100644 --- a/grails-datastore-gorm-test/src/test/groovy/org/grails/datastore/gorm/InheritanceWithOneToManySpec.groovy +++ b/grails-datastore-gorm-test/src/test/groovy/org/grails/datastore/gorm/InheritanceWithOneToManySpec.groovy @@ -1,7 +1,5 @@ package org.grails.datastore.gorm -import spock.lang.Ignore - import grails.gorm.tests.GormDatastoreSpec import grails.persistence.Entity diff --git a/grails-datastore-gorm-test/src/test/groovy/org/grails/datastore/gorm/validation/UniqueConstraintSpec.groovy b/grails-datastore-gorm-test/src/test/groovy/org/grails/datastore/gorm/validation/UniqueConstraintSpec.groovy index 47b44e627cf..72b26fcb7f7 100644 --- a/grails-datastore-gorm-test/src/test/groovy/org/grails/datastore/gorm/validation/UniqueConstraintSpec.groovy +++ b/grails-datastore-gorm-test/src/test/groovy/org/grails/datastore/gorm/validation/UniqueConstraintSpec.groovy @@ -1,7 +1,5 @@ package org.grails.datastore.gorm.validation -import spock.lang.Ignore - import grails.gorm.annotation.Entity import grails.gorm.transactions.Transactional import org.grails.datastore.gorm.validation.constraints.MappingContextAwareConstraintFactory diff --git a/grails-datastore-gorm/src/main/groovy/org/grails/datastore/gorm/utils/AnnotationMetadataReader.java b/grails-datastore-gorm/src/main/groovy/org/grails/datastore/gorm/utils/AnnotationMetadataReader.java index 50297c42e6b..19ee93a9b2d 100644 --- a/grails-datastore-gorm/src/main/groovy/org/grails/datastore/gorm/utils/AnnotationMetadataReader.java +++ b/grails-datastore-gorm/src/main/groovy/org/grails/datastore/gorm/utils/AnnotationMetadataReader.java @@ -17,6 +17,7 @@ import org.springframework.asm.AnnotationVisitor; import org.springframework.asm.SpringAsmInfo; +import org.springframework.core.annotation.AnnotationFilter; import org.springframework.core.io.Resource; import org.springframework.core.type.AnnotationMetadata; import org.springframework.core.type.ClassMetadata; @@ -43,8 +44,8 @@ public class AnnotationMetadataReader implements MetadataReader { * @param resource The resource * @throws IOException */ - AnnotationMetadataReader(Resource resource) throws IOException { - this.annotationMetadata = AnnotationMetadata.introspect(resource.getClass()); + AnnotationMetadataReader(Resource resource, AnnotationFilter filter) throws IOException { + this.annotationMetadata = new FilteredAnnotationMetadata(resource.getClass(), filter); // since AnnotationMetadata extends ClassMetadata this.classMetadata = this.annotationMetadata; this.resource = resource; diff --git a/grails-datastore-gorm/src/main/groovy/org/grails/datastore/gorm/utils/ClasspathEntityScanner.groovy b/grails-datastore-gorm/src/main/groovy/org/grails/datastore/gorm/utils/ClasspathEntityScanner.groovy index 15286130d3d..7037960a8ec 100644 --- a/grails-datastore-gorm/src/main/groovy/org/grails/datastore/gorm/utils/ClasspathEntityScanner.groovy +++ b/grails-datastore-gorm/src/main/groovy/org/grails/datastore/gorm/utils/ClasspathEntityScanner.groovy @@ -1,4 +1,5 @@ -/* Copyright 2016 the original author or authors. +/* + * Copyright 2016-2024 original authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -66,7 +67,7 @@ class ClasspathEntityScanner { */ Class[] scan(Package... packages) { ClassPathScanningCandidateComponentProvider componentProvider = new ClassPathScanningCandidateComponentProvider(false) - componentProvider.setMetadataReaderFactory(new AnnotationMetadataReaderFactory(classLoader)) + componentProvider.setMetadataReaderFactory(new EntityAnnotationMetadataReaderFactory(classLoader)) for(ann in annotations) { componentProvider.addIncludeFilter(new AnnotationTypeFilter(ann)) } diff --git a/grails-datastore-gorm/src/main/groovy/org/grails/datastore/gorm/utils/AnnotationMetadataReaderFactory.java b/grails-datastore-gorm/src/main/groovy/org/grails/datastore/gorm/utils/EntityAnnotationMetadataReaderFactory.java similarity index 57% rename from grails-datastore-gorm/src/main/groovy/org/grails/datastore/gorm/utils/AnnotationMetadataReaderFactory.java rename to grails-datastore-gorm/src/main/groovy/org/grails/datastore/gorm/utils/EntityAnnotationMetadataReaderFactory.java index 04b88373242..99d053afa28 100644 --- a/grails-datastore-gorm/src/main/groovy/org/grails/datastore/gorm/utils/AnnotationMetadataReaderFactory.java +++ b/grails-datastore-gorm/src/main/groovy/org/grails/datastore/gorm/utils/EntityAnnotationMetadataReaderFactory.java @@ -1,5 +1,6 @@ package org.grails.datastore.gorm.utils; +import org.springframework.core.annotation.AnnotationFilter; import org.springframework.core.io.Resource; import org.springframework.core.type.classreading.CachingMetadataReaderFactory; import org.springframework.core.type.classreading.MetadataReader; @@ -9,13 +10,13 @@ /** * A {@link CachingMetadataReaderFactory} that only reads annotations and not the whole class body */ -class AnnotationMetadataReaderFactory extends CachingMetadataReaderFactory { - public AnnotationMetadataReaderFactory(ClassLoader classLoader) { +class EntityAnnotationMetadataReaderFactory extends CachingMetadataReaderFactory { + public EntityAnnotationMetadataReaderFactory(ClassLoader classLoader) { super(classLoader); } @Override public MetadataReader getMetadataReader(Resource resource) throws IOException { - return new AnnotationMetadataReader(resource); + return new AnnotationMetadataReader(resource, AnnotationFilter.packages("grails.gorm.annotation", "grails.persistence", "jakarta.persistence")); } } diff --git a/grails-datastore-gorm/src/main/groovy/org/grails/datastore/gorm/utils/FilteredAnnotationMetadata.java b/grails-datastore-gorm/src/main/groovy/org/grails/datastore/gorm/utils/FilteredAnnotationMetadata.java new file mode 100644 index 00000000000..4162eb15ea0 --- /dev/null +++ b/grails-datastore-gorm/src/main/groovy/org/grails/datastore/gorm/utils/FilteredAnnotationMetadata.java @@ -0,0 +1,137 @@ +/* + * Copyright 2002-2021 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.grails.datastore.gorm.utils; + +import org.springframework.core.annotation.*; +import org.springframework.core.type.*; +import org.springframework.lang.Nullable; +import org.springframework.util.MultiValueMap; +import org.springframework.util.ReflectionUtils; + +import java.lang.reflect.Method; +import java.util.Collections; +import java.util.LinkedHashSet; +import java.util.Map; +import java.util.Set; + +/** + * {@link AnnotationMetadata} implementation that uses standard reflection + * to introspect a given {@link Class}. + * + *

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.

+ * + * @author Juergen Hoeller + * @author Mark Fisher + * @author Chris Beams + * @author Phillip Webb + * @author Sam Brannen + * @since 2.5 + * @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. + */ +@Deprecated +public class FilteredAnnotationMetadata extends StandardClassMetadata implements AnnotationMetadata { + private final MergedAnnotations mergedAnnotations; + + @Nullable + private Set annotationTypes; + + + /** + * Create a new {@code StandardAnnotationMetadata} wrapper for the given Class. + * @param introspectedClass the Class to introspect + */ + public FilteredAnnotationMetadata(Class introspectedClass, AnnotationFilter filter) { + super(introspectedClass); + this.mergedAnnotations = MergedAnnotations.from(introspectedClass, + MergedAnnotations.SearchStrategy.INHERITED_ANNOTATIONS, RepeatableContainers.none(), filter); + } + + + @Override + public MergedAnnotations getAnnotations() { + return this.mergedAnnotations; + } + + @Override + public Set getAnnotationTypes() { + Set annotationTypes = this.annotationTypes; + if (annotationTypes == null) { + annotationTypes = Collections.unmodifiableSet(AnnotationMetadata.super.getAnnotationTypes()); + this.annotationTypes = annotationTypes; + } + return annotationTypes; + } + + @Override + @Nullable + public Map getAnnotationAttributes(String annotationName, boolean classValuesAsString) { + return AnnotatedElementUtils.getMergedAnnotationAttributes( + getIntrospectedClass(), annotationName, classValuesAsString, false); + } + + @Override + @Nullable + public MultiValueMap getAllAnnotationAttributes(String annotationName, boolean classValuesAsString) { + return AnnotatedElementUtils.getAllAnnotationAttributes( + getIntrospectedClass(), annotationName, classValuesAsString, false); + } + + @Override + public boolean hasAnnotatedMethods(String annotationName) { + if (AnnotationUtils.isCandidateClass(getIntrospectedClass(), annotationName)) { + try { + Method[] methods = org.springframework.util.ReflectionUtils.getDeclaredMethods(getIntrospectedClass()); + for (Method method : methods) { + if (isAnnotatedMethod(method, annotationName)) { + return true; + } + } + } + catch (Throwable ex) { + throw new IllegalStateException("Failed to introspect annotated methods on " + getIntrospectedClass(), ex); + } + } + return false; + } + + @Override + public Set getAnnotatedMethods(String annotationName) { + Set result = new LinkedHashSet<>(4); + if (AnnotationUtils.isCandidateClass(getIntrospectedClass(), annotationName)) { + org.springframework.util.ReflectionUtils.doWithLocalMethods(getIntrospectedClass(), method -> { + if (isAnnotatedMethod(method, annotationName)) { + result.add(new StandardMethodMetadata(method)); + } + }); + } + return result; + } + + @Override + public Set getDeclaredMethods() { + Set result = new LinkedHashSet<>(16); + ReflectionUtils.doWithLocalMethods(getIntrospectedClass(), method -> + result.add(new StandardMethodMetadata(method))); + return result; + } + + + private static boolean isAnnotatedMethod(Method method, String annotationName) { + return !method.isBridge() && method.getAnnotations().length > 0 && + AnnotatedElementUtils.isAnnotated(method, annotationName); + } +} diff --git a/grails-datastore-gorm/src/test/groovy/org/grails/compiler/gorm/EntityWithGenericSignaturesSpec.groovy b/grails-datastore-gorm/src/test/groovy/org/grails/compiler/gorm/EntityWithGenericSignaturesSpec.groovy index 505c74c2a0d..11cf5700049 100644 --- a/grails-datastore-gorm/src/test/groovy/org/grails/compiler/gorm/EntityWithGenericSignaturesSpec.groovy +++ b/grails-datastore-gorm/src/test/groovy/org/grails/compiler/gorm/EntityWithGenericSignaturesSpec.groovy @@ -1,7 +1,5 @@ package org.grails.compiler.gorm -import spock.lang.Ignore - import org.grails.datastore.mapping.keyvalue.mapping.config.KeyValueMappingContext import org.grails.datastore.mapping.keyvalue.mapping.config.KeyValuePersistentEntity import org.grails.datastore.mapping.model.MappingContext @@ -54,4 +52,4 @@ class HotWidgetSetting extends WidgetSetting { entity.getPropertyByName("setting") entity.getPropertyByName("setting").type.name == 'test.WidgetSetting' } -} \ No newline at end of file +}