Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ import org.grails.datastore.mapping.multitenancy.exceptions.TenantNotFoundExcept
import org.grails.datastore.mapping.multitenancy.resolvers.SystemPropertyTenantResolver
import org.grails.datastore.mapping.simple.SimpleMapDatastore
import spock.lang.AutoCleanup
import spock.lang.PendingFeature
import spock.lang.Shared
import spock.lang.Specification

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

@PendingFeature(reason='''Expected exception of type 'org.grails.datastore.mapping.multitenancy.exceptions.TenantNotFoundException', but got 'java.lang.IllegalStateException\'
at app//org.spockframework.lang.SpecInternals.checkExceptionThrown(SpecInternals.java:84)
at app//org.spockframework.lang.SpecInternals.thrownImpl(SpecInternals.java:71)
at grails.gorm.services.multitenancy.partitioned.PartitionMultiTenancySpec.Test partitioned multi-tenancy with GORM services(PartitionMultiTenancySpec.groovy:42)
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.
at org.grails.datastore.gorm.GormEnhancer.stateException(GormEnhancer.groovy:467)
org.grails.datastore.gorm.GormEnhancer.findDatastore(GormEnhancer.groovy:349)
at org.grails.datastore.gorm.GormEnhancer.findTenantId(GormEnhancer.groovy:263)
at org.grails.datastore.gorm.GormEnhancer.findStaticApi(GormEnhancer.groovy:294)
at org.grails.datastore.gorm.GormEntity$Trait$Helper.currentGormStaticApi(GormEntity.groovy:1370)
at org.grails.datastore.gorm.GormEntity$Trait$Helper.count(GormEntity.groovy:649)
at grails.gorm.services.multitenancy.partitioned.PartitionMultiTenancySpec.Test partitioned multi-tenancy with GORM services(PartitionMultiTenancySpec.groovy:39)''')
void 'Test partitioned multi-tenancy with GORM services'() {
setup:
BookService bookService = new BookService()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ import org.grails.datastore.mapping.multitenancy.exceptions.TenantNotFoundExcept
import org.grails.datastore.mapping.multitenancy.resolvers.SystemPropertyTenantResolver
import org.grails.datastore.mapping.simple.SimpleMapDatastore
import spock.lang.AutoCleanup
import spock.lang.PendingFeature
import spock.lang.Shared
import spock.lang.Specification

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

@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.")
void 'Test schema per tenant'() {
when:"When there is no tenant"
Book.count()
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
/*
* Copyright 2002-2024 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.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.asm.AnnotationVisitor;
import org.springframework.asm.SpringAsmInfo;
import org.springframework.asm.Type;
import org.springframework.core.annotation.AnnotationAttributes;
import org.springframework.lang.Nullable;
import org.springframework.util.ClassUtils;
import org.springframework.util.ReflectionUtils;

import java.lang.reflect.Field;
import java.security.AccessControlException;

/**
* {@link AnnotationVisitor} to recursively visit annotations.

* <p>Note: This class was ported to Grails 7 from Spring Framework 5.3 as it was
* removed in Spring 6 without a public replacement.
*
* @author Chris Beams
* @author Juergen Hoeller
* @author Phillip Webb
* @author Sam Brannen
* @since 3.1.1
* @deprecated As of Spring Framework 5.2, this class and related classes in this
* package have been replaced by SimpleAnnotationMetadataReadingVisitor
* and related classes for internal use within the framework.
*/
@Deprecated
abstract class AbstractRecursiveAnnotationVisitor extends AnnotationVisitor {

protected final Log logger = LogFactory.getLog(getClass());

protected final AnnotationAttributes attributes;

@Nullable
protected final ClassLoader classLoader;


public AbstractRecursiveAnnotationVisitor(@Nullable ClassLoader classLoader, AnnotationAttributes attributes) {
super(SpringAsmInfo.ASM_VERSION);
this.classLoader = classLoader;
this.attributes = attributes;
}


@Override
public void visit(String attributeName, Object attributeValue) {
this.attributes.put(attributeName, attributeValue);
}

@Override
public AnnotationVisitor visitAnnotation(String attributeName, String asmTypeDescriptor) {
String annotationType = Type.getType(asmTypeDescriptor).getClassName();
AnnotationAttributes nestedAttributes = new AnnotationAttributes(annotationType, this.classLoader);
this.attributes.put(attributeName, nestedAttributes);
return new RecursiveAnnotationAttributesVisitor(annotationType, nestedAttributes, this.classLoader);
}

@Override
public AnnotationVisitor visitArray(String attributeName) {
return new RecursiveAnnotationArrayVisitor(attributeName, this.attributes, this.classLoader);
}

@Override
public void visitEnum(String attributeName, String asmTypeDescriptor, String attributeValue) {
Object newValue = getEnumValue(asmTypeDescriptor, attributeValue);
visit(attributeName, newValue);
}

protected Object getEnumValue(String asmTypeDescriptor, String attributeValue) {
Object valueToUse = attributeValue;
try {
Class<?> enumType = ClassUtils.forName(Type.getType(asmTypeDescriptor).getClassName(), this.classLoader);
Field enumConstant = ReflectionUtils.findField(enumType, attributeValue);
if (enumConstant != null) {
ReflectionUtils.makeAccessible(enumConstant);
valueToUse = enumConstant.get(null);
}
}
catch (ClassNotFoundException | NoClassDefFoundError ex) {
logger.debug("Failed to classload enum type while reading annotation metadata", ex);
}
catch (IllegalAccessException | AccessControlException ex) {
logger.debug("Could not access enum value while reading annotation metadata", ex);
}
return valueToUse;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
/*
* Copyright 2002-2024 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.AnnotationAttributes;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.lang.Nullable;
import org.springframework.util.MultiValueMap;
import org.springframework.util.ObjectUtils;

import java.lang.annotation.Annotation;
import java.lang.reflect.Modifier;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
* ASM visitor which looks for annotations defined on a class or method,
* including meta-annotations.
*
* <p>This visitor is fully recursive, taking into account any nested
* annotations or nested annotation arrays.
*
* <p>Note: This class was ported to Grails 7 from Spring Framework 5.3 as it was
* removed in Spring 6 without a public replacement.
*
* @author Juergen Hoeller
* @author Chris Beams
* @author Phillip Webb
* @author Sam Brannen
* @since 3.0
* @deprecated As of Spring Framework 5.2, this class and related classes in this
* package have been replaced by SimpleAnnotationMetadataReadingVisitor
* and related classes for internal use within the framework.
*/
@Deprecated
final class AnnotationAttributesReadingVisitor extends RecursiveAnnotationAttributesVisitor {

private final MultiValueMap<String, AnnotationAttributes> attributesMap;

private final Map<String, Set<String>> metaAnnotationMap;


public AnnotationAttributesReadingVisitor(String annotationType,
MultiValueMap<String, AnnotationAttributes> attributesMap, Map<String, Set<String>> metaAnnotationMap,
@Nullable ClassLoader classLoader) {

super(annotationType, new AnnotationAttributes(annotationType, classLoader), classLoader);
this.attributesMap = attributesMap;
this.metaAnnotationMap = metaAnnotationMap;
}


@Override
public void visitEnd() {
super.visitEnd();

Class<? extends Annotation> annotationClass = this.attributes.annotationType();
if (annotationClass != null) {
List<AnnotationAttributes> attributeList = this.attributesMap.get(this.annotationType);
if (attributeList == null) {
this.attributesMap.add(this.annotationType, this.attributes);
}
else {
attributeList.add(0, this.attributes);
}
if (!AnnotationUtils.isInJavaLangAnnotationPackage(annotationClass.getName())) {
try {
Annotation[] metaAnnotations = annotationClass.getAnnotations();
if (!ObjectUtils.isEmpty(metaAnnotations)) {
Set<Annotation> visited = new LinkedHashSet<>();
for (Annotation metaAnnotation : metaAnnotations) {
recursivelyCollectMetaAnnotations(visited, metaAnnotation);
}
if (!visited.isEmpty()) {
Set<String> metaAnnotationTypeNames = new LinkedHashSet<>(visited.size());
for (Annotation ann : visited) {
metaAnnotationTypeNames.add(ann.annotationType().getName());
}
this.metaAnnotationMap.put(annotationClass.getName(), metaAnnotationTypeNames);
}
}
}
catch (Throwable ex) {
if (logger.isDebugEnabled()) {
logger.debug("Failed to introspect meta-annotations on " + annotationClass + ": " + ex);
}
}
}
}
}

private void recursivelyCollectMetaAnnotations(Set<Annotation> visited, Annotation annotation) {
Class<? extends Annotation> annotationType = annotation.annotationType();
String annotationName = annotationType.getName();
if (!AnnotationUtils.isInJavaLangAnnotationPackage(annotationName) && visited.add(annotation)) {
try {
// Only do attribute scanning for public annotations; we'd run into
// IllegalAccessExceptions otherwise, and we don't want to mess with
// accessibility in a SecurityManager environment.
if (Modifier.isPublic(annotationType.getModifiers())) {
this.attributesMap.add(annotationName,
AnnotationUtils.getAnnotationAttributes(annotation, false, true));
}
for (Annotation metaMetaAnnotation : annotationType.getAnnotations()) {
recursivelyCollectMetaAnnotations(visited, metaMetaAnnotation);
}
}
catch (Throwable ex) {
if (logger.isDebugEnabled()) {
logger.debug("Failed to introspect meta-annotations on " + annotation + ": " + ex);
}
}
}
}

}
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
package org.grails.datastore.gorm.utils;
/*
* Copyright 2016-2024 original authors
*
Expand All @@ -15,15 +14,19 @@
* limitations under the License.
*/

package org.grails.datastore.gorm.utils;

import org.springframework.asm.AnnotationVisitor;
import org.springframework.asm.SpringAsmInfo;
import org.springframework.core.annotation.AnnotationFilter;
import org.springframework.asm.Type;
import org.springframework.core.io.Resource;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.core.type.ClassMetadata;
import org.springframework.core.type.classreading.MetadataReader;

import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;

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

/**
* Constructs a new annotation metadata reader with the attributes
* Constructs a new annotation metadata reader
*
* @param resource The resource
* @param classLoader The classloader
* @param readAttributeValues Whether to read the attributes in addition or just the annotation class names
* @throws IOException
*/
AnnotationMetadataReader(Resource resource, AnnotationFilter filter) throws IOException {
this.annotationMetadata = new FilteredAnnotationMetadata(resource.getClass(), filter);
// since AnnotationMetadata extends ClassMetadata
this.classMetadata = this.annotationMetadata;
public AnnotationMetadataReader(Resource resource, ClassLoader classLoader, boolean readAttributeValues) throws IOException {
InputStream is = new BufferedInputStream(resource.getInputStream());
ClassReader classReader;
try {
classReader = new ClassReader(is);
}
catch (IllegalArgumentException ex) {
throw new IOException("ASM ClassReader failed to parse class file - " +
"probably due to a new Java class file version that isn't supported yet: " + resource, ex);
}
finally {
is.close();
}


AnnotationMetadataReadingVisitor visitor;

if(readAttributeValues) {
visitor = new AnnotationMetadataReadingVisitor(classLoader);
}
else {
visitor = new AnnotationMetadataReadingVisitor(classLoader) {
@Override
public AnnotationVisitor visitAnnotation(final String desc, boolean visible) {
String className = Type.getType(desc).getClassName();
this.annotationSet.add(className);
return new EmptyAnnotationVisitor();
}
};
}
classReader.accept(visitor, ClassReader.SKIP_DEBUG);

this.annotationMetadata = visitor;
// (since AnnotationMetadataReadingVisitor extends ClassMetadataReadingVisitor)
this.classMetadata = visitor;
this.resource = resource;
}


@Override
public Resource getResource() {
return this.resource;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
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;
Expand All @@ -10,13 +9,13 @@
/**
* A {@link CachingMetadataReaderFactory} that only reads annotations and not the whole class body
*/
class EntityAnnotationMetadataReaderFactory extends CachingMetadataReaderFactory {
public EntityAnnotationMetadataReaderFactory(ClassLoader classLoader) {
class AnnotationMetadataReaderFactory extends CachingMetadataReaderFactory {
public AnnotationMetadataReaderFactory(ClassLoader classLoader) {
super(classLoader);
}

@Override
public MetadataReader getMetadataReader(Resource resource) throws IOException {
return new AnnotationMetadataReader(resource, AnnotationFilter.packages("grails.gorm.annotation", "grails.persistence", "jakarta.persistence"));
return new AnnotationMetadataReader(resource, getResourceLoader().getClassLoader(), false);
}
}
Loading
Loading