-
-
Notifications
You must be signed in to change notification settings - Fork 22
FAQ
- How to enable logging of Burningwave Core classes?
- How to hide banner during startup?
- How to configure the StaticComponentContainer?
- How to configure the ComponentContainer?
- Which is the value of 'main-class-paths' variable used in burningwave.properties file?
- What is the 'paths.class-factory.java-memory-compiler.class-repositories' property for?
- What is the 'paths.class-factory.default-class-loader.class-repositories' property for?
- How can I integrate the configuration properties into Spring?
- How can I use classes located outside the runtime class path in my sources to be generated?
- How to add a method to an interface?
- Is it possible to generate a lambda?
- How to perform a scan based on files signature check instead a scan based on files extension check?
- Is there a CPU optimized way to exclude jar files from class search?
You can solve adding to base path of your project classpath a file named burningwave.static.properties and setting the property managed-logger.repository.enabled to true if you want to enable logging otherwise false. E.g.:
managed-logger.repository=autodetect
managed-logger.repository.enabled=true
You can solve adding to base path of your project classpath a file named burningwave.static.properties and setting the property banner.hide to true. E.g.:
managed-logger.repository=autodetect
managed-logger.repository.enabled=false
buffer-handler.default-buffer-size=1024
buffer-handler.default-allocation-mode=ByteBuffer::allocateDirect
banner.hide=true
The configuration of this type of container is done via burningwave.static.properties file or via burningwave.static.default.properties file: the library searches for the first file and if it does not find it, then it searches for the second file and if neither this one is found then the library sets the default configuration programmatically. The default configuration loaded programmatically if no configuration file is found is available at this link and here an example of a custom burningwave.static.properties.
The configuration of this type of container can be done via Properties file or programmatically via a Properties object. If you use the singleton instance obtained via ComponentContainer.getInstance()
method, you must create a burningwave.properties file and put it on base path of your classpath project. The default configuration automatically loaded if no configuration file is found is available at this link.
If in your custom burningwave.properties file one of this four default properties is not found, the relative default value is assumed.
If you create a component container instance through method ComponentContainer.create(String relativeConfigFileName)
, you can specify the file name of your properties file and you can locate it everywhere in your classpath project but remember to use a relative path in this case, i.e.: if you name your file custom-config-file.properties
and put it in package org.burningwave
you must create the component container as follow: ComponentContainer.create("org/burningwave/custom-config-file.properties") and here an example of a custom burningwave.properties.
The value of this property is a list of the runtime classpaths.
This property is used by the JavaMemoryCompiler
and indirectly by the ClassFactory
to indicate, in case the sources to be compiled refer to classes that are not present in the runtime class paths, in which paths the JavaMemoryCompiler must searches them. Here, for example, we are adding the path 'C:/Users/user_name/.m2':
path-scanner-class-loader.parent=Thread.currentThread().getContextClassLoader()
paths.main-class-paths.extension=//${system.properties:java.home}/lib//children:.*\.jar|.*\.jmod;//${system.properties:java.home}/lib/ext//children:.*\.jar|.*\.jmod;//${system.properties:java.home}/jmods//children:.*\.jar|.*\.jmod;
class-factory.default-class-loader=Thread.currentThread().getContextClassLoader()
paths.java-memory-compiler.additional-class-repositories=C:/Users/user_name/.m2;
paths.class-factory.default-class-loader.additional-class-repositories=${paths.class-factory.java-memory-compiler.class-repositories};
This property is used by the ClassFactory
to indicate, in case the sources to be compiled refer to classes that are not present in the runtime class paths, in which paths the classloader must searches them. Here, for example, we are telling this property will assume the same values of 'paths.java-memory-compiler.class-repositories':
path-scanner-class-loader.parent=Thread.currentThread().getContextClassLoader()
paths.main-class-paths.extension=//${system.properties:java.home}/lib//children:.*\.jar|.*\.jmod;//${system.properties:java.home}/lib/ext//children:.*\.jar|.*\.jmod;//${system.properties:java.home}/jmods//children:.*\.jar|.*\.jmod;
class-factory.default-class-loader=Thread.currentThread().getContextClassLoader()
paths.java-memory-compiler.class-repositories=${main-class-paths};C:/Users/user_name/.m2;
paths.class-factory.default-class-loader.additional-class-repositories=${paths.class-factory.java-memory-compiler.class-repositories};
To use a single configuration file when using Burningwave Core with Spring, you can proceed by entering the configuration properties of Burningwave Core in the main configuration file of Spring and adding a prefix differentiated by container type to the aforementioned properties:
- application.yml:
burningwave.core.component-container:
class-hunter.default-path-scanner-class-loader: '(Supplier<PathScannerClassLoader>)() -> ((ComponentSupplier)parameter[0]).getPathScannerClassLoader()'
burningwave.core.static-component-container:
managed-logger.repository: 'org.burningwave.core.SLF4JManagedLoggerRepository'
modules.export-all-to-all: false
- application.properties:
burningwave.core.component-container.class-hunter.default-path-scanner-class-loader=(Supplier<PathScannerClassLoader>)() -> ((ComponentSupplier)parameter[0]).getPathScannerClassLoader()
burningwave.core.static-component-container.managed-logger.repository=org.burningwave.core.SLF4JManagedLoggerRepository
burningwave.core.static-component-container.modules.export-all-to-all=false
... And then adding the following beans to the Spring setup class:
@Bean("burningwave.core.staticComponentContainer.config")
@ConfigurationProperties("burningwave.core.static-component-container")
public Map<String, String> staticComponentContainerConfig(){
return new LinkedHashMap<>();
}
@Bean
public Class<org.burningwave.core.assembler.StaticComponentContainer> staticComponentContainer(
@Qualifier("burningwave.core.staticComponentContainer.config") Map<String, String> configMap
) {
org.burningwave.core.assembler.StaticComponentContainer.Configuration.Default.add(configMap);
return org.burningwave.core.assembler.StaticComponentContainer.class;
}
@Bean("burningwave.core.componentContainer.config")
@ConfigurationProperties("burningwave.core.component-container")
public Map<String, String> componentContainerConfig(){
return new LinkedHashMap<>();
}
@Bean(name="burningwave.core.componentContainer", destroyMethod="")
public org.burningwave.core.assembler.ComponentContainer componentContainer(
@Qualifier("burningwave.core.componentContainer.config") Map<String, String> configMap
) {
org.burningwave.core.assembler.ComponentContainer.Configuration.Default.add(configMap);
return org.burningwave.core.assembler.ComponentContainer.getInstance();
}
You can do it in two different ways:
- statically by adding the comma separated paths to the property
paths.java-memory-compiler.additional-class-repositories
or the propertypaths.class-factory.default-class-loader.additional-class-repositories
of your burningwave.properties file - dinamically by calling methods of adding/setting class paths/repositories on your
LoadOrBuildAndDefineConfig
If you decide to do it statically you can add the external class paths to the paths.java-memory-compiler.additional-class-repositories
or paths.class-factory.default-class-loader.additional-class-repositories
properties of burningwave.properties file. In the default configuration these two properties are included in class-factory.default-class-loader.class-repositories
property that is automatically loaded by the ClassFactory when executing the method loadOrBuildAndDefine
: all paths included in all these properties will be scanned for searching classes useful for compilation and class loading. Here you can find the explanation of the role and examples of how to configure the:
-
paths.java-memory-compiler.additional-class-repositories
property -
paths.class-factory.default-class-loader.additional-class-repositories
property
If you decide to do it dinamically it is suggested to call on your LoadOrBuildAndDefineConfig
the method addClassRepository
/addClassRepositories
or the method addClassRepositoriesWhereToSearchNotFoundClasses
/addClassRepositoryWhereToSearchNotFoundClasses
: these two methods will add the paths received in input to the paths referenced by the paths.java-memory-compiler.class-repositories
and paths.class-factory.default-class-loader.additional-class-repositories
properties of burningwave.properties file. The difference between the couple addClassRepository
/addClassRepositories
and addClassRepositoriesWhereToSearchNotFoundClasses
/addClassRepositoryWhereToSearchNotFoundClasses
is that the first couple of method executes a scan for searching for all class paths of all imported classes by the sources before compiling and then, if compilation has done with errors, will be executed a new scan; instead, the second couple of methods, will executes a scan for searching for all class paths of all imported classes by the sources only if compilation has done with errors. After the compilation has done without errors, the same paths are scanned by the class loader during loading in case of not found classes; Here you can find an example about the generation of classes by using libraries located outside the runtime class paths.
If you need to set class paths from scratch, instead of using the previous methods, you must use the method setClassRepository
/setClassRepositories
or the method setClassRepositoriesWhereToSearchNotFoundClasses
/setClassRepositoryWhereToSearchNotFoundClasses
.
You need to add the abstract modifier to the method:
ClassSourceGenerator.createInterface(
TypeDeclarationSourceGenerator.create("MyInterface")
).addMethod(
FunctionSourceGenerator.create("doSomething").setReturnType(void.class)
).addModifier(Modifier.PUBLIC | Modifier.ABSTRACT)
Yes, you can do it in several ways:
- you can use a lambda in your source class:
package org.burningwave.core.examples.classfactory;
import static org.burningwave.core.assembler.StaticComponentContainer.Constructors;
import java.lang.reflect.Modifier;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.Date;
import java.util.function.Function;
import org.burningwave.core.Virtual;
import org.burningwave.core.assembler.ComponentContainer;
import org.burningwave.core.assembler.ComponentSupplier;
import org.burningwave.core.classes.AnnotationSourceGenerator;
import org.burningwave.core.classes.ClassFactory;
import org.burningwave.core.classes.ClassSourceGenerator;
import org.burningwave.core.classes.FunctionSourceGenerator;
import org.burningwave.core.classes.GenericSourceGenerator;
import org.burningwave.core.classes.TypeDeclarationSourceGenerator;
import org.burningwave.core.classes.UnitSourceGenerator;
import org.burningwave.core.classes.VariableSourceGenerator;
public class UseFunctionalInterface {
@SuppressWarnings("resource")
public static void execute() throws Throwable {
UnitSourceGenerator unitSG = UnitSourceGenerator.create("packagename").addClass(
ClassSourceGenerator.create(
TypeDeclarationSourceGenerator.create("MyExtendedClass")
).addModifier(
Modifier.PUBLIC
//generating new method that override MyInterface.convert(LocalDateTime)
).addMethod(
FunctionSourceGenerator.create("convert")
.setReturnType(TypeDeclarationSourceGenerator.create(Comparable.class).addGeneric(GenericSourceGenerator.create(Date.class)))
.addParameter(VariableSourceGenerator.create(LocalDateTime.class, "localDateTime"))
.addModifier(Modifier.PUBLIC)
.addAnnotation(AnnotationSourceGenerator.create(Override.class))
.addBodyCodeLine("Function<LocalDateTime, Comparable<Date>> fI = (lDT) -> Date.from(lDT.atZone(ZoneId.systemDefault()).toInstant());")
.addBodyCodeLine("return fI.apply(localDateTime);")
.useType(ZoneId.class)
.useType(Function.class)
).addConcretizedType(
MyInterface.class
).expands(ToBeExtended.class)
);
System.out.println("\nGenerated code:\n" + unitSG.make());
//With this we store the generated source to a path
unitSG.storeToClassPath(System.getProperty("user.home") + "/Desktop/bw-tests");
ComponentSupplier componentSupplier = ComponentContainer.getInstance();
ClassFactory classFactory = componentSupplier.getClassFactory();
//this method compile all compilation units and upload the generated classes to default
//class loader declared with property "class-factory.default-class-loader" in
//burningwave.properties file (see "Overview and configuration").
//If you need to upload the class to another class loader use
//loadOrBuildAndDefine(LoadOrBuildAndDefineConfig) method
Class<?> generatedClass = classFactory.loadOrBuildAndDefine(
unitSG
).get(
"packagename.MyExtendedClass"
);
ToBeExtended generatedClassObject =
Constructors.newInstanceOf(generatedClass);
generatedClassObject.printSomeThing();
System.out.println(
((MyInterface)generatedClassObject).convert(LocalDateTime.now()).toString()
);
//You can also invoke methods by casting to Virtual (an interface offered by the
//library for faciliate use of runtime generated classes)
Virtual virtualObject = (Virtual)generatedClassObject;
//Invoke by using reflection
virtualObject.invoke("printSomeThing");
//Invoke by using MethodHandle
virtualObject.invokeDirect("printSomeThing");
System.out.println(
((Date)virtualObject.invokeDirect("convert", LocalDateTime.now())).toString()
);
}
public static class ToBeExtended {
public void printSomeThing() {
System.out.println("Called method printSomeThing");
}
}
public static interface MyInterface {
public Comparable<Date> convert(LocalDateTime localDateTime);
}
public static void main(String[] args) throws Throwable {
execute();
}
}
- if you need to create a functional interface and bind it to an object you can use the FunctionalInterfaceFactory as shown in this JUnit test::
@Test
public void getOrBuildFunctionClassTestOne() throws Throwable {
ComponentSupplier componentSupplier = getComponentSupplier();
testDoesNotThrow(() -> {
Method mth = Members.findOne(
MethodCriteria.forEntireClassHierarchy()
.name((name) -> name.matches("apply"))
.and().parameterType((params, idx) -> idx == 0 && params[idx].equals(Object.class))
.and().parameterType((params, idx) -> idx == 1 && params[idx].equals(String.class))
.and().parameterType((params, idx) -> idx == 2 && params[idx].equals(String.class)),
Service.class
);
MultiParamsFunction<String> virtualObj = componentSupplier.getFunctionalInterfaceFactory().create(mth);
virtualObj.apply(new Service(), "Hello", "World!", "How are you?");
});
}
- you can create a MultiParameterFunction with the ClassFactory:
ComponentSupplier componentSupplier = ComponentContainer.getInstance();
ClassFactory classFactory = componentSupplier.getClassFactory();
//In this case we are creating a functional interface with 5 input parameters
Class<MultiParamsFunction<?>> cls = classFactory.loadOrBuildAndDefineFunctionSubType(5);
- you can generate a functional interface class:
package org.burningwave.core.examples.classfactory;
import java.lang.reflect.Modifier;
import java.util.function.Function;
import org.burningwave.core.assembler.ComponentContainer;
import org.burningwave.core.assembler.ComponentSupplier;
import org.burningwave.core.classes.AnnotationSourceGenerator;
import org.burningwave.core.classes.ClassFactory;
import org.burningwave.core.classes.ClassSourceGenerator;
import org.burningwave.core.classes.FunctionSourceGenerator;
import org.burningwave.core.classes.TypeDeclarationSourceGenerator;
import org.burningwave.core.classes.UnitSourceGenerator;
import org.burningwave.core.classes.VariableSourceGenerator;
public class FunctionalInterfaceBuilder {
@SuppressWarnings("resource")
public static void execute() throws Throwable {
UnitSourceGenerator unitSG = UnitSourceGenerator.create("packagename").addClass(
ClassSourceGenerator.createInterface(
TypeDeclarationSourceGenerator.create("MyFunctionalInterface")
).addModifier(
Modifier.PUBLIC
//generating new method that override MyInterface.convert(LocalDateTime)
).addMethod(
FunctionSourceGenerator.create("convert")
.setReturnType(TypeDeclarationSourceGenerator.create(Integer.class))
.addParameter(VariableSourceGenerator.create(String.class, "number"))
.addModifier(Modifier.ABSTRACT)
).addAnnotation(AnnotationSourceGenerator.create(FunctionalInterface.class))
);
System.out.println("\nGenerated code:\n" + unitSG.make());
//With this we store the generated source to a path
unitSG.storeToClassPath(System.getProperty("user.home") + "/Desktop/bw-tests");
ComponentSupplier componentSupplier = ComponentContainer.getInstance();
ClassFactory classFactory = componentSupplier.getClassFactory();
@SuppressWarnings("unused")
Class<?> generatedClass = classFactory.loadOrBuildAndDefine(
unitSG
).get(
"packagename.MyFunctionalInterface"
);
}
public static void main(String[] args) throws Throwable {
execute();
}
}
Simply call the method SearchConfigAbst.withScanFileCriteria(FileSystemItem.Criteria)
as follow:
import java.util.Collection;
import org.burningwave.core.assembler.ComponentContainer;
import org.burningwave.core.assembler.ComponentSupplier;
import org.burningwave.core.classes.ClassHunter;
import org.burningwave.core.classes.ClassHunter.SearchResult;
import org.burningwave.core.classes.SearchConfig;
import org.burningwave.core.io.FileSystemItem;
import org.burningwave.core.io.PathHelper;
public class Finder {
public Collection<Class<?>> find() {
ComponentSupplier componentSupplier = ComponentContainer.getInstance();
PathHelper pathHelper = componentSupplier.getPathHelper();
ClassHunter classHunter = componentSupplier.getClassHunter();
SearchConfig searchConfig = SearchConfig.forPaths(
pathHelper.getMainClassPaths()
).setFileFilter(
FileSystemItem.Criteria.forClassTypeFiles(
FileSystemItem.CheckingOption.FOR_SIGNATURE
)
);
try (ClassHunter.SearchResult searchResult = classHunter.findBy(searchConfig)) {
return searchResult.getClasses();
}
}
}
If you need performance and the search criterion is limited to the name of the class or of the package, you should consider the possibility of using the FileSystemItem
combined with JavaClass
and ClassLoaders
. For not compressed folders is better to use the FileSystemItem.findRecursiveInChildren
instead for archives (zip, jar, ear, war and jmod) due to their flat nature and for folders within them it's better to use FileSystemItem.findInAllChildren
. If you use the ClassHunter
with default configuration it loads the classes on the shared PathScannerClassLoader
of the ComponentContainer
so if you do not call the method useNewIsolatedClassLoader()
on the SearchConfig
object the classes you have searched are loaded in the parents of PathScannerClassLoader
or within itself. But if you want to proceed with manual upload you can do as following:
PathScannerClassLoader pathScannerClassLoader = componentSupplier.getPathScannerClassLoader();
pathScannerClassLoader.scanPathsAndAddAllByteCodesFound(Arrays.asList("absolutePath.of.your.jar"));
pathScannerClassLoader.loadClass("className");
Take also a look here if you want have more informations about the ClassHunter
.
You can also set the parent of the shared PathScannerClassLoader
through the Javacoded property path-scanner-class-loader.parent in the burningwave.properties file.
Here an example of an optimized search:
SearchConfig.forPaths(
pathHelper.getAbsolutePathOfResource("../../src/test/external-resources")
).addPaths(
pathHelper.getPaths(path -> path.endsWith("jar"))
).addFileSystemItems(
//folder inside a compressed archive
pathHelper.getResource("../guava-30.1.1-jre.jar/com/google")
).setFindFunction(
currentScannedPath -> {
// Check if the path is a folder outside of a zip archive
return currentScannedPath.isFolder() && !currentScannedPath.isCompressed() ?
FileSystemItem.Find.RECURSIVE_IN_CHILDREN:
FileSystemItem.Find.IN_ALL_CHILDREN;
}
).setFileFilter(
currentScannedPath -> {
if (currentScannedPath.isFolder() && !currentScannedPath.isCompressed()) {
return FileSystemItem.Criteria.forAllFileThat(
fileSystemItem ->
fileSystemItem.isFolder() ||
(fileSystemItem.getExtension() != null &&
!fileSystemItem.getExtension().equals("zip") &&
!fileSystemItem.getExtension().equals("jar") &&
fileSystemItem.toJavaClass() != null)
);
}
return FileSystemItem.Criteria.forClassTypeFiles(FileSystemItem.CheckingOption.FOR_NAME);
}
)
In first way the CodeExecutor uses the org.burningwave.core.classes.SourceCodeHandler that, by using the source generators components of the same package (AnnotationSourceGenerator
, BodySourceGenerator
, ClassSourceGenerator
, FunctionSourceGenerator
, GenericSourceGenerator
, TypeDeclarationSourceGenerator
, UnitSourceGenerator
, VariableSourceGenerator
) it generates the source from the code supplied.
Then the generated sources are passed to org.burningwave.core.classes.ClassFactory
that uses the org.burningwave.core.classes.JavaMemoryCompiler
to compile them: the compiled sources are finally passed to the ClassLoader
setted through useClassLoader method of org.burningwave.core.classes.ExecuteConfig
received by the CodeExecutor
or, if it has not be supplied, by a one shot org.burningwave.core.classes.MemoryClassLoader
with the parent class loader set by the property class-factory.default-class-loader
of burningwave configuration file (see Architectural overview and configuration). In the default configuration the parent class loader is the class loader obtained through Thread.currentThread().getContextClassLoader()
method call.Once the class is loaded the CodeExecutor
create an instance of it and calls the method execute inherited from org.burningwave.core.Executor
interface.
Burningwave core is a fully indipendent, advanced, free and open source Java frameworks building library that contains AN EXTREMELY POWERFUL CLASSPATH SCANNER.
To include Burningwave Core library in your projects simply use with Apache Maven:
<dependency>
<groupId>org.burningwave</groupId>
<artifactId>core</artifactId>
<version>12.65.2</version>
</dependency>
To use Burningwave Core as a Java module add the following to your module-info.java
:
requires org.burningwave.core;
ClassFactory
ClassHunter
- In depth look to and configuration guide
- USE CASE: retrieving all classes of the classpath
- USE CASE: retrieving all classes that implement one or more interfaces
- USE CASE: finding all classes that extend a base class
- USE CASE: searching for all classes that have package name that matches a regex
- USE CASE: finding all classes for module name (Java 9 and later)
- USE CASE: finding all annotated classes
- USE CASE: how to scan classes for specific annotations and collect its values
- USE CASE: searching for all classes with a constructor that takes a specific type as first parameter and with at least 2 methods that begin for a given string
- USE CASE: searching for all classes with methods whose name begins for a given string and that takes a specific type as its first parameter
- USE CASE: finding all classes that have at least 2 protected fields