package io.ebeaninternal.server.core.bootup;
import io.ebean.annotation.DocStore;
import io.ebean.config.IdGenerator;
import io.ebean.config.ScalarTypeConverter;
import io.ebean.config.ServerConfig;
import io.ebean.event.BeanFindController;
import io.ebean.event.BeanPersistController;
import io.ebean.event.BeanPersistListener;
import io.ebean.event.BeanPostConstructListener;
import io.ebean.event.BeanPostLoad;
import io.ebean.event.BeanQueryAdapter;
import io.ebean.event.ServerConfigStartup;
import io.ebean.event.changelog.ChangeLogListener;
import io.ebean.event.changelog.ChangeLogPrepare;
import io.ebean.event.changelog.ChangeLogRegister;
import io.ebean.event.readaudit.ReadAuditLogger;
import io.ebean.event.readaudit.ReadAuditPrepare;
import io.ebean.util.AnnotationUtil;
import io.ebeaninternal.server.type.ScalarType;
import org.avaje.classpath.scanner.ClassFilter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.persistence.AttributeConverter;
import javax.persistence.Embeddable;
import javax.persistence.Entity;
import javax.persistence.Table;
import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.List;
Interesting classes for a EbeanServer such as Embeddable, Entity,
ScalarTypes, Finders, Listeners and Controllers.
/**
* Interesting classes for a EbeanServer such as Embeddable, Entity,
* ScalarTypes, Finders, Listeners and Controllers.
*/
public class BootupClasses implements ClassFilter {
private static final Logger logger = LoggerFactory.getLogger(BootupClasses.class);
private final List<Class<?>> embeddableList = new ArrayList<>();
private final List<Class<?>> entityList = new ArrayList<>();
private final List<Class<? extends ScalarType<?>>> scalarTypeList = new ArrayList<>();
private final List<Class<? extends ScalarTypeConverter<?, ?>>> scalarConverterList = new ArrayList<>();
private final List<Class<? extends AttributeConverter<?, ?>>> attributeConverterList = new ArrayList<>();
// The following objects are instantiated on first request
// there is always a candidate list, that holds the class and an
// instance list, that holds the instance. Once a class is instantiated
// (or added) it will get removed from the candidate list
private final List<Class<? extends IdGenerator>> idGeneratorCandidates = new ArrayList<>();
private final List<Class<? extends BeanPersistController>> beanPersistControllerCandidates = new ArrayList<>();
private final List<Class<? extends BeanPostLoad>> beanPostLoadCandidates = new ArrayList<>();
private final List<Class<? extends BeanPostConstructListener>> beanPostConstructListenerCandidates = new ArrayList<>();
private final List<Class<? extends BeanFindController>> beanFindControllerCandidates = new ArrayList<>();
private final List<Class<? extends BeanPersistListener>> beanPersistListenerCandidates = new ArrayList<>();
private final List<Class<? extends BeanQueryAdapter>> beanQueryAdapterCandidates = new ArrayList<>();
private final List<Class<? extends ServerConfigStartup>> serverConfigStartupCandidates = new ArrayList<>();
private final List<IdGenerator> idGeneratorInstances = new ArrayList<>();
private final List<BeanPersistController> beanPersistControllerInstances = new ArrayList<>();
private final List<BeanPostLoad> beanPostLoadInstances = new ArrayList<>();
private final List<BeanPostConstructListener> beanPostConstructListenerInstances = new ArrayList<>();
private final List<BeanFindController> beanFindControllerInstances = new ArrayList<>();
private final List<BeanPersistListener> beanPersistListenerInstances = new ArrayList<>();
private final List<BeanQueryAdapter> beanQueryAdapterInstances = new ArrayList<>();
private final List<ServerConfigStartup> serverConfigStartupInstances = new ArrayList<>();
// single objects
private Class<? extends ChangeLogPrepare> changeLogPrepareClass;
private Class<? extends ChangeLogListener> changeLogListenerClass;
private Class<? extends ChangeLogRegister> changeLogRegisterClass;
private Class<? extends ReadAuditPrepare> readAuditPrepareClass;
private Class<? extends ReadAuditLogger> readAuditLoggerClass;
private ChangeLogPrepare changeLogPrepare;
private ChangeLogListener changeLogListener;
private ChangeLogRegister changeLogRegister;
private ReadAuditPrepare readAuditPrepare;
private ReadAuditLogger readAuditLogger;
public BootupClasses() {
}
public BootupClasses(List<Class<?>> list) {
if (list != null) {
for (Class<?> cls : list) {
isMatch(cls);
}
}
}
Run any ServerConfigStartup listeners.
/**
* Run any ServerConfigStartup listeners.
*/
public void runServerConfigStartup(ServerConfig serverConfig) {
for (Class<?> cls : serverConfigStartupCandidates) {
try {
ServerConfigStartup newInstance = (ServerConfigStartup) cls.newInstance();
newInstance.onStart(serverConfig);
} catch (Exception e) {
// assume that the desired behavior is to fail - add your own try catch if needed
throw new IllegalStateException("Error running ServerConfigStartup " + cls, e);
}
}
for (ServerConfigStartup startup : serverConfigStartupInstances) {
try {
startup.onStart(serverConfig);
} catch (Exception e) {
// assume that the desired behavior is to fail - add your own try catch if needed
throw new IllegalStateException("Error running ServerConfigStartup " + startup.getClass(), e);
}
}
}
Adds the list toAdd
to instances
and removes any pending
candiate, to prevent duplicate instantiiation.
/**
* Adds the list <code>toAdd</code> to <code>instances</code> and removes any pending
* candiate, to prevent duplicate instantiiation.
*/
private <T> void add(List<T> toAdd, List<T> instances, List<Class<? extends T>> candidates) {
if (toAdd != null) {
for (T obj : toAdd) {
instances.add(obj);
// don't automatically instantiate
candidates.remove(obj.getClass());
}
}
}
Add IdGenerator instances (registered explicitly with the ServerConfig).
/**
* Add IdGenerator instances (registered explicitly with the ServerConfig).
*/
public void addIdGenerators(List<IdGenerator> idGenerators) {
add(idGenerators, idGeneratorInstances, idGeneratorCandidates);
}
Add BeanPersistController instances.
/**
* Add BeanPersistController instances.
*/
public void addPersistControllers(List<BeanPersistController> beanControllers) {
add(beanControllers, beanPersistControllerInstances, beanPersistControllerCandidates);
}
Add BeanPostLoad instances.
/**
* Add BeanPostLoad instances.
*/
public void addPostLoaders(List<BeanPostLoad> postLoaders) {
add(postLoaders, beanPostLoadInstances, beanPostLoadCandidates);
}
Add BeanPostConstructListener instances.
/**
* Add BeanPostConstructListener instances.
*/
public void addPostConstructListeners(List<BeanPostConstructListener> postConstructListener) {
add(postConstructListener, beanPostConstructListenerInstances, beanPostConstructListenerCandidates);
}
Add BeanFindController instances.
/**
* Add BeanFindController instances.
*/
public void addFindControllers(List<BeanFindController> findControllers) {
add(findControllers, beanFindControllerInstances, beanFindControllerCandidates);
}
public void addPersistListeners(List<BeanPersistListener> listenerInstances) {
add(listenerInstances, beanPersistListenerInstances, beanPersistListenerCandidates);
}
public void addQueryAdapters(List<BeanQueryAdapter> queryAdapters) {
add(queryAdapters, beanQueryAdapterInstances, beanQueryAdapterCandidates);
}
public void addServerConfigStartup(List<ServerConfigStartup> startupInstances) {
add(startupInstances, serverConfigStartupInstances, serverConfigStartupCandidates);
}
public void addChangeLogInstances(ServerConfig serverConfig) {
readAuditPrepare = serverConfig.getReadAuditPrepare();
readAuditLogger = serverConfig.getReadAuditLogger();
changeLogPrepare = serverConfig.getChangeLogPrepare();
changeLogListener = serverConfig.getChangeLogListener();
changeLogRegister = serverConfig.getChangeLogRegister();
// if not already set create the implementations found
// via classpath scanning
if (readAuditPrepare == null && readAuditPrepareClass != null) {
readAuditPrepare = create(readAuditPrepareClass, false);
}
if (readAuditLogger == null && readAuditLoggerClass != null) {
readAuditLogger = create(readAuditLoggerClass, false);
}
if (changeLogPrepare == null && changeLogPrepareClass != null) {
changeLogPrepare = create(changeLogPrepareClass, false);
}
if (changeLogListener == null && changeLogListenerClass != null) {
changeLogListener = create(changeLogListenerClass, false);
}
if (changeLogRegister == null && changeLogRegisterClass != null) {
changeLogRegister = create(changeLogRegisterClass, false);
}
}
Create an instance using the default constructor returning null if there
is no default constructor (implying the class was not intended to be instantiated
automatically via classpath scanning.
Use logOnException = true to log the error and carry on.
/**
* Create an instance using the default constructor returning null if there
* is no default constructor (implying the class was not intended to be instantiated
* automatically via classpath scanning.
* <p>
* Use logOnException = true to log the error and carry on.
*/
private <T> T create(Class<T> cls, boolean logOnException) {
try {
// instantiate via found class
Constructor<T> constructor = cls.getConstructor();
return constructor.newInstance();
} catch (NoSuchMethodException e) {
logger.debug("Ignore/expected - no default constructor", e);
return null;
} catch (Exception e) {
if (logOnException) {
// not expected but we log and carry on
logger.error("Error creating " + cls, e);
return null;
} else {
// ok, stop the bus
throw new IllegalStateException("Error creating " + cls, e);
}
}
}
Create the instance if it has a default constructor and add it to the list of instances.
It clears the list of classes afterwards, so that each class in the given list is
instantiated only once
/**
* Create the instance if it has a default constructor and add it to the list of instances.
* It clears the list of classes afterwards, so that each class in the given list is
* instantiated only once
*/
private <T> List<T> createAdd(List<T> instances, List<Class<? extends T>> candidates) {
for (Class<? extends T> cls : candidates) {
T newInstance = create(cls, true);
if (newInstance != null) {
instances.add(newInstance);
}
}
candidates.clear(); // important, clear class list!
return instances;
}
public ChangeLogPrepare getChangeLogPrepare() {
return changeLogPrepare;
}
public ChangeLogListener getChangeLogListener() {
return changeLogListener;
}
public ChangeLogRegister getChangeLogRegister() {
return changeLogRegister;
}
public ReadAuditPrepare getReadAuditPrepare() {
return readAuditPrepare;
}
public ReadAuditLogger getReadAuditLogger() {
return readAuditLogger;
}
public List<IdGenerator> getIdGenerators() {
return createAdd(idGeneratorInstances, idGeneratorCandidates);
}
public List<BeanPersistController> getBeanPersistControllers() {
return createAdd(beanPersistControllerInstances, beanPersistControllerCandidates);
}
public List<BeanPostLoad> getBeanPostLoaders() {
return createAdd(beanPostLoadInstances, beanPostLoadCandidates);
}
public List<BeanPostConstructListener> getBeanPostConstructoListeners() {
return createAdd(beanPostConstructListenerInstances, beanPostConstructListenerCandidates);
}
public List<BeanFindController> getBeanFindControllers() {
return createAdd(beanFindControllerInstances, beanFindControllerCandidates);
}
public List<BeanPersistListener> getBeanPersistListeners() {
return createAdd(beanPersistListenerInstances, beanPersistListenerCandidates);
}
public List<BeanQueryAdapter> getBeanQueryAdapters() {
return createAdd(beanQueryAdapterInstances, beanQueryAdapterCandidates);
}
Return the list of Embeddable classes.
/**
* Return the list of Embeddable classes.
*/
public List<Class<?>> getEmbeddables() {
return embeddableList;
}
Return the list of entity classes.
/**
* Return the list of entity classes.
*/
public List<Class<?>> getEntities() {
return entityList;
}
Return the list of ScalarTypes found.
/**
* Return the list of ScalarTypes found.
*/
public List<Class<? extends ScalarType<?>>> getScalarTypes() {
return scalarTypeList;
}
Return the list of ScalarConverters found.
/**
* Return the list of ScalarConverters found.
*/
public List<Class<? extends ScalarTypeConverter<?, ?>>> getScalarConverters() {
return scalarConverterList;
}
Return the list of AttributeConverters found.
/**
* Return the list of AttributeConverters found.
*/
public List<Class<? extends AttributeConverter<?, ?>>> getAttributeConverters() {
return attributeConverterList;
}
@Override
public boolean isMatch(Class<?> cls) {
if (isEmbeddable(cls)) {
embeddableList.add(cls);
} else if (isEntity(cls)) {
entityList.add(cls);
} else {
return isInterestingInterface(cls);
}
return true;
}
Look for interesting interfaces.
This includes ScalarType, BeanController, BeanFinder and BeanListener.
/**
* Look for interesting interfaces.
* <p>
* This includes ScalarType, BeanController, BeanFinder and BeanListener.
* </p>
*/
@SuppressWarnings("unchecked")
private boolean isInterestingInterface(Class<?> cls) {
if (Modifier.isAbstract(cls.getModifiers())) {
// do not include abstract classes as we can
// not instantiate them
return false;
}
boolean interesting = false;
// Types
if (ScalarType.class.isAssignableFrom(cls)) {
scalarTypeList.add((Class<? extends ScalarType<?>>) cls);
interesting = true;
}
if (ScalarTypeConverter.class.isAssignableFrom(cls)) {
scalarConverterList.add((Class<? extends ScalarTypeConverter<?, ?>>) cls);
interesting = true;
}
if (AttributeConverter.class.isAssignableFrom(cls)) {
attributeConverterList.add((Class<? extends AttributeConverter<?, ?>>) cls);
interesting = true;
}
if (IdGenerator.class.isAssignableFrom(cls)) {
idGeneratorCandidates.add((Class<? extends IdGenerator>) cls);
interesting = true;
}
// "Candidates"
if (BeanPersistController.class.isAssignableFrom(cls)) {
beanPersistControllerCandidates.add((Class<? extends BeanPersistController>) cls);
interesting = true;
}
if (BeanPostLoad.class.isAssignableFrom(cls)) {
beanPostLoadCandidates.add((Class<? extends BeanPostLoad>) cls);
interesting = true;
}
if (BeanPostConstructListener.class.isAssignableFrom(cls)) {
beanPostConstructListenerCandidates.add((Class<? extends BeanPostConstructListener>) cls);
interesting = true;
}
if (BeanFindController.class.isAssignableFrom(cls)) {
beanFindControllerCandidates.add((Class<? extends BeanFindController>) cls);
interesting = true;
}
if (BeanPersistListener.class.isAssignableFrom(cls)) {
beanPersistListenerCandidates.add((Class<? extends BeanPersistListener>) cls);
interesting = true;
}
if (BeanQueryAdapter.class.isAssignableFrom(cls)) {
beanQueryAdapterCandidates.add((Class<? extends BeanQueryAdapter>) cls);
interesting = true;
}
if (ServerConfigStartup.class.isAssignableFrom(cls)) {
serverConfigStartupCandidates.add((Class<? extends ServerConfigStartup>) cls);
interesting = true;
}
// single instances
// TODO: What should happen, if there is already an other
// changeLogListener assigned? (Last wins? / Exception?)
if (ChangeLogListener.class.isAssignableFrom(cls)) {
changeLogListenerClass = (Class<? extends ChangeLogListener>) cls;
interesting = true;
}
if (ChangeLogRegister.class.isAssignableFrom(cls)) {
changeLogRegisterClass = (Class<? extends ChangeLogRegister>) cls;
interesting = true;
}
if (ChangeLogPrepare.class.isAssignableFrom(cls)) {
changeLogPrepareClass = (Class<? extends ChangeLogPrepare>) cls;
interesting = true;
}
if (ReadAuditPrepare.class.isAssignableFrom(cls)) {
readAuditPrepareClass = (Class<? extends ReadAuditPrepare>) cls;
interesting = true;
}
if (ReadAuditLogger.class.isAssignableFrom(cls)) {
readAuditLoggerClass = (Class<? extends ReadAuditLogger>) cls;
interesting = true;
}
return interesting;
}
private boolean isEntity(Class<?> cls) {
return has(cls, Entity.class) || has(cls, Table.class) || has(cls, DocStore.class);
}
private boolean isEmbeddable(Class<?> cls) {
return has(cls, Embeddable.class);
}
Returns true if this class has the annotation (or meta annotation). Does not search recursively.
/**
* Returns true if this class has the annotation (or meta annotation). Does not search recursively.
*/
private boolean has(Class<?> cls, Class<? extends Annotation> ann) {
return AnnotationUtil.findAnnotation(cls, ann) != null;
}
}