Skip to content

Commit

Permalink
Merge pull request #2779 from apache/2297-beansort
Browse files Browse the repository at this point in the history
2297 beansort
  • Loading branch information
andi-huber authored Nov 13, 2024
2 parents b3bd742 + 59791d1 commit 77488e3
Show file tree
Hide file tree
Showing 24 changed files with 304 additions and 283 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
import java.lang.annotation.Target;

/**
* Indicates that an property, collection or action is to be called
* Indicates that a property, collection or action is to be called
* programmatically and should be ignored from the metamodel.
*
* <p>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,14 @@
*/
package org.apache.causeway.applib.services.metamodel;

import javax.imageio.spi.ServiceRegistry;

import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Profile;

import org.apache.causeway.applib.annotation.Programmatic;
import org.apache.causeway.applib.services.factory.FactoryService;

/**
* Top level object classification.
Expand All @@ -34,118 +39,141 @@ public enum BeanSort {
* Includes classes annotated with {@code @DomainObject}, when *not* associated
* with a persistence layer. <p> see also {@link #ENTITY}
*/
VIEW_MODEL,
VIEW_MODEL(new BeanPolicy(BeanPolicy.INTROSPECTION | BeanPolicy.WRAPPING | BeanPolicy.ELEMENTTYPE | BeanPolicy.NON_SUBSTITUTABLE)),
/**
* Persistable object, associated with a persistence layer/context.
* <p>
* Includes classes annotated with {@code @DomainObject}, when associated
* with a persistence layer. <p> see also {@link #VIEW_MODEL}
*
*/
ENTITY,
ENTITY(new BeanPolicy(BeanPolicy.INTROSPECTION | BeanPolicy.WRAPPING | BeanPolicy.ELEMENTTYPE | BeanPolicy.NON_SUBSTITUTABLE)),
/**
* Injectable object, associated with a lifecycle context
* (application-scoped, request-scoped, ...).
* <p>
* In other words: Indicates that the class is a Spring managed bean,
* which IS also contributing to the UI or WEB API. Hence needs introspection.
* <p>
* The {@link ServiceRegistry} must be aware.
* <p>
* to be introspected: YES
*/
MANAGED_BEAN_CONTRIBUTING,
MANAGED_BEAN_CONTRIBUTING(new BeanPolicy(BeanPolicy.INTROSPECTION | BeanPolicy.INJECTABLE | BeanPolicy.WRAPPING | BeanPolicy.NON_SUBSTITUTABLE)),
/**
* Injectable object, associated with a lifecycle context
* (application-scoped, request-scoped, ...).
* <p>
* In other words: Indicates that the class is a Spring managed bean,
* which is NOT contributing to the UI or WEB API. Hence not introspected.
* <p>
* The {@link ServiceRegistry} must be aware regardless.
* <p>
* This is also the fallback {@link BeanSort} for beans originating from {@link Bean} annotated factory methods,
* when the bean class itself declares no annotations.
* <p>
* to be introspected: NO
*/
MANAGED_BEAN_NOT_CONTRIBUTING,
MANAGED_BEAN_NOT_CONTRIBUTING(new BeanPolicy(BeanPolicy.INJECTABLE | BeanPolicy.WRAPPING | BeanPolicy.NON_SUBSTITUTABLE)),
/**
* Object associated with an <i>entity</i>, <i>viewmodel</i> or <i>domain-service</i>
* to act as contributer of a single <i>domain-action</i> or
* <i>domain-property</i> or <i>domain-collection</i>.
*/
MIXIN,
MIXIN(new BeanPolicy(BeanPolicy.INTROSPECTION | BeanPolicy.WRAPPING)),
/**
* Immutable, serializable object.
* Values (including enums) my have object support methods, hence needs introspection.
*/
VALUE,
VALUE(new BeanPolicy(BeanPolicy.INTROSPECTION | BeanPolicy.ELEMENTTYPE)),
/**
* Container of objects.
*/
COLLECTION,
COLLECTION(new BeanPolicy(0)),
/**
* A non concrete type, that is a placeholder for a its concrete implementer.
* A non concrete type, that is a placeholder for a concrete implementer.
* <p>
* E.g. action return types or collection element types could be interfaces or abstract types
* (as discovered by reflection during introspection)
*/
ABSTRACT,
ABSTRACT(new BeanPolicy(BeanPolicy.INTROSPECTION | BeanPolicy.ELEMENTTYPE)),
/**
* Type must NOT be added to the meta-model, eg. by means of
* {@link org.apache.causeway.applib.annotation.Domain.Exclude} or {@link Programmatic}.
* Consequently the specification-loader should skip introspection of those types.
* <p>
* In other words: Indicates that the class is some bean,
* which is NOT contributing to the UI or WEB API, and
* also NOT managed by Spring.
* <p>
* {@link ServiceRegistry} will NOT be aware.
* <p>
* {@link FactoryService#create(Class)} will nevertheless support
* those programmatic beans. They may have injection points that need resolving.
*/
PROGRAMMATIC(new BeanPolicy(BeanPolicy.WRAPPING)),
/**
* Type must not be added to the meta-model, eg. by means of
* {@link org.apache.causeway.applib.annotation.Domain.Exclude}, {@link Profile} or {@link Programmatic}
* {@link javax.enterprise.inject.Vetoed} or {@link Profile}.
* Consequently the specification-loader should skip introspection of those types.
* If discovered by Spring during class-path scanning, we remove the corresponding {@link BeanDefinition}.
* <p>
* {@link ServiceRegistry} must not be aware of those types.
* <p>
* {@link FactoryService#getOrCreate(Class)} must fail for those types.
*/
VETOED,
UNKNOWN;
@SuppressWarnings("javadoc")
VETOED(new BeanPolicy(0)),
UNKNOWN(new BeanPolicy(BeanPolicy.ELEMENTTYPE));

