io.devbench.uibuilder.spring.startup.RegistryInitializer Maven / Gradle / Ivy
/*
*
* Copyright © 2018 Webvalto Ltd.
*
* 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
*
* http://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 io.devbench.uibuilder.spring.startup;
import com.typesafe.config.Config;
import com.typesafe.config.ConfigFactory;
import io.devbench.uibuilder.annotations.ControllerBean;
import io.devbench.uibuilder.api.exceptions.InternalResolverException;
import io.devbench.uibuilder.api.exceptions.NoUniqueBeanNameFoundException;
import io.devbench.uibuilder.api.member.scanner.MemberScanner;
import io.devbench.uibuilder.api.parse.backendtemplate.BackendTemplateManager;
import io.devbench.uibuilder.components.i18n.pageloaderprocessor.CollectI18NMessagesProcessor;
import io.devbench.uibuilder.core.controllerbean.ControllerBeanManager;
import io.devbench.uibuilder.core.controllerbean.ControllerBeanProvidersHolder;
import io.devbench.uibuilder.core.page.PageLoader;
import io.devbench.uibuilder.core.page.pageloaderprocessors.AddJsModuleImportsProcessor;
import io.devbench.uibuilder.core.parse.PageTransformersRegistry;
import io.devbench.uibuilder.core.parse.ParseInterceptorsRegistry;
import io.devbench.uibuilder.core.startup.ComponentTagRegistry;
import io.devbench.uibuilder.data.api.datasource.DataSource;
import io.devbench.uibuilder.data.api.datasource.DataSourceManager;
import io.devbench.uibuilder.data.api.datasource.DataSourceProvider;
import io.devbench.uibuilder.data.api.datasource.DataSourceSelector;
import io.devbench.uibuilder.data.api.exceptions.DataSourceRegistryException;
import io.devbench.uibuilder.data.api.filter.validation.FilterValidationExecutor;
import io.devbench.uibuilder.i18n.core.I;
import io.devbench.uibuilder.i18n.core.interfaces.LanguageProvider;
import io.devbench.uibuilder.spring.configuration.basepackages.UIBuilderScanPackages;
import io.devbench.uibuilder.spring.data.BasicSpringDataSourceProvider;
import io.devbench.uibuilder.spring.data.SpringDataSourceProvider;
import io.devbench.uibuilder.spring.scanner.MemberComponentProvider;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider;
import org.springframework.core.type.filter.AnnotationTypeFilter;
import org.springframework.core.type.filter.AssignableTypeFilter;
import java.lang.annotation.Annotation;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;
@Slf4j
public class RegistryInitializer {
private final ApplicationContext applicationContext;
private final UIBuilderScanPackages scanPackages;
public RegistryInitializer(ApplicationContext applicationContext, UIBuilderScanPackages scanPackages) throws Exception {
this.applicationContext = applicationContext;
this.scanPackages = scanPackages;
initializeMemberScanner();
initializeComponentTagRegistry();
initializeDataSourceManager();
initializeParseInterceptors();
initializeBackendTemplateManager();
initializeLanguageProvider();
initializePageTransformers();
initializeControllerBeanManager();
initializeFilterValidatorFacory();
PageLoader.registerProcessorAfter(AddJsModuleImportsProcessor.class, new CollectI18NMessagesProcessor());
}
private void initializeFilterValidatorFacory() {
FilterValidationExecutor.registerToActiveContext();
}
private void initializeMemberScanner() {
MemberScanner.registerToActiveContext(
this::findClassesAnnotatedWith,
this::findClassesBySuperType
);
}
@SuppressWarnings("unchecked")
private void initializeDataSourceManager() {
SpringDataSourceProvider, ?> dataSourceProvider = findSpringDataSourceProvider();
Map, Set>> annotatedClasses = dataSourceProvider.getInterestedAnnotationTypes().stream()
.collect(Collectors.toMap(Function.identity(), annotationType -> findClassesAnnotatedWith(annotationType, true)));
dataSourceProvider.initialize(annotatedClasses);
DataSourceManager.registerToActiveContext((DataSourceProvider, ? super DataSourceSelector>) dataSourceProvider);
}
private SpringDataSourceProvider, ?> findSpringDataSourceProvider() {
List providerNames = Arrays.asList(applicationContext.getBeanNamesForType(SpringDataSourceProvider.class));
if (providerNames.size() == 1) {
return applicationContext.getBean(SpringDataSourceProvider.class);
} else {
String foundProviderName = providerNames.stream()
.filter(providerName -> !BasicSpringDataSourceProvider.BASIC_SPRING_DATA_SOURCE_PROVIDER_BEAN_NAME.equals(providerName))
.findFirst()
.orElseThrow(() -> new DataSourceRegistryException("No available data source provider found"));
return applicationContext.getBean(foundProviderName, SpringDataSourceProvider.class);
}
}
private void initializeLanguageProvider() throws ClassNotFoundException, IllegalAccessException, InstantiationException {
Config conf = ConfigFactory.load();
I.loadTranslations(conf.getStringList("translationsDir").get(0));
I.setLanguageProvider((LanguageProvider) Class.forName(conf.getString("activeLanguageProvider")).newInstance());
}
private void initializeComponentTagRegistry() {
ComponentTagRegistry.registerToActiveContext();
}
private void initializePageTransformers() {
PageTransformersRegistry.registerToActiveContext();
}
private void initializeParseInterceptors() {
ParseInterceptorsRegistry.registerToActiveContext();
}
private void initializeBackendTemplateManager() {
BackendTemplateManager.registerToActiveContext();
}
private void initializeControllerBeanManager() {
ControllerBeanProvidersHolder providersHolder = ControllerBeanProvidersHolder.builder()
.beanInstanceByClass(applicationContext::getBean)
.beanInstanceByName(applicationContext::getBean)
.beanNameByClass(this::getUniqueBeanNameForControllerBeanType)
.build();
ControllerBeanManager.registerToActiveContext(providersHolder, ControllerBean.class);
}
private Set> findClassesAnnotatedWith(Class extends Annotation> annotationType, boolean considerInterfaces) {
MemberComponentProvider provider = new MemberComponentProvider(considerInterfaces, applicationContext.getEnvironment());
provider.setResourceLoader(applicationContext);
provider.addIncludeFilter(new AnnotationTypeFilter(annotationType));
return findActualClassesByComponentProvider(provider).collect(Collectors.toSet());
}
@SuppressWarnings("unchecked")
private Set> findClassesBySuperType(Class superClass, boolean considerInterfaces) {
MemberComponentProvider provider = new MemberComponentProvider(considerInterfaces, applicationContext.getEnvironment());
provider.setResourceLoader(applicationContext);
provider.addIncludeFilter(new AssignableTypeFilter(superClass));
return findActualClassesByComponentProvider(provider)
.map(clazz -> ((Class extends SUPER_TYPE>) clazz))
.collect(Collectors.toSet());
}
private Stream> findActualClassesByComponentProvider(ClassPathScanningCandidateComponentProvider provider) {
return scanPackages.getScanPackages().stream()
.map(provider::findCandidateComponents)
.flatMap(Collection::stream)
.map(this::getBeanClass);
}
private String getUniqueBeanNameForControllerBeanType(Class> clazz) {
String[] beanNamesForType = applicationContext.getBeanNamesForType(clazz);
if (beanNamesForType.length == 1) {
return beanNamesForType[0];
} else {
return getControllerBeanNameFromAnnotation(clazz)
.orElseThrow(() -> new NoUniqueBeanNameFoundException(clazz));
}
}
private Optional getControllerBeanNameFromAnnotation(Class> clazz) {
ControllerBean annotation = clazz.getAnnotation(ControllerBean.class);
if (annotation != null && !annotation.value().trim().isEmpty()) {
return Optional.of(annotation.value());
} else {
Class> superClass = clazz.getSuperclass();
return superClass != null ? getControllerBeanNameFromAnnotation(superClass) : Optional.empty();
}
}
private Class> getBeanClass(BeanDefinition beanDefinition) {
AbstractBeanDefinition definition = (AbstractBeanDefinition) beanDefinition;
Class> beanClass;
if (definition.hasBeanClass()) {
beanClass = definition.getBeanClass();
} else {
try {
beanClass = definition.resolveBeanClass(applicationContext.getClassLoader());
} catch (ClassNotFoundException e) {
throw new InternalResolverException(e);
}
}
return beanClass;
}
}