Skip to content

Commit

Permalink
Allow additional persistent bean registration outside of the actual b…
Browse files Browse the repository at this point in the history
…ean types (if you want to use the bean types in some API)
  • Loading branch information
SimonDan committed May 9, 2020
1 parent 336c314 commit 16bc971
Show file tree
Hide file tree
Showing 9 changed files with 218 additions and 37 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package de.adito.ojcms.persistence;

import de.adito.ojcms.beans.IBean;

import java.lang.annotation.*;

/**
* Allows to register an additional persistent bean.
*
* @author Simon Danner, 09.05.2020
* @see AdditionalPersistConfiguration
*/
@Repeatable(AdditionalPersist.Multiple.class)
@Documented
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface AdditionalPersist
{
/**
* The type of the persistent bean.
*/
Class<? extends IBean> beanType();

/**
* The id of the container in that the bean/beans are stored.
*
* @return the container id
*/
String containerId();

/**
* The mode in which the beans should be persisted.
* Default: they will be stored in a container.
*
* @return the persistence mode to use
*/
EPersistenceMode mode() default EPersistenceMode.CONTAINER;

/**
* Required to repeat this annotation.
*/
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@interface Multiple
{
AdditionalPersist[] value();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package de.adito.ojcms.persistence;

import de.adito.ojcms.beans.IBean;

import java.lang.annotation.*;

/**
* Allows to register an additional persistent bean for some sub types.
*
* @author Simon Danner, 09.05.2020
* @see AdditionalPersistConfiguration
*/
@Repeatable(AdditionalPersistAsBaseType.Multiple.class)
@Documented
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface AdditionalPersistAsBaseType
{
/**
* The base type of the persistent bean.
*/
Class<? extends IBean> baseType();

/**
* The container id of the element, in which the bean/beans are stored.
*
* @return the container id
*/
String containerId();

/**
* All sub types the base type container is for. The base type itself can be included if it is not abstract.
* At least two types must be provided. The given types must be assignable from the base bean type.
*
* @return an array of bean sub types the base container should be for
*/
Class<? extends IBean>[] forSubTypes();

/**
* Required to repeat this annotation.
*/
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@interface Multiple
{
AdditionalPersistAsBaseType[] value();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package de.adito.ojcms.persistence;

/**
* Marks a class to provide additional registrations for persistent beans.
* Use {@link AdditionalPersist} or {@link AdditionalPersistAsBaseType} to define additional persistent beans.
* This is useful if you want to use bean types in some API modules and also want to use them as persistent beans in the server module.
*
* @author Simon Danner, 09.05.2020
*/
public interface AdditionalPersistConfiguration
{
}
Original file line number Diff line number Diff line change
Expand Up @@ -43,9 +43,30 @@ void findPersistentBeans(@Observes ProcessAnnotatedType<? extends IBean> pProces
final Class<? extends IBean> beanType = (Class<? extends IBean>) type.getBaseType();

if (type.isAnnotationPresent(Persist.class))
_registerPersistentBeanType(type.getAnnotation(Persist.class), beanType, pProcessedBean);
{
final Persist annotation = type.getAnnotation(Persist.class);
_registerPersistentBeanType(beanType, annotation.mode(), annotation.containerId());

if (annotation.mode() == EPersistenceMode.SINGLE)
pProcessedBean.veto(); //Veto single beans and use custom producer instead
}
else if (type.isAnnotationPresent(PersistAsBaseType.class))
_registerPersistentBaseContainer(type.getAnnotation(PersistAsBaseType.class), beanType);
{
final PersistAsBaseType annotation = type.getAnnotation(PersistAsBaseType.class);
_registerPersistentBaseContainer(beanType, annotation.forSubTypes(), annotation.containerId());
}
}

/**
* Scans every {@link AdditionalPersistConfiguration} in the classpath and registers declared additional persistent beans.
*/
void findAdditionalConfigurations(@Observes ProcessAnnotatedType<? extends AdditionalPersistConfiguration> pProcessedType)
{
for (AdditionalPersist additional : pProcessedType.getAnnotatedType().getAnnotations(AdditionalPersist.class))
_registerPersistentBeanType(additional.beanType(), additional.mode(), additional.containerId());

for (AdditionalPersistAsBaseType additional : pProcessedType.getAnnotatedType().getAnnotations(AdditionalPersistAsBaseType.class))
_registerPersistentBaseContainer(additional.baseType(), additional.forSubTypes(), additional.containerId());
}

/**
Expand All @@ -61,59 +82,56 @@ void afterBeanDiscovery(@Observes AfterBeanDiscovery pAfterBeanDiscovery)
}

/**
* Registers a persistent bean annotated by {@link Persist}.
* If the bean should be persisted in {@link EPersistenceMode#SINGLE} the processed bean will be vetoed.
* The producer should be used instead.
* Registers a persistent bean type.
*
* @param pAnnotation the persistence annotation instance to retrieve configuration from
* @param pBeanType the annotated bean type
* @param pProcessedBean the processed CDI bean
* @param pBeanType the annotated bean type
* @param pPersistenceMode the persistence mode of the bean type to register
* @param pContainerId the container id to use for the persistent bean
*/
private static void _registerPersistentBeanType(Persist pAnnotation, Class<? extends IBean> pBeanType,
ProcessAnnotatedType<? extends IBean> pProcessedBean)
private static void _registerPersistentBeanType(Class<? extends IBean> pBeanType, EPersistenceMode pPersistenceMode, String pContainerId)
{
if (Modifier.isAbstract(pBeanType.getModifiers()))
throw new OJPersistenceException("Abstract bean type " + pBeanType.getName() + " cannot be persisted!");

if (pAnnotation.mode() == EPersistenceMode.CONTAINER)
CONTAINER_BEAN_TYPES.put(pBeanType, pAnnotation.containerId());
if (pPersistenceMode == EPersistenceMode.CONTAINER)
CONTAINER_BEAN_TYPES.put(pBeanType, pContainerId);
else
{
SINGLE_BEAN_TYPES.put(pBeanType, pAnnotation.containerId());
pProcessedBean.veto();
}
SINGLE_BEAN_TYPES.put(pBeanType, pContainerId);
}

/**
* Registers a persistent base bean type annotated by {@link PersistAsBaseType}.
* A base container will be registered for all {@link PersistAsBaseType#forSubTypes()}.
* Registers a persistent base bean type.
* A base container will be registered for all provided sub types.
* Checks several conditions before registering the bean type.
*
* @param pAnnotation the persistence annotation instance to retrieve configuration from
* @param pBeanType the annotated base bean type
* @param pBeanBaseType the annotated base bean type
* @param pForSubTypes the set of bean sub types the container is for
* @param pContainerId the id of the container
*/
private static void _registerPersistentBaseContainer(PersistAsBaseType pAnnotation, Class<? extends IBean> pBeanType)
private static void _registerPersistentBaseContainer(Class<? extends IBean> pBeanBaseType, Class<? extends IBean>[] pForSubTypes,
String pContainerId)
{
final Set<Class<? extends IBean>> subTypes = new HashSet<>();

if (pAnnotation.forSubTypes().length < 2)
throw new OJPersistenceException("At least two sub types must be given to create a persistent container for " + pBeanType.getName());
if (pForSubTypes.length < 2)
throw new OJPersistenceException(
"At least two sub types must be given to create a persistent container for " + pBeanBaseType.getName());

for (Class<? extends IBean> subType : pAnnotation.forSubTypes())
for (Class<? extends IBean> subType : pForSubTypes)
{
if (Modifier.isAbstract(subType.getModifiers()))
throw new OJPersistenceException(
"Sub bean type " + subType.getName() + " is abstract! " + "Cannot be used for base container " + pBeanType.getName());
"Sub bean type " + subType.getName() + " is abstract! " + "Cannot be used for base container " + pBeanBaseType.getName());

if (!pBeanType.isAssignableFrom(subType))
throw new OJPersistenceException("Sub bean type " + subType.getName() + " is not assignable from " + pBeanType
if (!pBeanBaseType.isAssignableFrom(subType))
throw new OJPersistenceException("Sub bean type " + subType.getName() + " is not assignable from " + pBeanBaseType
.getName() + "!" + "Cannot be used for base container!");

subTypes.add(subType);
}

final BaseContainerRegistration registration = new BaseContainerRegistration(pAnnotation.containerId(), subTypes);
BASE_CONTAINER_TYPES.put(pBeanType, registration);
final BaseContainerRegistration registration = new BaseContainerRegistration(pContainerId, subTypes);
BASE_CONTAINER_TYPES.put(pBeanBaseType, registration);
}

/**
Expand Down Expand Up @@ -142,7 +160,7 @@ private static void _addContainerCdiBeans(AfterBeanDiscovery pAfterBeanDiscovery
* @param pRegistration information about the registered base bean container
*/
private static void _addBaseContainerCdiBeans(AfterBeanDiscovery pAfterBeanDiscovery, Class<? extends IBean> pBeanBaseType,
BaseContainerRegistration pRegistration)
BaseContainerRegistration pRegistration)
{
final ContainerQualifier literal = ContainerQualifier.Literal.forContainerId(pRegistration.getContainerId());
_registerContainerCdiBean(pAfterBeanDiscovery, pBeanBaseType, literal);
Expand Down Expand Up @@ -183,7 +201,7 @@ private static void _addSingleBeanCdiBeans(AfterBeanDiscovery pAfterBeanDiscover
* @param pLiteral a CDI literal to identify the container content
*/
private static void _registerContainerCdiBean(AfterBeanDiscovery pAfterBeanDiscovery, Class<? extends IBean> pBeanType,
ContainerQualifier pLiteral)
ContainerQualifier pLiteral)
{
//noinspection unchecked
pAfterBeanDiscovery.addBean() //
Expand All @@ -201,7 +219,7 @@ private static void _registerContainerCdiBean(AfterBeanDiscovery pAfterBeanDisco
* @param pLiteral a CDI literal to identify the container content
*/
private static void _registerContainerContentCdiBean(AfterBeanDiscovery pAfterBeanDiscovery, String pContainerId, //
@Nullable Class<? extends IBean> pBeanType, ContainerQualifier pLiteral)
@Nullable Class<? extends IBean> pBeanType, ContainerQualifier pLiteral)
{
//noinspection unchecked
pAfterBeanDiscovery.addBean() //
Expand All @@ -220,7 +238,7 @@ private static void _registerContainerContentCdiBean(AfterBeanDiscovery pAfterBe
* @return the created bean container instance
*/
private static <BEAN extends IBean> IBeanContainer<BEAN> _createBeanContainer(Instance<Object> pEnvironment, Class<BEAN> pBeanType,
ContainerQualifier pLiteral)
ContainerQualifier pLiteral)
{
//noinspection unchecked
final ContainerContent<BEAN> content = pEnvironment.select(ContainerContent.class, pLiteral).get();
Expand All @@ -237,7 +255,7 @@ private static <BEAN extends IBean> IBeanContainer<BEAN> _createBeanContainer(In
* @return the created instance managing the persistent container's content
*/
private static <BEAN extends IBean> ContainerContent<BEAN> _createContainerContent(Instance<Object> pEnvironment, String pContainerId,
Class<BEAN> pBeanType)
Class<BEAN> pBeanType)
{
return new ContainerContent<>(pContainerId, pBeanType, pEnvironment.select(ITransaction.class).get());
}
Expand All @@ -251,7 +269,7 @@ private static <BEAN extends IBean> ContainerContent<BEAN> _createContainerConte
* @return the created single bean instance
*/
private static <BEAN extends IBean> BEAN _createSingleBean(Instance<Object> pEnvironment, Class<BEAN> pBeanType,
ContainerQualifier pLiteral)
ContainerQualifier pLiteral)
{
final SingleBeanContent content = pEnvironment.select(SingleBeanContent.class, pLiteral).get();
final PersistentBeanDatasource datasource = new PersistentBeanDatasource(content);
Expand All @@ -267,7 +285,7 @@ private static <BEAN extends IBean> BEAN _createSingleBean(Instance<Object> pEnv
* @return the created instance managing the bean's content
*/
private static SingleBeanContent<?> _createSingleBeanContent(Instance<Object> pEnvironment, String pBeanId,
Class<? extends IBean> pBeanType)
Class<? extends IBean> pBeanType)
{
final ITransaction transaction = pEnvironment.select(ITransaction.class).get();
return new SingleBeanContent<>(new SingleBeanKey(pBeanId), pBeanType, transaction);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package de.adito.ojcms.rest.application;

import de.adito.ojcms.persistence.AdditionalPersistConfiguration;
import de.adito.ojcms.rest.serialization.*;
import jakarta.ws.rs.*;
import jakarta.ws.rs.core.Application;
Expand All @@ -12,7 +13,7 @@
*
* @author Simon Danner, 02.04.2020
*/
public abstract class OJRestApplication extends Application
public abstract class OJRestApplication extends Application implements AdditionalPersistConfiguration
{
protected final Set<Class<?>> providerAndResourceTypes = new HashSet<>();
protected final Set<Object> providerAndResourceInstances = new HashSet<>();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
import jakarta.ws.rs.*;
import jakarta.ws.rs.core.*;

import javax.inject.Inject;

/**
* Rest resource for testing. Contains some secured methods.
* Also used to test bean serialization.
Expand All @@ -14,12 +16,16 @@ public class RestResourceForTest
{
private static UserForTest user;

@Inject
private SomeTestBusiness business;

@GET
@Produces(MediaType.TEXT_PLAIN)
@TestBoundary(requiresOneOfTheseRoles = EUserRoleForTest.ROLE1)
@Path("/secret")
public String getSomeSecretValue()
{
business.doIt(); //Do it to test additional persistent bean
return "42";
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package de.adito.ojcms.rest.testapplication;

import de.adito.ojcms.persistence.AdditionalPersist;
import de.adito.ojcms.rest.application.OJSecuredRestApplication;
import de.adito.ojcms.rest.auth.api.AuthenticationResponse;
import jakarta.ws.rs.ApplicationPath;
Expand All @@ -11,6 +12,7 @@
*
* @author Simon Danner, 22.04.2020
*/
@AdditionalPersist(beanType = SomeAdditionalTestBean.class, containerId = "ADDITIONAL")
@ApplicationPath("/")
public class SecuredApplicationForTest
extends OJSecuredRestApplication<TestBoundary, UserForTest, RegistrationRequestForTest, AuthenticationResponse>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package de.adito.ojcms.rest.testapplication;

import de.adito.ojcms.beans.*;
import de.adito.ojcms.beans.literals.fields.types.TextField;

/**
* Some business bean for test.
*
* @author Simon Danner, 09.05.2020
*/
public class SomeAdditionalTestBean extends OJBean
{
public static final TextField SOME_FIELD = OJFields.create(SomeAdditionalTestBean.class);

public SomeAdditionalTestBean()
{
setValue(SOME_FIELD, "someValue");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package de.adito.ojcms.rest.testapplication;

import de.adito.ojcms.beans.IBeanContainer;
import de.adito.ojcms.transactions.annotations.Transactional;

import javax.enterprise.context.ApplicationScoped;
import javax.inject.Inject;

/**
* Test business to test the additionally added persistent bean type.
*
* @author Simon Danner, 09.05.2020
*/
@ApplicationScoped
class SomeTestBusiness
{
@Inject
private IBeanContainer<SomeAdditionalTestBean> additionalBeans;

@Transactional
void doIt()
{
additionalBeans.addBean(new SomeAdditionalTestBean());
}
}

0 comments on commit 16bc971

Please sign in to comment.