public record BeanPolicy(int flags) {
final static int INTROSPECTION = 0x01;
final static int INJECTABLE = 0x02;
final static int WRAPPING = 0x04;
final static int ELEMENTTYPE = 0x08;
final static int NON_SUBSTITUTABLE = 0x10;
/**
* Enables meta-model introspection.
* Potentially contributes to the UI or Web API.
*/
public boolean isIntrospectionAllowed() { return (flags & INTROSPECTION) !=0; }
/**
* Contributes actions, members and/or object support to the UI or Web API.
*/
public boolean contributesToUiOrWebApi() { return isIntrospectionAllowed(); } // seems to be just a synonym
/**
* Whether corresponding type is in principle injectable,
* that is, provided it is also registered with a Spring context.
*/
public boolean isInjectable() { return (flags & INJECTABLE) !=0; }
public boolean isWrappingSupported() { return (flags & WRAPPING) !=0; }
public boolean isAllowedAsMemberElementType() { return (flags & ELEMENTTYPE) !=0; }
public boolean isNotSubstitutable() { return (flags & NON_SUBSTITUTABLE) !=0; }
}

// -- SIMPLE PREDICATES
// -- POLICY

public boolean isManagedBeanContributing() {
return this == MANAGED_BEAN_CONTRIBUTING;
}
private final BeanPolicy policy;
private BeanSort(final BeanPolicy policy) { this.policy = policy;}
public BeanPolicy policy() { return policy; }

public boolean isManagedBeanNotContributing() {
return this == MANAGED_BEAN_NOT_CONTRIBUTING;
}
// -- PREDICATES

public boolean isManagedBeanContributing() { return this == MANAGED_BEAN_CONTRIBUTING; }
public boolean isManagedBeanNotContributing() { return this == MANAGED_BEAN_NOT_CONTRIBUTING; }
public boolean isMixin() { return this == MIXIN; }
public boolean isViewModel() { return this == VIEW_MODEL; }
public boolean isValue() { return this == VALUE; }
public boolean isCollection() { return this == COLLECTION; }
public boolean isEntity() { return this == ENTITY; }
public boolean isAbstract() { return this == ABSTRACT; }
public boolean isProgrammatic() { return this == PROGRAMMATIC; }
public boolean isVetoed() { return this == VETOED; }
public boolean isUnknown() { return this == UNKNOWN; }

public boolean isManagedBeanAny() {
return this == MANAGED_BEAN_CONTRIBUTING
|| this == MANAGED_BEAN_NOT_CONTRIBUTING;
}

public boolean isMixin() {
return this == MIXIN;
}

public boolean isViewModel() {
return this == VIEW_MODEL;
}

public boolean isValue() {
return this == VALUE;
}

public boolean isCollection() {
return this == COLLECTION;
}

public boolean isEntity() {
return this == ENTITY;
}

public boolean isAbstract() {
return this == ABSTRACT;
}

public boolean isVetoed() {
return this == VETOED;
}

public boolean isUnknown() {
return this == UNKNOWN;
}

// -- HIGHER LEVEL PREDICATES

public boolean isToBeIntrospected() {

if(isVetoed()
|| isUnknown()
|| isCollection()
|| this == MANAGED_BEAN_NOT_CONTRIBUTING) {
return false;
}

return true;
}

