Burningwave Core is an advanced, free and open source Java frameworks building library and it is useful for scanning class paths, generating classes at runtime, facilitating the use of reflection, scanning the filesystem, executing stringified source code, iterating collections or arrays in parallel, executing tasks in parallel and much more...
Burningwave Core contains AN EXTREMELY POWERFUL CLASSPATH SCANNER: it’s possible to search classes by every criteria that your imagination can make by using lambda expressions; scan engine is highly optimized using direct allocated ByteBuffers to avoid heap saturation; searches are executed in multithreading context and are not affected by “the issue of the same class loaded by different classloaders” (normally if you try to execute "isAssignableFrom" method on a same class loaded from different classloader it returns false).
By default Burningwave Core uses the dynamic driver supplied by the ToolFactory JVM Driver library but you can change it through the property jvm.driver.type in the burningwave.static.properties file. It is also possible to switch to the drivers supplied by Burningwave JVM Driver library by simply adding the following to your dependencies instead the previous one shown above:
Generating classes at runtime and invoking their methods with and without the use of reflection
For this purpose is necessary the use of ClassFactory component and of the sources generating components. Once the sources have been set in UnitSourceGenerator objects, they must be passed to loadOrBuildAndDefine method of ClassFactory with the ClassLoader where you want to define new generated classes. This method performs the following operations: tries to load all the classes present in the UnitSourceGenerator through the class loader, if at least one of these is not found it proceeds to compiling all the UnitSourceGenerators and uploading their classes on class loader: in this case, keep in mind that if a class with the same name was previously loaded by the class loader, the compiled class will not be uploaded. If you need more information you can:
Once the classes have been compiled and loaded, it is possible to invoke their methods in severals ways as shown at the end of the example below.
generatedClass = classRetriever.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() ); classRetriever.close(); } public static class ToBeExtended { public void printSomeThing() { System.out.println("Called method printSomeThing"); } } public static interface MyInterface { public Comparable
convert(LocalDateTime localDateTime); } public static void main(String[] args) throws Throwable { execute(); } }">
packageorg.burningwave.core.examples.classfactory;
import staticorg.burningwave.core.assembler.StaticComponentContainer.Constructors;
importjava.lang.reflect.Modifier;
importjava.time.LocalDateTime;
importjava.time.ZoneId;
importjava.util.Date;
importorg.burningwave.core.Virtual;
importorg.burningwave.core.assembler.ComponentContainer;
importorg.burningwave.core.assembler.ComponentSupplier;
importorg.burningwave.core.classes.AnnotationSourceGenerator;
importorg.burningwave.core.classes.ClassFactory;
importorg.burningwave.core.classes.ClassSourceGenerator;
importorg.burningwave.core.classes.FunctionSourceGenerator;
importorg.burningwave.core.classes.GenericSourceGenerator;
importorg.burningwave.core.classes.TypeDeclarationSourceGenerator;
importorg.burningwave.core.classes.UnitSourceGenerator;
importorg.burningwave.core.classes.VariableSourceGenerator;
publicclassRuntimeClassExtender {
@SuppressWarnings("resource")
publicstaticvoidexecute() throwsThrowable {
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("return Date.from(localDateTime.atZone(ZoneId.systemDefault()).toInstant());")
.useType(ZoneId.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) methodClassFactory.ClassRetriever classRetriever = classFactory.loadOrBuildAndDefine(
unitSG
);
Class generatedClass = classRetriever.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()
);
classRetriever.close();
}
publicstaticclassToBeExtended {
publicvoidprintSomeThing() {
System.out.println("Called method printSomeThing");
}
}
publicstaticinterfaceMyInterface {
publicComparable<Date>convert(LocalDateTimelocalDateTime);
}
publicstaticvoidmain(String[] args) throwsThrowable {
execute();
}
}
Retrieving classes of runtime class paths or of other paths through the ClassHunter
The components of the class paths scanning engine are: ByteCodeHunter, ClassHunter and the ClassPathHunter. Now we are going to use the ClassHunter to search for all classes that have package name that matches a regex. So in this example we're looking for all classes whose package name contains "springframework" string in the runtime class paths:
importjava.util.Collection;
importorg.burningwave.core.assembler.ComponentContainer;
importorg.burningwave.core.assembler.ComponentSupplier;
importorg.burningwave.core.classes.ClassCriteria;
importorg.burningwave.core.classes.ClassHunter;
importorg.burningwave.core.classes.JavaClass;
importorg.burningwave.core.classes.SearchConfig;
importorg.burningwave.core.io.FileSystemItem;
importorg.burningwave.core.io.PathHelper;
publicclassFinder {
publicCollection<Class>find() {
ComponentSupplier componentSupplier =ComponentContainer.getInstance();
PathHelper pathHelper = componentSupplier.getPathHelper();
ClassHunter classHunter = componentSupplier.getClassHunter();
SearchConfig searchConfig =SearchConfig.forPaths(
//Here you can add all absolute path you want://both folders, zip, jar, ear and war will be recursively scanned.//For example you can add: "C:\\Users\\user\\.m2", or a path of//an ear file that contains nested war with nested jar files//With the rows below the search will be executed on runtime class paths and//on java 9 and later also on .jmod files contained in jmods folder of the Java home//(see https://github.com/burningwave/core/wiki/In-depth-look-to-ClassHunter-and-configuration-guide)
pathHelper.getAllMainClassPaths(),
pathHelper.getPaths(PathHelper.Configuration.Key.MAIN_CLASS_REPOSITORIES)
//If you want to scan only one jar you can replace the two line of code above with://pathHelper.getPaths(path -> path.contains("spring-core-4.3.4.RELEASE.jar"))
).addFileFilter(
FileSystemItem.Criteria.forAllFileThat( fileSystemItem -> {
JavaClass javaClass = fileSystemItem.toJavaClass();
if (javaClass ==null) {
returnfalse;
}
String packageName = javaClass.getPackageName();
return packageName !=null&& packageName.contains("springframework");
})
);
try(ClassHunter.SearchResult searchResult = classHunter.findBy(searchConfig)) {
return searchResult.getClasses();
}
}
}
Finding where a class is loaded from
For this purpose we are going to use the ClassPathHunter component:
importjava.util.Collection;
importorg.burningwave.core.assembler.ComponentContainer;
importorg.burningwave.core.assembler.ComponentSupplier;
importorg.burningwave.core.classes.ClassPathHunter;
importorg.burningwave.core.classes.JavaClass;
importorg.burningwave.core.classes.SearchConfig;
importorg.burningwave.core.io.FileSystemItem;
importorg.burningwave.core.io.PathHelper;
publicclassFinder {
publicCollection<FileSystemItem>find() {
ComponentSupplier componentSupplier =ComponentContainer.getInstance();
PathHelper pathHelper = componentSupplier.getPathHelper();
ClassPathHunter classPathHunter = componentSupplier.getClassPathHunter();
SearchConfig searchConfig =SearchConfig.forPaths(
//Here you can add all absolute path you want://both folders, zip and jar will be recursively scanned.//For example you can add: "C:\\Users\\user\\.m2"//With the line below the search will be executed on runtime class paths
pathHelper.getMainClassPaths()
).addFileFilter(
FileSystemItem.Criteria.forAllFileThat(fileSystemItem -> {
JavaClass javaClass = fileSystemItem.toJavaClass();
return javaClass !=null&& javaClass.getName().equals(Finder.class.getName());
})
);
try(ClassPathHunter.SearchResult searchResult = classPathHunter.findBy(searchConfig)) {
return searchResult.getClassPaths();
}
}
}
Performing tasks in parallel with different priorities
Used by the IterableObjectHelper to iterate collections or arrays in parallel, the BackgroundExecutor component is able to run different functional interfaces in parallel by setting the priority of the thread they will be assigned to. There is also the option to wait for them start or finish.
For obtaining threads this component uses the ThreadSupplier that can be customized in the burningwave.static.properties file and provides a fixed number of reusable threads indicated by the thread-supplier.max-poolable-thread-count property and, if these threads have already been assigned, new non-reusable threads will be created whose quantity maximum is indicated by the thread-supplier.max-detached-thread-count property. Once this limit is reached if the request for a new thread exceeds the waiting time indicated by the thread-supplier.poolable-thread-request-timeout property, the ThreadSupplier will proceed to increase the limit indicated by the 'thread-supplier.max-detached-thread-count' property for the quantity indicated by the thread-supplier.max-detached-thread-count.increasing-step property. Resetting the 'thread-supplier.max-detached-thread-count' property to its initial value, will occur gradually only when there have been no more waits on thread requests for an amount of time indicated by the thread-supplier.max-detached-thread-count.elapsed-time-threshold-from-last-increase-for-gradual-decreasing-to-initial-value property.
{ logInfo("internal task started"); synchronized (this) { wait(5000); } logInfo("internal task finished"); }, Thread.MAX_PRIORITY).submit(); internalTask.waitForFinish(); logInfo("task one finished"); return startTime; }, Thread.MAX_PRIORITY); taskOne.submit(); Task taskTwo = BackgroundExecutor.createTask(task -> { logInfo("task two started and wait for task one finishing"); taskOne.waitForFinish(); logInfo("task two finished"); }, Thread.NORM_PRIORITY); taskTwo.submit(); ProducerTask
taskThree = BackgroundExecutor.createProducerTask(task -> { logInfo("task three started and wait for task two finishing"); taskTwo.waitForFinish(); logInfo("task two finished"); return System.currentTimeMillis(); }, Thread.MIN_PRIORITY); taskThree.submit(); taskThree.waitForFinish(); logInfo("Elapsed time: {}ms", taskThree.join() - taskOne.join()); } public static void main(String[] args) { new TaskLauncher().launch(); } }">
import staticorg.burningwave.core.assembler.StaticComponentContainer.BackgroundExecutor;
importorg.burningwave.core.ManagedLogger;
importorg.burningwave.core.concurrent.QueuedTasksExecutor.ProducerTask;
importorg.burningwave.core.concurrent.QueuedTasksExecutor.Task;
publicclassTaskLauncherimplementsManagedLogger {
publicvoidlaunch() {
ProducerTask<Long> taskOne =BackgroundExecutor.createProducerTask(task -> {
Long startTime =System.currentTimeMillis();
logInfo("task one started");
synchronized (this) {
wait(5000);
}
Task internalTask =BackgroundExecutor.createTask(tsk -> {
logInfo("internal task started");
synchronized (this) {
wait(5000);
}
logInfo("internal task finished");
}, Thread.MAX_PRIORITY).submit();
internalTask.waitForFinish();
logInfo("task one finished");
return startTime;
}, Thread.MAX_PRIORITY);
taskOne.submit();
Task taskTwo =BackgroundExecutor.createTask(task -> {
logInfo("task two started and wait for task one finishing");
taskOne.waitForFinish();
logInfo("task two finished");
}, Thread.NORM_PRIORITY);
taskTwo.submit();
ProducerTask<Long> taskThree =BackgroundExecutor.createProducerTask(task -> {
logInfo("task three started and wait for task two finishing");
taskTwo.waitForFinish();
logInfo("task two finished");
returnSystem.currentTimeMillis();
}, Thread.MIN_PRIORITY);
taskThree.submit();
taskThree.waitForFinish();
logInfo("Elapsed time: {}ms", taskThree.join() - taskOne.join());
}
publicstaticvoidmain(String[] args) {
newTaskLauncher().launch();
}
}
Iterating collections and arrays in parallel by setting thread priority
Through the underlying configurable BackgroundExecutor the IterableObjectHelper component is able to iterate a collection or an array in parallel and execute an action on each iterated item giving also the ability to set the threads priority:
packageorg.burningwave.core.examples.iterableobjecthelper;
import staticorg.burningwave.core.assembler.StaticComponentContainer.IterableObjectHelper;
import staticorg.burningwave.core.assembler.StaticComponentContainer.ManagedLoggerRepository;
importjava.util.ArrayList;
importjava.util.Collection;
importjava.util.List;
importjava.util.stream.Collectors;
importjava.util.stream.IntStream;
importorg.burningwave.core.iterable.IterableObjectHelper.IterationConfig;
publicclassCollectionAndArrayIterator {
publicstaticvoidexecute() {
Collection<Integer> inputCollection =IntStream.rangeClosed(1, 1000000).boxed().collect(Collectors.toList());
List<String> outputCollection =IterableObjectHelper.iterateAndGet(
IterationConfig.of(inputCollection)
//Enabling parallel iteration when the input collection size is greater than 2
.parallelIf(inputColl -> inputColl.size() >2)
//Setting threads priority
.withPriority(Thread.MAX_PRIORITY)
//Setting up the output collection
.withOutput(newArrayList<String>())
.withAction((number, outputCollectionSupplier) -> {
if (number >500000) {
//Terminating the current thread iteration early.IterableObjectHelper.terminateCurrentThreadIteration();
//If you need to terminate all threads iteration (useful for a find first iteration) use//IterableObjectHelper.terminateIteration();
}
if ((number %2) ==0) {
outputCollectionSupplier.accept(outputColl ->//Converting and adding item to output collection
outputColl.add(number.toString())
);
}
})
);
IterableObjectHelper.iterate(
IterationConfig.of(outputCollection)
//Disabling parallel iteration
.parallelIf(inputColl ->false)
.withAction((number) -> {
ManagedLoggerRepository.logInfo(CollectionAndArrayIterator.class::getName, "Iterated number: {}", number);
})
);
ManagedLoggerRepository.logInfo(
CollectionAndArrayIterator.class::getName,
"Output collection size {}", outputCollection.size()
);
}
publicstaticvoidmain(String[] args) {
execute();
}
}
Reaching a resource of the file system
Through FileSystemItem you can reach a resource of the file system even if it is contained in a nested supported (zip, jar, war, ear, jmod) compressed archive and obtain the content of it or other informations such as if it is a folder or a file or a compressed archive or if it is a compressed entry or obtain, if it is a folder or a compressed archive, the direct children or all nested children or a filtered collection of them. You can retrieve a FileSystemItem through an absolute path or through a relative path referred to your class path by using the PathHelper. FileSystemItems are cached and there will only be one instance of them for an absolute path and you can also clear the cache e reload all informations of a FileSystemItem. In the example below we show how to retrieve and use a FileSystemItem.
"txt".equals(fSIC.getExtension()) || "exe".equals(fSIC.getExtension())) ) ){ System.out.println("child name: " + child.getName() + " - child parent: " + child.getParent().getName()); //copy the file to a folder child.copyTo(System.getProperty("user.home") + "/Desktop/copy"); } //Obtaining a FileSystemItem through a relative path (in this case we are obtaining a reference to a jar //contained in an ear that is contained in a zip fSI = ComponentContainer.getInstance().getPathHelper().getResource( "/../../src/test/external-resources/libs-for-test.zip/ESC-Lib.ear/APP-INF/lib/jaxb-xjc-2.1.7.jar" ); System.out.println("is an archive:" + fSI.isArchive()); //This method return true if the file or folder is located inside a compressed archive System.out.println("is compressed:" + fSI.isCompressed()); //this clear cache fSI.refresh(true); //Obtaining direct children for (FileSystemItem child : fSI.getChildren()) { System.out.println("child name:" + child.getAbsolutePath()); } //Obtaining all nested children for (FileSystemItem child : fSI.getAllChildren()) { System.out.println("child name:" + child.getAbsolutePath()); } //Obtaining the content of the resource (once the content is loaded it will be cached) fSI.toByteBuffer(); } public static void main(String[] args) { execute(); } }">
packageorg.burningwave.core.examples.filesystemitem;
importorg.burningwave.core.assembler.ComponentContainer;
importorg.burningwave.core.io.FileSystemItem;
publicclassResourceReacher {
privatestaticvoidexecute() {
//Obtaining FileSystemItem through absolute pathFileSystemItem fSI =FileSystemItem.ofPath("C:/Program Files (x86)");
FileSystemItem firstFolderFound =null;
//Obtaining direct childrenfor (FileSystemItem child : fSI.getChildren()) {
System.out.println("child name:"+ child.getAbsolutePath());
if (firstFolderFound ==null&& child.isFolder()) {
System.out.println(child.getAbsolutePath() +" is a folder: "+ child.isFolder());
firstFolderFound = child;
}
}
//Filtering all nested children for extensionfor (FileSystemItem child : firstFolderFound.findInAllChildren(
FileSystemItem.Criteria.forAllFileThat(fSIC ->"txt".equals(fSIC.getExtension()) ||"exe".equals(fSIC.getExtension()))
)
){
System.out.println("child name: "+ child.getName() +" - child parent: "+ child.getParent().getName());
//copy the file to a folder
child.copyTo(System.getProperty("user.home") +"/Desktop/copy");
}
//Obtaining a FileSystemItem through a relative path (in this case we are obtaining a reference to a jar//contained in an ear that is contained in a zip
fSI =ComponentContainer.getInstance().getPathHelper().getResource(
"/../../src/test/external-resources/libs-for-test.zip/ESC-Lib.ear/APP-INF/lib/jaxb-xjc-2.1.7.jar"
);
System.out.println("is an archive:"+ fSI.isArchive());
//This method return true if the file or folder is located inside a compressed archiveSystem.out.println("is compressed:"+ fSI.isCompressed());
//this clear cache
fSI.refresh(true);
//Obtaining direct childrenfor (FileSystemItem child : fSI.getChildren()) {
System.out.println("child name:"+ child.getAbsolutePath());
}
//Obtaining all nested childrenfor (FileSystemItem child : fSI.getAllChildren()) {
System.out.println("child name:"+ child.getAbsolutePath());
}
//Obtaining the content of the resource (once the content is loaded it will be cached)
fSI.toByteBuffer();
}
publicstaticvoidmain(String[] args) {
execute();
}
}
Resolving, collecting or retrieving paths
Through PathHelper we can resolve or collect paths or retrieving resources even through supported archive files (zip, jar, jmod, ear and war). So we can create a path collection by adding an entry in burningwave.properties file that starts with paths. prefix (this is a fundamental requirement to allow PathHelper to load the paths), e.g.:
These paths could be retrieved through PathHelper.getPaths method and we can find a resource in all configured paths plus the runtime class paths (that is automatically loaded under the entry named paths.main-class-paths) by using PathHelper.getResource method, e.g.:
ComponentSupplier componentSupplier =ComponentContainer.getInstance();
PathHelper pathHelper = componentSupplier.getPathHelper();
Collection<String> paths = pathHelper.getPaths("paths.my-collection", "paths.my-collection-2"));
//With the code below all configured paths plus runtime class paths will be iterated to search//the resource called some.jarFileSystemItem resource = pathHelper.getResource("/../some.jar");
InputStream inputStream = resource.toInputStream();
We can also use placeholder and relative paths, e.g.:
paths.my-collection-3=C:/some folder 2/ some folder 3;paths.my-jar=${paths.my-collection-3}/../some.jar;
It is also possibile to obtain references to resources of the runtime class paths by using the pre-loaded entry 'paths.main-class-paths' (runtime class paths are automatically iterated for searching the path that match the entry), e.g.:
We can also use a FileSystemItem listing (FSIL) expression and, for example, create a path collection of all absolute path of all classes of the runtime class paths:
A FSIL expression encloses in a couple of double slash an absolute path or a placeholdered path collection that will be scanned; after the second double slash we have the listing type that could refear to direct children of scanned paths ('children') or to all nested children of scanned paths ('allChildren'); after that and colons we have the regular expression with we are going to filter the absolute paths iterated.
Retrieving placeholdered items from map and properties file
With IterableObjectHelper component it is possible to retrieve items from map by using placeholder or not. In the following example we are going to show how to retrieve strings or objects from burningwave.properties file and from maps.
Handling privates and all other members of an object
Through Fields, Constructors and Methods components it is possible to get or set fields value, invoking or finding constructors or methods of an object. Members handlers use to cache all members for faster access. For fields handling we are going to use Fields component:
> loadedClasses = Fields.getDirect(classLoader, "classes"); //Access by Reflection loadedClasses = Fields.get(classLoader, "classes"); //Get all field values of an object through memory address access Map
values = Fields.getAllDirect(classLoader); //Get all field values of an object through reflection access values = Fields.getAll(classLoader); Object obj = new Object() { volatile List
please provide an option to disable startup of any threads:
I am using:
if (StaticComponentContainer.Modules != null) {
StaticComponentContainer.Modules.exportAllToAll();
}
And afterwards there is no way to shutdown all threads. I don't see an option to prevent the threads from being started as well. Maybe start the threads lazy the first time when a BackgroundExecutor call is made? Or provide a BackgroundExecutor implementation that directly invokes runnables instead of delegating to threads. Or allow someone to provide his own implementation for the BackgroundExecutor that works without threads. Same applies to FileSystemHelper.
Thank you for creating this library. I have found it immensely helpful!
One thing I haven't wrapped my head around is how to declare a generic type in a static method. For instance, I would like to generate this code:
// Goal is to define <X> as follows
public static <X> Builder<X> builder() {
return new Builder<>();
}
I can successfully create the method, but <X> is undefined because this is a static function.
// Actual code generated where <X> is not defined
public static Builder<X> builder() {
return new Builder<>();
}
Is there a way to define <X> as is the first example? Can I pass a GenericSourceGenerator somewhere or set a flag I'm missing? The code I'm using is fairly dynamic, but a simplified version is:
FunctionSourceGenerator.create("builder")
.addModifier(Modifier.PUBLIC | Modifier.STATIC)
.setReturnType(
TypeDeclarationSourceGenerator.create("Builder").addGeneric(GenericSourceGenerator.create("X"))
)
.addBodyCodeLine("return new Builder<>();")
In https://github.com/spotify/fmt-maven-plugin/pull/139, I try using burningwave core to export packages from jdk.compiler.
When I then run this plugin, I get the following error in maven shutdown (it seems like)
Exception in thread "Burningwave - Resource releaser" java.lang.NoClassDefFoundError: org/burningwave/core/function/ThrowingRunnable
at org.burningwave.core.assembler.StaticComponentContainer.lambda$static$21(StaticComponentContainer.java:371)
at java.base/java.lang.Thread.run(Thread.java:833)
Caused by: java.lang.ClassNotFoundException: org.burningwave.core.function.ThrowingRunnable
at org.codehaus.plexus.classworlds.strategy.SelfFirstStrategy.loadClass(SelfFirstStrategy.java:50)
at org.codehaus.plexus.classworlds.realm.ClassRealm.unsynchronizedLoadClass(ClassRealm.java:271)
at org.codehaus.plexus.classworlds.realm.ClassRealm.loadClass(ClassRealm.java:247)
at org.codehaus.plexus.classworlds.realm.ClassRealm.loadClass(ClassRealm.java:239)
... 2 more
????Here we are making almost all types of Spring Boot Applications that you suggested via linkedin/Gmail, If you are looking something that is out of the box then just ping me in linkedin or mail with your required project i will make the template/full project for you for free
In this course, we will learn how to build a complete full-stack web application using Spring boot as backend and React (React Hooks) as frontend. We will use MySQL database to store and retrieve the data.