com.foreach.across.config.AcrossDynamicModulesConfigurer Maven / Gradle / Ivy
/*
* Copyright 2014 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
*
* 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 com.foreach.across.config;
import com.foreach.across.core.AcrossContext;
import com.foreach.across.core.AcrossException;
import com.foreach.across.core.AcrossModule;
import com.foreach.across.core.DynamicAcrossModuleFactory;
import com.foreach.across.core.context.AcrossModuleRole;
import com.foreach.across.core.context.ClassPathScanningChildPackageProvider;
import com.foreach.across.core.context.ModuleDependencyResolver;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Configuration;
import java.util.Optional;
/**
* {@link AcrossContextConfigurer} bean that will add dynamic modules to an {@link AcrossContext}.
* It will do so by scanning a base package ({@link #setBasePackage(String)} for child packages
* application, infrastructure and postprocessor. For each of these found, a module with the respective role
* will be added. The name of the module will be determined by the base module name ({@link #setBaseModuleName(String)}).
*
* If there is a {@link ModuleDependencyResolver} found in the bean factory, it will be used to resolve the corresponding modules.
* This way an implemented {@link AcrossModule} descriptor will automatically be picked up.
*
* Can also be configured from a single class instance that will be used for determining the base package and base
* module name. See also {@link AcrossDynamicModulesConfiguration} for a {@link Configuration} that will use the
* importing class as base.
*
* @author Arne Vandamme
* @see AcrossDynamicModulesConfiguration
* @since 1.1.2
*/
public class AcrossDynamicModulesConfigurer implements AcrossContextConfigurer
{
private static final Logger LOG = LoggerFactory.getLogger( AcrossDynamicModulesConfiguration.class );
private String basePackage, baseModuleName;
public AcrossDynamicModulesConfigurer() {
}
public AcrossDynamicModulesConfigurer( Class> applicationClass ) {
setApplicationClass( applicationClass );
}
public AcrossDynamicModulesConfigurer( String basePackage, String baseModuleName ) {
setBasePackage( basePackage );
setBaseModuleName( baseModuleName );
}
/**
* The base package that should be scanned for modules.
* Can also be set through a single {@link #setApplicationClass(Class)}.
*
* @param basePackage package name
*/
public void setBasePackage( String basePackage ) {
this.basePackage = basePackage;
}
/**
* The base module name that should be used. If dynamic modules are added, their names will be
* prefixed with the base module name. Resulting in either BASEApplicationModule, BASEInfrastructureModule or
* BASEPostProcessorModule.
*
* @param baseModuleName module name prefix
*/
public void setBaseModuleName( String baseModuleName ) {
this.baseModuleName = baseModuleName;
}
/**
* Set a single class from that is in the base package, and determine a base module name from the class name.
* If the simple class name ends with Application (eg. MyCurrentApplication), the part before Application
* will be used as base module name (eg. MyCurrent); else the simple class name will be used.
*
* @param clazz instance
*/
public void setApplicationClass( Class> clazz ) {
basePackage = packageName( clazz );
baseModuleName = baseModuleName( clazz );
}
@Override
public void configure( AcrossContext context ) {
if ( basePackage == null || baseModuleName == null ) {
throw new AcrossException(
"Unable to add dynamic modules as no basePackage and no baseModuleName have been configured" );
}
ClassPathScanningChildPackageProvider packageProvider = new ClassPathScanningChildPackageProvider();
String[] children = packageProvider.findChildren( basePackage );
if ( hasPackage( children, "application" ) ) {
configureApplicationModule( context, basePackage + ".application", baseModuleName );
}
if ( hasPackage( children, "infrastructure" ) ) {
configureInfrastructureModule( context, basePackage + ".infrastructure", baseModuleName );
}
if ( hasPackage( children, "postprocessor" ) ) {
configurePostProcessorModule( context, basePackage + ".postprocessor", baseModuleName );
}
}
private String packageName( Class> clazz ) {
return clazz.getPackage() != null ? clazz.getPackage().getName() : "";
}
private boolean hasPackage( String[] packages, String name ) {
String suffix = "." + name;
for ( String pkg : packages ) {
if ( StringUtils.endsWith( pkg, suffix ) ) {
return true;
}
}
return false;
}
private void configureApplicationModule( AcrossContext context, String moduleBasePackage, String baseModuleName ) {
String applicationModuleName = baseModuleName + "ApplicationModule";
String resourcesKey = StringUtils.uncapitalize( baseModuleName );
if ( context.getModule( applicationModuleName ) == null ) {
Optional module = resolveModule( context, applicationModuleName );
context.addModule( module.orElseGet( () -> dynamicModule(
AcrossModuleRole.APPLICATION,
applicationModuleName,
resourcesKey,
moduleBasePackage
) ) );
}
}
private void configureInfrastructureModule( AcrossContext context,
String moduleBasePackage,
String baseModuleName ) {
String infrastructureModule = baseModuleName + "InfrastructureModule";
String resourcesKey = StringUtils.uncapitalize( baseModuleName + "Infrastructure" );
if ( context.getModule( infrastructureModule ) == null ) {
Optional module = resolveModule( context, infrastructureModule );
context.addModule( module.orElseGet( () -> dynamicModule(
AcrossModuleRole.INFRASTRUCTURE,
infrastructureModule,
resourcesKey,
moduleBasePackage
) ) );
}
}
private void configurePostProcessorModule( AcrossContext context,
String moduleBasePackage,
String baseModuleName ) {
String postprocessorModule = baseModuleName + "PostProcessorModule";
String resourcesKey = StringUtils.uncapitalize( baseModuleName + "PostProcessor" );
if ( context.getModule( postprocessorModule ) == null ) {
Optional module = resolveModule( context, postprocessorModule );
context.addModule( module.orElseGet( () -> dynamicModule(
AcrossModuleRole.POSTPROCESSOR,
postprocessorModule,
resourcesKey,
moduleBasePackage
) ) );
}
}
private AcrossModule dynamicModule( AcrossModuleRole moduleRole,
String moduleName,
String resourcesKey,
String moduleBasePackage ) {
LOG.info( "Adding package based {} module {}, resources: {}, base package: {}",
moduleRole.name(), moduleName, resourcesKey, moduleBasePackage );
DynamicAcrossModuleFactory factory = new DynamicAcrossModuleFactory()
.setModuleRole( moduleRole )
.setModuleName( moduleName )
.setResourcesKey( resourcesKey )
.setBasePackage( moduleBasePackage );
try {
return factory.getObject();
}
catch ( Exception e ) {
throw new AcrossException( "Unable to create package based module", e );
}
}
private Optional resolveModule( AcrossContext context, String moduleName ) {
ModuleDependencyResolver moduleDependencyResolver = context.getModuleDependencyResolver();
if ( moduleDependencyResolver != null ) {
return moduleDependencyResolver.resolveModule( moduleName, true );
}
return Optional.empty();
}
private String baseModuleName( Class> importingClass ) {
String moduleName = importingClass.getSimpleName();
if ( StringUtils.endsWith( moduleName, "Application" ) ) {
return StringUtils.substringBeforeLast( moduleName, "Application" );
}
return moduleName;
}
}