public boolean isWrappingSupported() {
return isMixin() || isViewModel() || isEntity() || isManagedBeanAny();
}

// ...
}
1 change: 0 additions & 1 deletion commons/src/main/java/module-info.java
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@
exports org.apache.causeway.commons.tabular;
// internals exported as well
exports org.apache.causeway.commons.internal;
exports org.apache.causeway.commons.internal.annotations;
exports org.apache.causeway.commons.internal.assertions;
exports org.apache.causeway.commons.internal.base;
exports org.apache.causeway.commons.internal.binding;
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
Expand All @@ -38,15 +37,14 @@
import jakarta.xml.bind.annotation.XmlRootElement;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
import org.springframework.core.annotation.MergedAnnotations;
import org.springframework.core.annotation.MergedAnnotations.SearchStrategy;
import org.springframework.lang.Nullable;

import org.apache.causeway.commons.collections.Can;
import org.apache.causeway.commons.functional.Try;
import org.apache.causeway.commons.internal._Constants;
import org.apache.causeway.commons.internal.annotations.BeanInternal;
import org.apache.causeway.commons.internal.assertions._Assert;
import org.apache.causeway.commons.internal.base._Casts;
import org.apache.causeway.commons.internal.base._Lazy;
Expand Down Expand Up @@ -92,19 +90,19 @@ public enum Attribute {
MIXIN_MAIN_METHOD_NAME;
}

@Nullable private final ClassLoader classLoader;
private static _ClassCache defaultInstance() { return new _ClassCache(_Context.getDefaultClassLoader()); }
public static _ClassCache getInstance() {
return _Context.computeIfAbsent(_ClassCache.class, ()->new _ClassCache(_Context.getDefaultClassLoader()));
return _Context.computeIfAbsent(_ClassCache.class, ()->defaultInstance());
}

/**
* JUnit support.
*/
public static void invalidate() {
_Context.put(_ClassCache.class, new _ClassCache(_Context.getDefaultClassLoader()), true);
_Context.put(_ClassCache.class, defaultInstance(), true);
}

private final @Nullable ClassLoader classLoader;

public void add(final Class<?> type) {
classModel(type);
}
Expand Down Expand Up @@ -265,7 +263,8 @@ public record ClassModelHead(

static ClassModelHead create(final Class<?> type) {
var mergedAnnotations = MergedAnnotations.from(type, SearchStrategy.TYPE_HIERARCHY);
return new ClassModelHead(mergedAnnotations, _ClassCacheUtil.inferName(type, mergedAnnotations),
return new ClassModelHead(mergedAnnotations,
_ClassCacheUtil.inferName(type, mergedAnnotations),
new ConcurrentHashMap<>());
}

Expand All @@ -281,14 +280,6 @@ public boolean hasAnnotation(final Class<? extends Annotation> annotationType) {
return mergedAnnotations.get(annotationType).isPresent();
}

/**
* whether type is annotated with {@link BeanInternal} or {@link Configuration}
*/
public boolean hasInternalBeanSemantics() {
return hasAnnotation(BeanInternal.class)
|| hasAnnotation(Configuration.class);
}

/**
* whether type is annotated with {@link XmlRootElement}
*/
Expand All @@ -304,6 +295,12 @@ public boolean isJdoPersistenceCapable() {
&& !_ClassCacheUtil.isJdoEmbeddedOnly(mergedAnnotations);
}

public Can<String> springProfiles() {
var profileAnnot = mergedAnnotations.get(Profile.class);
if(!profileAnnot.isPresent()) return Can.empty();
return Can.ofArray(profileAnnot.getStringArray("value"));
}

}

private record ClassModelBody(
Expand All @@ -323,13 +320,12 @@ private record ClassModelBody(
new HashMap<>(), new HashMap<>(), new HashMap<>());
}

private static ClassModelBody EMPTY = new ClassModelBody(
Can.empty(),
Collections.emptyMap(), Collections.emptyMap(), Collections.emptyMap(),
Collections.emptyMap(), Collections.emptyMap(), Collections.emptyMap());
// private static ClassModelBody EMPTY = new ClassModelBody(
// Can.empty(),
// Collections.emptyMap(), Collections.emptyMap(), Collections.emptyMap(),
// Collections.emptyMap(), Collections.emptyMap(), Collections.emptyMap());

private static ClassModelBody create(final Class<?> type, final ClassModelHead head) {
if(head.hasInternalBeanSemantics()) return EMPTY; //skip further inspection

var body = new ClassModelBody(Can.ofArray(type.getDeclaredFields()));

Expand Down
Loading

0 comments on commit 77488e3

Please sign in to comment.