diff --git a/CNAME b/CNAME
new file mode 100644
index 0000000..1fd5828
--- /dev/null
+++ b/CNAME
@@ -0,0 +1 @@
+voidframework.dev
\ No newline at end of file
diff --git a/categories/index.xml b/categories/index.xml
new file mode 100644
index 0000000..84a1c06
--- /dev/null
+++ b/categories/index.xml
@@ -0,0 +1 @@
+
Getting Started
Core
Relational Databases
NoSQL Databases
Cache
Internationalization
REST Client
Scheduler
Sendmail
Template
Virtual File Storage
Web
Testing
Advanced
The reference.conf files define the various configuration keys and default values. These files are the first to be loaded when the application starts. On this page you will find links to the complete list of reference.conf files used by Void Framework.
voidframework-bucket4j | reference.conf |
voidframework-cache | reference.conf |
voidframework-core | reference.conf |
voidframework-datasource-c3p0 | reference.conf |
voidframework-datasource-hikaricp | reference.conf |
voidframework-healthcheck | reference.conf |
voidframework-i18n | reference.conf |
voidframework-migration-flyway | reference.conf |
voidframework-persistence-hibernate | reference.conf |
voidframework-redis | reference.conf |
voidframework-remoteconf-etcd | reference.conf |
voidframework-remoteconf-http | reference.conf |
voidframework-restclient | reference.conf |
voidframework-scheduler | reference.conf |
voidframework-sendmail | reference.conf |
voidframework-sendmail-commonsemail | reference.conf |
voidframework-template-freemarker | reference.conf |
voidframework-validation | reference.conf |
voidframework-vfs | reference.conf |
voidframework-web-healthcheck | reference.conf |
voidframework-web | reference.conf |
Getting Started
Core
Relational Databases
NoSQL Databases
Cache
Internationalization
REST Client
Scheduler
Sendmail
Template
Virtual File Storage
Web
Testing
Advanced
Aspect Oriented Programming (AOP) includes programming methods and tools that support the modularization of concerns at the level of the source code. Void Framework manages the AOP via the AspectJ and Guice libraries. The first library is more powerful but also more complicated to use, while the latter has fewer capabilities but is simple to use.
AspectJ runtime (aspectjrt) is already provided by the module voidframework-core, but you have to add an extra plugin to compile aspects during the compilation of your application.
<plugin>
+ <!-- https://mvnrepository.com/artifact/org.codehaus.mojo/aspectj-maven-plugin -->
+ <groupId>org.codehaus.mojo</groupId>
+ <artifactId>aspectj-maven-plugin</artifactId>
+ <version>1.16.0</version>
+ <inherited>true</inherited>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.aspectj</groupId>
+ <artifactId>aspectjtools</artifactId>
+ <version>1.9.22</version> <!-- Match version provided by Void Framework -->
+ </dependency>
+ </dependencies>
+
+ <configuration>
+ <complianceLevel>${java.version}</complianceLevel>
+ <source>${maven.compiler.source}</source>
+ <target>${maven.compiler.target}</target>
+ <showWeaveInfo>true</showWeaveInfo>
+ <verbose>true</verbose>
+ <Xlint>ignore</Xlint>
+ <encoding>UTF-8</encoding>
+ </configuration>
+
+ <executions>
+ <execution>
+ <goals>
+ <goal>compile</goal>
+ <goal>test-compile</goal>
+ </goals>
+ </execution>
+ </executions>
+</plugin>
+aspectj:compile before running your application. In IntelliJ, go to Maven tab, plugin, aspectj, left-click on aspectj:compile, choose "Execute before Run/Debug".@Aspect.Read more about AspectJ at:
Read more about Guice AOP at:
Getting Started
Core
Relational Databases
NoSQL Databases
Cache
Internationalization
REST Client
Scheduler
Sendmail
Template
Virtual File Storage
Web
Testing
Advanced
Do you want to create a Guice module that will be loaded when your application starts? Nothing could be simpler than creating an implementation of the abstract class AbstractModule or the interface Module.
To meet all needs, the Void Framework provides the ability to obtain additional information via the module’s constructor. As a constructor’s parameter, you could optionally use the following elements:
Config the current configuration of the applicationScannedClassesToLoad the classes that were scanned when the application startedif a module needs to be loaded before another, you can use the OrderedModule interface.
public final class MyCustomModule extends AbstractModule implements OrderedModule {
+
+ private final Config configuration;
+ private final ScannedClassesToLoad scannedClassesToLoad;
+
+ public MyCustomModule(final Config configuration,
+ final ScannedClassesToLoad scannedClassesToLoad) {
+
+ this.configuration = configuration;
+ this.scannedClassesToLoad = scannedClassesToLoad;
+ }
+
+ @Override
+ protected void configure() {
+
+ // Place your module code here
+ }
+
+ @Override
+ public int priority() {
+
+ return 50;
+ }
+}
+voidframework.core.acceptedScanPaths.Getting Started
Core
Relational Databases
NoSQL Databases
Cache
Internationalization
REST Client
Scheduler
Sendmail
Template
Virtual File Storage
Web
Testing
Advanced
Getting Started
Core
Relational Databases
NoSQL Databases
Cache
Internationalization
REST Client
Scheduler
Sendmail
Template
Virtual File Storage
Web
Testing
Advanced
This implementation of the cache engine is convenient during the development phase because it avoids the installation of a distributed cache server. However, this implementation is absolutely not recommended for use in a production environment.
<dependency>
+ <groupId>dev.voidframework</groupId>
+ <artifactId>voidframework-cache</artifactId>
+ <version>1.16.0</version>
+</dependency>
+To enable In-memory cache engine, you have to set voidframework.cache.engine to dev.voidframework.cache.engine.MemoryCacheEngine in your application configuration file.
The following configuration key can be used in the configuration file of your application.
voidframework.cache.inMemory.flushWhenFullMaxItem the maximum number of items to be kept in the cache before it gets flushed.Getting Started
Core
Relational Databases
NoSQL Databases
Cache
Internationalization
REST Client
Scheduler
Sendmail
Template
Virtual File Storage
Web
Testing
Advanced
Getting Started
Core
Relational Databases
NoSQL Databases
Cache
Internationalization
REST Client
Scheduler
Sendmail
Template
Virtual File Storage
Web
Testing
Advanced
This implementation requires the voidframework-redis module to be properly configured.
<dependency>
+ <groupId>dev.voidframework</groupId>
+ <artifactId>voidframework-cache-redis</artifactId>
+ <version>1.16.0</version>
+</dependency>
+To enable In-memory cache engine, you have to set voidframework.cache.engine to dev.voidframework.cache.engine.RedisCacheEngine in your application configuration file.
Redis module must be properly configured, read more about Redis configuration.
Getting Started
Core
Relational Databases
NoSQL Databases
Cache
Internationalization
REST Client
Scheduler
Sendmail
Template
Virtual File Storage
Web
Testing
Advanced
Void Framework provides the tools to use a cache system. Caching can be done in two different ways, via the use of annotations or programmatically via the use of the CacheEngine.
CacheEngine implementation was specified, the cache will not be active.Result (Web) object can lead to errors during deserialization, especially if Result contains an InputStream. Serialization and deserialization of cached objects is handled by the Kryo library.The cache can be used with the following annotations:
Indicates that the cache must be evicted.
The annotation accepts the following parameters:
key the cache key. It can contains dynamic information via the usage of {class}, {method} and {n} (with n the method argument position). The default value is {class}.{name}.
evictOn allows you to provide the exception types that must cause a cache eviction. If classes are specified, the cache will only be evicted if the specified exceptions are thrown. The default value is {}.
noEvictOn allows you to provide the exception types that must not cause a cache eviction. The default value is {}.
Indicates that the result of the method will be cached and reused in future calls.
The annotation accepts the following parameters:
key the cache key. It can contains dynamic information via the usage of {class}, {method} and {n} (with n the method argument position). The default value is {class}.{name}.
timeToLive the maximum time to live in milliseconds. The default value is -1 (no expiration).
Indicates that the result of the method will be cached regardless value already exists.
key the cache key. It can contains dynamic information via the usage of {class}, {method} and {n} (with n the method argument position). The default value is {class}.{name}.
timeToLive the maximum time to live in milliseconds. The default value is -1 (no expiration).
@BindClass
+public class CacheExample {
+
+ @CachePut
+ public String exampleMethod() {
+ return "value";
+ }
+}
+The programmatic usage of the cache is very simple and can be done via the injection of CacheEngine.
public class CacheExample {
+
+ private final CacheEngine cacheEngine;
+
+ @Inject
+ public CacheExample(final CacheEngine cacheEngine) {
+ this.cacheEngine = cacheEngine;
+ }
+
+ public void exampleMethod() {
+ cacheEngine.set("key", "value");
+ }
+}
+Getting Started
Core
Relational Databases
NoSQL Databases
Cache
Internationalization
REST Client
Scheduler
Sendmail
Template
Virtual File Storage
Web
Testing
Advanced
Below is a diagram describing the boot sequence of Void Framework.
Getting Started
Core
Relational Databases
NoSQL Databases
Cache
Internationalization
REST Client
Scheduler
Sendmail
Template
Virtual File Storage
Web
Testing
Advanced
The core of the Void Framework is based on the scanning of different paths to discover elements to load. +5 types of elements are recognised during the scan:
com.google.inject.Module or extending com.google.inject.AbstractModuleBindable familly annotationAspect annotationProxyable annotationGuice modules that extend the abstract class AbstractModule are automatically detected and loaded when the application starts. Unless the module is explicitly disabled by the voidframework.core.disabledModules configuration.
OrderedModule interface.@Bindable is a specific annotation that allows annotated classes to be considered as candidates for auto-detection during classpath scan.
+Other class-level specialized annotations can also be considered as identifying a bindable and provide a clearer identification of the purpose of the class.
| Annotation | Description |
|---|---|
@Bindable | |
@Controller | Indicates that an annotated class is a “Controller” |
@Repository | Indicates that an annotated class is a “Repository” |
@Service | Indicates that an annotated class is a “Service” |
The configuration key voidframework.core.bindExtraInterfaces is used to define a set of interfaces for which to bind the found implementations. For example, converters are automatically detected in this way via the TypeConverter interface.
Aspect Oriented Programming (AOP) can be used with Void Framework, see chapter “Advanced / Aspect Oriented Programming” section for more information.
The @Proxyable annotation indicates that the implementation of the annotated interface is a proxy that will be configured by one of the modules that will be loaded when the application starts. The module can retrieve the interface(s) it is interested in via the ScannedClassesToLoad variable provided in the module constructor.
Scanning classes can, for several reasons, drastically increase the start-up time of the application. To mitigate this phenomenon, it is possible to use a bootstrap file, generated in advance, containing the useful classes detected during the scan. At compile time, the file resources/classpath.bootstrap will be created.
To activate the generator, simply add the following lines to the pom.xml file.
<plugin>
+ <groupId>org.codehaus.mojo</groupId>
+ <artifactId>exec-maven-plugin</artifactId>
+ <version>3.0.0</version>
+ <executions>
+ <execution>
+ <id>generate-classpath-bootstrap</id>
+ <goals>
+ <goal>java</goal>
+ </goals>
+ <phase>process-sources</phase>
+ <configuration>
+ <mainClass>dev.voidframework.core.classestoload.generator.ClasspathBootstrapGenerator</mainClass>
+ <arguments>
+ <argument>${project.build.outputDirectory}</argument>
+ </arguments>
+ </configuration>
+ </execution>
+ </executions>
+</plugin>
+Getting Started
Core
Relational Databases
NoSQL Databases
Cache
Internationalization
REST Client
Scheduler
Sendmail
Template
Virtual File Storage
Web
Testing
Advanced
Depending on your runtime environment, you may only need to run a few features of your application. And so, load only the necessary modules and bindable classes.
The @ConditionalFeature annotation allows a feature to be enabled or disabled depending on the processing performed by the class supplied as a parameter. This class must implement the Condition interface.
The annotation accepts the following parameter:
value is used to define the implementation of Condition interface to use.Exemple
@WebController
+@ConditionalFeature(MyCustomCondition.class)
+public final class SampleController {
+}
+The @ConfigurationConditionalFeature annotation allows a feature to be enabled or disabled based on a value in the configuration, properties, or environment variables.
The annotation accepts the following parameter:
value is used to define the name of the configuration/property/env. variable to be read.expectedValue is used to define all possible expected values. The default value is {"true", "enabled", "yes", "1"}.Exemple
@WebController
+@ConfigurationConditionalFeature("feature.name")
+public final class SampleController {
+}
+The @RunInDevModeConditionalFeature annotation allows a feature to be enabled only if the dev mode is set to true.
Exemple
@WebController
+@RunInDev
+public final class SampleController {
+}
+To create your own condition, simply implement the Condition interface and use this new implementation with the @ConditionalFeature annotation. You can also create your own annotation if you wish.
Exemple
public class RunInDevModeCondition implements Condition {
+
+ @Override
+ public boolean isEnabled(final Config configuration,
+ final Class<?> annotatedClassType,
+ final AnnotationMetadata annotationMetadata) {
+
+ return configuration.getBoolean("voidframework.core.runInDevMode");
+ }
+}
+@Documented
+@Target(ElementType.TYPE)
+@Retention(RetentionPolicy.RUNTIME)
+@ConditionalFeature(RunInDevModeCondition.class)
+public @interface RunInDevModeConditionalFeature {
+}
+Getting Started
Core
Relational Databases
NoSQL Databases
Cache
Internationalization
REST Client
Scheduler
Sendmail
Template
Virtual File Storage
Web
Testing
Advanced
Void Framework use a configuration file format is HOCON (Human-Optimized Config Object Notation). This format supports types such as integer, long, boolean, double, string, list and object. It is also possible to include other files by using include. There are two ways to write comments: using // or #. Comments can be written in-line at the end of the line or in separate lines. For more information on Typesafe Config, visit the Github project’s page.
These are the default filenames that the Config library looks for on the classpath:
application.confapplication.jsonapplication.propertiesreference.confIt is sometimes useful to be able to override or simply set specific configuration values from the parameters provided at JVM startup. To do this, all properties defined via -D flag are accessible from the Configuration object.
If you need to use a custom/external configuration file, especially when your application is deployed on a server. You can use the flag -Dconfig.file to specify location of the configuration file to use.
-Dconfig.file=/path/to/your/application.conf
+@Service
+public class MyService {
+
+ private final Config configuration;
+
+ @Inject
+ public MyService(final Config configuration) {
+ this.configuration = configuration;
+ }
+
+ public void test() {
+ final boolean runInDevMode = configuration.getBoolean("voidframework.core.runInDevMode");
+ if (runInDevMode) {
+ // Do something
+ } else {
+ // Do something else
+ }
+ }
+}
+Once packaged, you will probably need to modulate the configuration of the application depending on the environment (ie: development, integration, production). The typical example is the configuration of the data source, you cannot define this information directly in the configuration file included in the Jars files. Void Framework allows you to load configurations that will be stored elsewhere, such as on etcd or vault.
To enable remote configuration loading, you must declare one or more providers to be used via the configuration key voidframework.core.remoteConfiguration.providers.
Provider “etcd”
<dependency>
+ <groupId>dev.voidframework</groupId>
+ <artifactId>voidframework-remoteconf-etcd</artifactId>
+ <version>1.16.0</version>
+</dependency>
+voidframework {
+ code {
+ remoteConfiguration {
+ providers = "dev.voidframework.remoteconfiguration.provider.EtcdRemoteConfigurationProvider"
+
+ # Configuration of the remote configuration provider "etcd"
+ etcd {
+
+ # API endpoint. HTTPS endpoint could be used,
+ # but the SSL certificate must be valid
+ endpoint = "http://127.0.0.1:2379/"
+ endpoint = ${?VOID_REMOTECONF_ETCD_ENDPOINT}
+
+ # Authentication username
+ username = ""
+ username = ${?VOID_REMOTECONF_ETCD_USERNAME}
+
+ # Authentication password
+ password = ""
+ password = ${?VOID_REMOTECONF_ETCD_PASSWORD}
+
+ # Prefix. Get only values with key beginning
+ # with the configured prefix. With etcd, it
+ # must be a directory.
+ prefix = "/"
+ prefix = ${?VOID_REMOTECONF_ETCD_PREFIX}
+ }
+ }
+ }
+}
+Provider “http”
<dependency>
+ <groupId>dev.voidframework</groupId>
+ <artifactId>voidframework-remoteconf-http</artifactId>
+ <version>1.16.0</version>
+</dependency>
+voidframework {
+ code {
+ remoteConfiguration {
+ providers = "dev.voidframework.remoteconfiguration.provider.HttpRemoteConfigurationProvider"
+
+ # Configuration of the remote configuration provider "http"
+ http {
+
+ # Endpoint. HTTPS endpoint could be used,
+ # but the SSL certificate must be valid
+ endpoint = "http://127.0.0.1:2379/"
+ endpoint = ${?VOID_REMOTECONF_HTTP_ENDPOINT}
+
+ # Method to use (ie: GET, POST, ...)
+ method = "GET"
+ method = ${?VOID_REMOTECONF_HTTP_METHOD}
+
+ # Authentication username
+ username = null
+ username = ${?VOID_REMOTECONF_HTTP_USERNAME}
+
+ # Authentication password
+ password = null
+ password = ${?VOID_REMOTECONF_HTTP_PASSWORD}
+ }
+ }
+ }
+}
+Creating a new provider is very easy. Simply implement the RemoteConfigurationProvider interface or extend the AbstractRemoteConfigurationProvider abstract class.
Getting Started
Core
Relational Databases
NoSQL Databases
Cache
Internationalization
REST Client
Scheduler
Sendmail
Template
Virtual File Storage
Web
Testing
Advanced
Getting Started
Core
Relational Databases
NoSQL Databases
Cache
Internationalization
REST Client
Scheduler
Sendmail
Template
Virtual File Storage
Web
Testing
Advanced
The dev.voidframework.core.lang package contains a set of Java classes that extend the basic JDK.
CUID is a collision-resistant ID optimized for horizontal scaling and performance. Read more at https://usecuid.org/. Read the Java file to get more information about available methods.
// Random CUID
+final CUID cuid = CUID.randomCUID();
+
+// CUID from String representating a CUID
+final CUID cuid = CUID.fromString("cl9gts1kw00393647w1z4v2tc");
+
+// Check if String contains a valid CUID (implicitly called by "fromString" method)
+final boolean isValid = CUID.isValid("cl9gts1kw00393647w1z4v2tc");
+CUID works with Jackson serialization/deserialization.Either represents a value of one or two possible types (a disjoint union). Read the Java file to get more information about available methods.
final Either<String, Integer> either = Either.ofLeft("1337");
+
+final Integer value = either.match(Integer::valueOf, right -> right);
+TypedMap is a simple hashmap with typed key. Typed means that the type of the value associated to the key is part of the key itself. Read the Java file to get more information about available methods.
final TypedMap.Key<String> STRING_KEY = TypedMap.Key.of("STRING_KEY", String.class)
+final TypedMap typedMap = new TypedMap();
+
+typedMap.put(STRING_KEY, "Hello World");
+Getting Started
Core
Relational Databases
NoSQL Databases
Cache
Internationalization
REST Client
Scheduler
Sendmail
Template
Virtual File Storage
Web
Testing
Advanced
Void Framework provides a way to manage the life of a component. The @LifeCycleStart and @LifeCycleStop annotations allow you to define the methods to be called automatically when the application is started and stopped.
Indicates that this method should be called when the application starts.
The annotation accepts the following parameter:
priority is used to define when the method will be called in relation to the others. The lower the priority, the higher the priority of the method. The default value is 1000.Indicates that this method should be called when the application is stopped.
The annotation accepts the following parameters:
priority defines when the method will be called in relation to the others. The lower the priority, the higher the priority of the method. The default value is 1000.gracefulStopTimeoutConfigKey allows you to provide a configuration key to retrieve the maximum time (duration or milliseconds) to wait before giving up and continuing to stop the application. If no key is specified, the default value 0 will be used.@BindClass
+@Singleton
+public final class ExampleLifeCycle {
+
+ @Inject
+ private final Config configuration;
+
+ @Inject
+ public ExampleLifeCycle(final Config configuration) {
+ this.configuration = configuration;
+ }
+
+ @LifeCycleStart(priority = 1000)
+ public void onStart() {
+ }
+
+ @LifeCycleStop(priority = 1)
+ public void onStop() {
+ }
+}
+Getting Started
Core
Relational Databases
NoSQL Databases
Cache
Internationalization
REST Client
Scheduler
Sendmail
Template
Virtual File Storage
Web
Testing
Advanced
Type conversion is a mechanism for converting data from one type to another. For example, convert the string “1234” to an integer. The conversion is very useful when moving from one layer to another in DDD-based architectures. It is also used in the web feature to convert path param to typed values in the controller.
The conversion is used via the Conversion service which is accessible via direct injection.
public final class ExampleConversionService {
+
+ private final Conversion conversion;
+
+ @Inject
+ public ExampleConversionService(final Conversion conversion) {
+ this.conversion = conversion;
+ }
+
+ public void example() {
+ final String boolAsString = "true";
+ final Boolean bool = conversion.convert(boolAsString, Boolean.class);
+ // or conversion.convert(boolAsString, String.class, Boolean.class);
+ }
+}
+The creation of a new converter is very simple, you just have to implement the TypeConverter interface and to register this implementation with the ConverterManager. The registration will be done automatically if the newly created converter is in a package which will be scanned at the start of the application (see the voidframework.core.acceptedScanPaths parameter).
/**
+ * Convert a {@code String} into an {@code Boolean}.
+ */
+public final class StringToBooleanConverter implements TypeConverter<String, Boolean> {
+
+ private static final List<String> VALUE_TRUE_LIST = Arrays.asList("1", "true", "y", "yes");
+
+ @Override
+ public Boolean convert(final String source) {
+ // The "source" variable will never be null
+ return VALUE_TRUE_LIST.contains(source.toLowerCase(Locale.ENGLISH));
+ }
+}
+Getting Started
Core
Relational Databases
NoSQL Databases
Cache
Internationalization
REST Client
Scheduler
Sendmail
Template
Virtual File Storage
Web
Testing
Advanced
Getting Started
Core
Relational Databases
NoSQL Databases
Cache
Internationalization
REST Client
Scheduler
Sendmail
Template
Virtual File Storage
Web
Testing
Advanced
To create a new application with Void Framework, simply create a new Maven project and then add the necessary dependencies.
The very first dependency to include in your pom.xml is voidframework-core, without it you will not be able to start the application. The dependencies to be added will depend on the features you wish to use on your application, so do not hesitate to consult the different chapters of the documentation.
In this example, we will also use the web feature, so the voidframework-web dependency will also be added.
<dependency>
+ <groupId>dev.voidframework</groupId>
+ <artifactId>voidframework-core</artifactId>
+ <version>1.16.0</version>
+</dependency>
+<dependency>
+ <groupId>dev.voidframework</groupId>
+ <artifactId>voidframework-web</artifactId>
+ <version>1.16.0</version>
+</dependency>
+Void Framework use a configuration file format is HOCON (Human-Optimized Config Object Notation). This format supports types such as integer, long, boolean, double, string, list and object. It is also possible to include other files by using include. There are two ways to write comments: using // or #. Comments can be written in-line at the end of the line or in separate lines. For more information on Typesafe Config, visit the Github project’s page.
The first thing to do is to indicate the paths to scan to find the elements of your application that can be loaded. The default location for the configuration file is resources/application.conf. And to define a key to sign the Session (even if you plan to not use session).
voidframework {
+ core {
+ acceptedScanPaths += "controller"
+ }
+
+ web {
+ session {
+ signatureKey = "BUXpcQ6OAXMGR45sV9bjeq161raMoIrNiJw3z18leM4TRIBVUHsZsrTlK58fX2JD"
+ }
+ }
+}
+Add the following controller to the controller package or any package that will be considered by the acceptedScanPaths configuration.
package controller;
+
+import dev.voidframework.web.bindable.WebController;
+import dev.voidframework.web.http.HttpContentType;
+import dev.voidframework.web.http.Result;
+import dev.voidframework.web.http.param.RequestRoute;
+import dev.voidframework.web.routing.HttpMethod;
+
+@WebController
+public final class MyFirstController {
+
+ @RequestRoute(method = HttpMethod.GET, route = "/")
+ public Result sayHello() {
+ return Result.ok("Hello World!");
+ }
+}
+To start the application, you have to instantiate ApplicationLauncher and then call the launch method.
final VoidApplication app = new VoidApplication();
+app.launch();
+You can now start your application and go to the URL http://127.0.0.1:9000 in your favourite web browser.
Getting Started
Core
Relational Databases
NoSQL Databases
Cache
Internationalization
REST Client
Scheduler
Sendmail
Template
Virtual File Storage
Web
Testing
Advanced
To use the Void Framework, you need Java JDK 17 or higher, Maven 3 or higher and the Void Framework JAR files. These JAR files are published to the Maven Repository.
To check that you have Java JDK 17 or higher, enter the following in a shell:
#> java -version
+You should see something like:
openjdk version "17.0.2" 2022-01-18
+OpenJDK Runtime Environment (build 17.0.2+8-86)
+OpenJDK 64-Bit Server VM (build 17.0.2+8-86, mixed mode, sharing)
+If Java JDK is not available on your computer, you can get Java JDK from Oracle’s website.
To check that you have Maven 3 or higher, enter the following in a shell:
#> mvn -version
+You should see something like:
Apache Maven 3.8.1 (05c21c65bdfed0f71a2f2ada8b84da59348c4c5d)
+Maven home: C:\maven\lib\maven3
+Java version: 17.0.2, vendor: Oracle Corporation, runtime: C:\Users\user\.jdks\openjdk-17.0.2
+Default locale: en_US, platform encoding: UTF-8
+OS name: "windows 11", version: "10.0", arch: "amd64", family: "windows"
+If Maven is not available on your computer, you can get Maven from Apache Maven Project.
Getting Started
Core
Relational Databases
NoSQL Databases
Cache
Internationalization
REST Client
Scheduler
Sendmail
Template
Virtual File Storage
Web
Testing
Advanced
Sometimes important changes will be introduced that will break the backwards compatibility with older versions of the framework. On this page you will find the necessary steps to make your application work with the latest version.
Initial release
ApplicationLauncher has been renamed to VoidApplicationvoidframework.web.session.signatureKey is now requireddev.voidframework.web.http.filter.csrf package, if you use this filter in your application, you will need to modify the configuration key voidframework.web.globalFiltersdev.voidframework.web.http.filter.security package, if you use this filter in your application, you will need to modify the configuration key voidframework.web.globalFiltersNoCSRF, RequestBody, RequestPath, RequestRoute, RequestVariable and WithFilter has been moved to the package dev.voidframework.web.http.annotation@CacheRemove and @CacheResult are now in the package dev.voidframework.cache.annotationconnectionTimeout is now a duration, a time unit must be providedaddInList method of the voidframework-redis module now adds items to the end of the listdev.voidframework.core.helper has been moved to dev.voidframework.core.utils and the suffix Utils has been added (ie: IO is now named IOUtils)Result::redirectPermanentlyTo has been renamed to Result::redirectMovedPermanentlyResult::redirectTemporaryTo has been renamed to Result::redirectFoundHttpMimeType has been renamed to HttpMimeTypesHttpRequestBodyContent::asRaw now return an InputStreamValidationError is now a record. All methods are now named without any prefix (ie: getMessage() is now named message())Router::addRoute has changed. The routeUrl parameter is now of type RouteURL. You can use RouteURL::of to migrate to the new format@BindClass has been renamed to @Bindablevoidframework.web.server.listenHost and voidframework.web.server.listenPort have been renamed to voidframework.web.server.http.listenHost and voidframework.web.server.http.listenPortjakarta.inject, jakarta.servlet and jakarta.persistence namespaces. Read more at https://github.com/google/guice/wiki/Guice700PARENTHESIS_OPEN and PARENTHESIS_CLOSE are no longer inverted. If you use these two constants, take care to adapt their useGetting Started
Core
Relational Databases
NoSQL Databases
Cache
Internationalization
REST Client
Scheduler
Sendmail
Template
Virtual File Storage
Web
Testing
Advanced
Getting Started
Core
Relational Databases
NoSQL Databases
Cache
Internationalization
REST Client
Scheduler
Sendmail
Template
Virtual File Storage
Web
Testing
Advanced
Getting Started
Core
Relational Databases
NoSQL Databases
Cache
Internationalization
REST Client
Scheduler
Sendmail
Template
Virtual File Storage
Web
Testing
Advanced
Depending on your needs, you may need to use translated messages in different languages. By default the Void Framework provides a ResourceBundle based implementation. If your needs require more advanced management or different storage (ie: DB rather than files), you can easily use your own implementation.
To enable this module, just add following lines to the pom.xml file of your project.
<dependency>
+ <groupId>dev.voidframework</groupId>
+ <artifactId>voidframework-i18n</artifactId>
+ <version>1.16.0</version>
+</dependency>
+Translations should be placed in a file named messages_<LANG>.properties (ie: messages_en.properties) in the resources directory.
resources/messages_fr.properties
lang.fr=Français
+resources/messages_en.properties
lang.fr=French
+The retrieval of translated messages is done through the use of the Internationalization service.
Example
public final class InternationalizationExample {
+
+ private final Internationalization i18n;
+
+ @Inject
+ public InternationalizationExample(final Internationalization i18n) {
+ this.i18n = i18n;
+ }
+
+ public void i18n() {
+ // Output: Français
+ i18n.getMessage(Locale.FRENCH, "lang.fr");
+
+ // Output: French
+ i18n.getMessage(Locale.ENGLISH, "lang.fr");
+ }
+}
+The plural form can be handled in two ways, the first uses ChoiceFormat which allows via an advanced syntax to add some intelligence to the translation messages. The second, more simple, is based on the use of new keys with special naming.
ChoiceFormat on official
+Java documentation.Example (ChoiceFormat)
key=This element contains {0,choice,0#no comments|1#one comment|1<{0,number,000} comments}
+public void i18n() {
+ // Output: This element contains 0 comments
+ i18n.getMessage(Locale.ENGLISH, "key", 0);
+
+ // Output: This element contains 1 comment
+ i18n.getMessage(Locale.ENGLISH, "key", 1);
+
+ // Output: This element contains 1337 comments
+ i18n.getMessage(Locale.ENGLISH, "key", 1337);
+}
+Example (New key)
inbox.0=Inbox "{0}" contains no messages
+inbox.1=Inbox "{0}" contains one message
+inbox.2=Inbox "{0}" contains {1} messages
+public void i18n() {
+ // Output: Inbox "Unread" contains no messages
+ i18n.getMessage(Locale.ENGLISH, 0, "inbox", "Unread", 0);
+
+ // Output: Inbox "Unread" contains one message
+ i18n.getMessage(Locale.ENGLISH, 1,"inbox", "Unread", 1);
+
+ // Output: Inbox "Unread" contains 42 messages
+ i18n.getMessage(Locale.ENGLISH, 42, "inbox", "Unread", 42);
+}
+Getting Started
Core
Relational Databases
NoSQL Databases
Cache
Internationalization
REST Client
Scheduler
Sendmail
Template
Virtual File Storage
Web
Testing
Advanced
Getting Started
Core
Relational Databases
NoSQL Databases
Cache
Internationalization
REST Client
Scheduler
Sendmail
Template
Virtual File Storage
Web
Testing
Advanced
Sometimes a relational database does not fit the need, so you have to turn to more specific, non-relational databases. Void Framework provides the tools to use the NoSQL database Redis.
To enable this module, just add following lines to the pom.xml file of your project.
<dependency>
+ <groupId>dev.voidframework</groupId>
+ <artifactId>voidframework-redis</artifactId>
+ <version>1.16.0</version>
+</dependency>
+The following configuration key can be used in the configuration file of your application.
voidframework.redis.host the Redis server host. Default value is 127.0.0.1.voidframework.redis.port the port on which the server is listening. Default value is 6379.voidframework.redis.username the authentication username (Redis 6 ACL). By default, this value is empty.voidframework.redis.password the authentication password. By default, this value is empty.voidframework.redis.defaultDatabase the database to use by default. Default value is 1.voidframework.redis.connPool.connectionTimeout the connection timeout in milliseconds. Default value is 2000 milliseconds.voidframework.redis.connPool.maximumWait the maximum time to obtain a connection from the pool. Default value is 2000 milliseconds.voidframework.redis.connPool.minimumIdle the minimum number of idle connections. Default value is 2.voidframework.redis.connPool.maximumIdle the maximum number of idle connections. Default value is 8.voidframework.redis.connPool.maximumPoolSize the maximum number of connections. Default value is 16.If you wan to monitoring Redis health, you can enable the healthcheck module by adding the following lines to the pom.xml file of your project.
<dependency>
+ <groupId>dev.voidframework</groupId>
+ <artifactId>voidframework-redis-healthcheck</artifactId>
+ <version>1.16.0</version>
+</dependency>
+@Service
+public final class ExampleService {
+
+ private final Redis redis;
+
+ @Inject
+ public ExampleService(final Redis redis) {
+ this.redis = redis;
+ }
+
+ public void example() {
+ final long nextValue = this.redis.increment("incr.key");
+ }
+}
+Getting Started
Core
Relational Databases
NoSQL Databases
Cache
Internationalization
REST Client
Scheduler
Sendmail
Template
Virtual File Storage
Web
Testing
Advanced
Your application needs to connect to one or more databases? Void Framework is capable of handling multiple data sources at the same time through the use of DataSourceManager. It will provide all the necessary methods to obtain a connection from the desired data source. Each data source can be configured independently. Your application can, for example, be connected to PostgreSQL and Oracle at the same time.
Void Framework offers different implementations, depending on the implementation chosen, the configuration keys may change. Here you will find all the information to fully configure each implementation.
<dependency>
+ <groupId>dev.voidframework</groupId>
+ <artifactId>voidframework-datasource-c3p0</artifactId>
+ <version>1.16.0</version>
+</dependency>
+<dependency>
+ <groupId>dev.voidframework</groupId>
+ <artifactId>voidframework-datasource-hikaricp</artifactId>
+ <version>1.16.0</version>
+</dependency>
+The following configuration keys can be used in the configuration file of your application:
voidframework.datasource.default.driver the driver to be used to communicate with the database.voidframework.datasource.default.url the JDBC format URL to be used to reach the database.voidframework.datasource.default.username the username to be provided durint the authentication step.voidframework.datasource.default.password the password to be provided durint the authentication step.voidframework.datasource.default.statementCacheNumDeferredCloseThreads the number of threads to track when Connections are in use, and only destroy Statements when their parent Connections are not otherwise in use.voidframework.datasource.default.cachePrepStmts enable or disable the prepared statements cache.voidframework.datasource.default.prepStmtCacheSize the number of prepared statements cache to keep in the cache.voidframework.datasource.default.prepStmtCacheSqlLimit the size of the largest SQL query for which the parsing result will be keep in the cache.voidframework.datasource.default.autoCommit enable or disable the auto commit behavior when the connection goes back into the pool.voidframework.datasource.default.connectionInitSql the SQL statement that will be executed after every new connection.voidframework.datasource.default.connectionTestQuery the SQL statement that will be executed to test if the connection is still valid.voidframework.datasource.default.connectionTimeout the milliseconds to wait before timing out during the connection.voidframework.datasource.default.idleTimeout the milliseconds to wait before closing an unused connection.voidframework.datasource.default.keepaliveTime the milliseconds to wait before attempting to keep the connection alive.voidframework.datasource.default.minimumIdle the minimum number of alive connections in the pool.voidframework.datasource.default.maximumPoolSize the maximum number of connections allowed in the pool.voidframework.datasource.default.acquireIncrement determines how many connections at a time C3P0 will try to acquire when the pool is exhausted.voidframework.datasource.default.maxConnectionAge the milliseconds to wait before closing a connection.@Service
+public class ExampleDataSourceService {
+
+ private final Provider<DataSourceManager> dataSourceManagerProvider;
+
+ @Inject
+ public ExampleService(final Provider<DataSourceManager> dataSourceManagerProvider) {
+ this.dataSourceManagerProvider = dataSourceManagerProvider;
+ }
+
+ public void example() throws SQLException {
+ final Connection conn = dataSourceManagerProvider
+ .get()
+ .getConnection("default");
+ /* ... */
+ conn.close();
+ }
+}
+Getting Started
Core
Relational Databases
NoSQL Databases
Cache
Internationalization
REST Client
Scheduler
Sendmail
Template
Virtual File Storage
Web
Testing
Advanced
H2 is an open-source lightweight relational database. It allows you to quickly have an operational database and its use is very practical during the development phase of an application. This module provides the H2 dependency and the useful H2 web console.
To enable this module, just add following lines to the pom.xml file of your project.
<dependency>
+ <groupId>dev.voidframework</groupId>
+ <artifactId>voidframework-h2</artifactId>
+ <version>1.16.0</version>
+</dependency>
+The following configuration keys can be used in the configuration file of your application:
voidframework.h2.webPort the listen port of the H2 console. The default value is 9002.voidframework.h2.webAdminPassword the password to access preferences and tools of H2 console. The default value is null (disabled).voidframework.h2.webAllowOthers specifies if other computers are allowed to connect. The default value is false.Once your application has started, you can access the H2 web console by going to http://127.0.0.1:9002.
voidframework.core.runInDevMode = true).Getting Started
Core
Relational Databases
NoSQL Databases
Cache
Internationalization
REST Client
Scheduler
Sendmail
Template
Virtual File Storage
Web
Testing
Advanced
Getting Started
Core
Relational Databases
NoSQL Databases
Cache
Internationalization
REST Client
Scheduler
Sendmail
Template
Virtual File Storage
Web
Testing
Advanced
Hibernate is an Object Relational Mapper (ORM). It provides a framework for mapping an object-oriented domain model to a relational database.
javax.persistence package is no longer available and is replaced by jakarta.persistence package.This module adds support for the Transactional annotation as well as setting up an EntityManager provider pre-configured with all the data sources configured via the datasource module. There is no special configuration to apply, just add the voidframework-persistence-hibernate module to the pom.xml file of your project.
<dependency>
+ <groupId>dev.voidframework</groupId>
+ <artifactId>voidframework-persistence-hibernate</artifactId>
+ <version>1.16.0</version>
+</dependency>
+The following configuration key can be used in the configuration file of your application.
voidframework.persistence.modelsJarUrlPattern Pattern to select Jar to scan to find models. For more information, see below the section “Speed-up PersistenceEntityFactory initialization time”. Default value is auto.voidframework.datasource.default.dialect the Redis server host. Default value is null.final EntityManager entityManager = this.entityManagerProvider.get();
+final EntityTransaction transaction = entityManager.getTransaction();
+
+transaction.begin();
+/* insert/update/delete operations */
+transaction.commit();
+| Identifier type | Assignable to | Identifier generator annotation |
|---|---|---|
| CUID | CUID, String, byte[] | @CuidGenerator |
| UUID | UUID, String, byte[] | @UuidGenerator |
@Entity
+@DynamicUpdate
+@Table(name = "NEWS")
+public class NewsModel extends Model {
+
+ @Id
+ @UuidGenerator
+ @Column(name = "ID", updatable = false, nullable = false)
+ private UUID id;
+
+ @Column(name = "TITLE", nullable = false)
+ private String title;
+
+ @Column(name = "CONTENT", nullable = false)
+ private String content;
+
+ @Column(name = "PUBLISHED_AT", columnDefinition = "TIMESTAMP", updatable = false, nullable = false)
+ private LocalDateTime publishedAt;
+
+ @Override
+ public String toString() {
+ return this.getClass().getName() + "[id=" + this.id + "]";
+ }
+
+ public String getTitle() {
+ return title;
+ }
+
+ public void setTitle(final String title) {
+ this.title = title;
+ }
+
+ public String getContent() {
+ return content;
+ }
+
+ public void setContent(final String content) {
+ this.content = content;
+ }
+
+ public LocalDateTime getPublishedAt() {
+ return publishedAt;
+ }
+
+ public void setPublishedAt(final LocalDateTime publishedAt) {
+ this.publishedAt = publishedAt;
+ }
+}
+public class NewsRepositoryImpl implements NewsRepository {
+
+ private final Conversion conversion;
+ private final Provider<EntityManager> entityManagerProvider;
+
+ @Inject
+ public NewsRepositoryImpl(final Conversion conversion,
+ final Provider<EntityManager> entityManagerProvider) {
+ this.conversion = conversion;
+ this.entityManagerProvider = entityManagerProvider;
+ }
+
+ @Overide
+ @Transactional(Transactional.TxType.SUPPORTS)
+ public List<News> findTop10ByPublishedAtBeforeNowOrderByPublishedAtDesc() {
+ final List<NewsModel> newsModelList = entityManagerProvider.get()
+ .createQuery("SELECT x FROM NewsModel x WHERE x.publishedAt <= CURRENT_TIMESTAMP ORDER BY x.publishedAt DESC, x.id ASC", NewsModel.class)
+ .setMaxResults(10)
+ .getResultList();
+
+ return conversion.convert(newsModelList, NewsModel.class, News.class);
+ }
+By default, Void Framework tells the PersistenceEntityFactory to scan all JARs of the application’s and dependencies. This scan is necessary to detect models. If you want to greatly improve this initialization time, it is advisable to set the voidframework.persistence.modelsJarUrlPattern configuration key to indicate which JARs to keep. The could be null, a regular expression, or auto (default value).
If the configuration is set to null, only the current JAR / classpath (when started in IDE) will be used. auto is like using the regular expression (.*).
Getting Started
Core
Relational Databases
NoSQL Databases
Cache
Internationalization
REST Client
Scheduler
Sendmail
Template
Virtual File Storage
Web
Testing
Advanced
jOOQ is an Object Relational Mapper (ORM). It provides a framework for mapping an object-oriented domain model to a relational database.
This module adds support for the Transactional annotation as well as setting up an DSLContext provider pre-configured with all the data sources configured via the datasource module. There is no special configuration to apply, just add the voidframework-persistence-jooq module to the pom.xml file of your project.
<dependency>
+ <groupId>dev.voidframework</groupId>
+ <artifactId>voidframework-persistence-jooq</artifactId>
+ <version>1.16.0</version>
+</dependency>
+To work, jOOQ needs files generated during the compilation of the project. It is possible to generate the required classes in different ways (scan database, scan entity, …). For more information, please visit the official jOOQ website.
To enable code generation, add & customize the code below.
pom.xml
<build>
+ <plugins>
+ <plugin>
+ <groupId>org.jooq</groupId>
+ <artifactId>jooq-codegen-maven</artifactId>
+ <executions>
+ <execution>
+ <id>jooq-codegen</id>
+ <phase>generate-sources</phase>
+ <goals>
+ <goal>generate</goal>
+ </goals>
+ </execution>
+ </executions>
+ <configuration>
+ <configurationFile>${project.build.sourceDirectory}/../resources/jooq-code-generator.xml</configurationFile>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>org.codehaus.mojo</groupId>
+ <artifactId>build-helper-maven-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>add-jooq-gen-code-test-sources</id>
+ <phase>generate-sources</phase>
+ <goals>
+ <goal>add-test-source</goal>
+ </goals>
+ <configuration>
+ <sources>
+ <source>${project.build.directory}/generated-sources/java/</source>
+ </sources>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+</build>
+jooq-code-generator.xml
<configuration>
+ <generator>
+ <database>
+ <name>org.jooq.meta.extensions.jpa.JPADatabase</name>
+ <properties>
+
+ <!-- A comma separated list of Java packages, that contain your entities -->
+ <property>
+ <key>packages</key>
+ <value>com.myapp.models</value>
+ </property>
+
+ <!-- Whether JPA 2.1 AttributeConverters should be auto-mapped to jOOQ Converters.
+ Custom <forcedType/> configurations will have a higher priority than these auto-mapped converters.
+ This defaults to true. -->
+ <property>
+ <key>useAttributeConverters</key>
+ <value>true</value>
+ </property>
+
+ <!-- The default schema for unqualified objects:
+
+ - public: all unqualified objects are located in the PUBLIC (upper case) schema
+ - none: all unqualified objects are located in the default schema (default)
+
+ This configuration can be overridden with the schema mapping feature -->
+ <property>
+ <key>unqualifiedSchema</key>
+ <value>public</value>
+ </property>
+ </properties>
+ </database>
+ <target>
+ <packageName>com.myapp.models.jooq</packageName>
+ <directory>target/generated-test-sources/java</directory>
+ </target>
+ </generator>
+</configuration>
+final DSLContext dslContext = this.dslContextProvider.get();
+dslContext.transaction((configuration) -> {
+ /* insert/update/delete operations with configuration.dsl() */
+});
+@Entity
+@Table(name = "NEWS")
+public class NewsModel {
+
+ @Id
+ @Column(name = "ID", nullable = false)
+ public String id;
+
+ @Column(name = "TITLE", nullable = false)
+ public String title;
+}
+public class NewsRepositoryImpl implements NewsRepository {
+
+ private final Conversion conversion;
+ private final Provider<DSLContext> dslContext;
+
+ @Inject
+ public NewsRepositoryImpl(final Conversion conversion,
+ final Provider<DSLContext> dslContextProvider) {
+ this.conversion = conversion;
+ this.dslContext = dslContext;
+ }
+
+ @Overide
+ @Transactional(Transactional.TxType.SUPPORTS)
+ public List<News> findAll() {
+ final List<NewsModel> newsModelList = dslContextProvider.get()
+ .select()
+ .from(Tables.NEWS)
+ .where(Tables.NEWS.TITLE.isNotNull())
+ .fetchInto(NewsModel.class);
+
+ return conversion.convert(newsModelList, NewsModel.class, News.class);
+ }
+Getting Started
Core
Relational Databases
NoSQL Databases
Cache
Internationalization
REST Client
Scheduler
Sendmail
Template
Virtual File Storage
Web
Testing
Advanced
It is possible to work within a transaction in two ways: manually or via the use of the @Transactional annotation. The first one is highly dependent of the used backend (ie: Hibernate vs jOOQ), the second one is fully handled by Void Framework. For more information on the manual management of transactions, go to the page corresponding to the backend used.
The transactional annotation itself defines the scope of a single database transaction. Void Framework fully implements Transactional (Jakarta) annotation, so all availables options can be used without any restrictions.
If annotation is used both at class and method level, the second one will take ascendance.
Transactional annotation does not allow the specification of the data source to be used. The current implementation will only work for data source default.Getting Started
Core
Relational Databases
NoSQL Databases
Cache
Internationalization
REST Client
Scheduler
Sendmail
Template
Virtual File Storage
Web
Testing
Advanced
Getting Started
Core
Relational Databases
NoSQL Databases
Cache
Internationalization
REST Client
Scheduler
Sendmail
Template
Virtual File Storage
Web
Testing
Advanced
Retrofit 2 is a REST Client for Java allowing to retrieve and upload data via HTTP.
To enable this module, just add following lines to the pom.xml file of your project.
<dependency>
+ <groupId>dev.voidframework</groupId>
+ <artifactId>voidframework-restclient</artifactId>
+ <version>1.16.0</version>
+</dependency>
+All interfaces annotated with @RestClient will be automatically proxyfied with the Retrofit 2 client.
The following configuration key can be used in the configuration file of your application
voidframework.restclient.maxIdleConnections the number of connection to keep idle. The default value is 5.voidframework.restclient.keepAliveDuration the duration for which connections will be kept idle before being closed. The default value is 5 minutes.voidframework.restclient.connectionTimeout the connect timeout duration. The default value is 15 seconds.voidframework.restclient.readTimeout the read timeout duration. The default value is 35 seconds.voidframework.restclient.authentication.type the authentication type to use (API_KEY, BASIC or BEARER). The default value is null (disabled).voidframework.restclient.authentication.apiKeyName the API key variable name. This setting is only used if the authentication type is set to API_KEY. The default value is null.voidframework.restclient.authentication.apiKeyValue the API key variable value. This setting is only used if the authentication type is set to API_KEY. The default value is null.voidframework.restclient.authentication.apiKeyAddTo where to add the API key (COOKIE, HEADER, QUERY_PARAMETER). This setting is only used if the authentication type is set to API_KEY. The default value is HEADER.voidframework.restclient.authentication.basicUsername the basic authentication username. This setting is only used if the authentication type is set to BASIC. The default value is null.voidframework.restclient.authentication.basicPassword the basic authentication password. This setting is only used if the authentication type is set to BASIC. The default value is null.voidframework.restclient.authentication.basicUseISO88591Encoding use ISO-8859-1 encoding rather than UTF-8. This setting is only used if the authentication type is set to BASIC. The default value is false.voidframework.restclient.authentication.bearerPrefix the bearer prefix to use in the request header. This setting is only used if the authentication type is set to BEARER. The default value is Bearer.voidframework.restclient.authentication.bearerToken the bearer token to use in the request header. This setting is only used if the authentication type is set to BEARER. The default value is null.voidframework.restclient.<service-name>.xxxvoidframework.restclient.echo-api.readTimeoutvoidframework {
+ restclient {
+
+ maxIdleConnections = 10
+
+ echo-api {
+
+ endpoint = "https://postman-echo.com"
+ maxIdleConnections = 1
+ readTimeout = "15 seconds"
+ }
+ }
+}
+@RestClient("echo-api")
+private interface EchoApiRestClient {
+
+ @GET("/get")
+ String echo(@Query("msg") final String message);
+
+ @POST("/post")
+ @Headers("Content-Type: application/json")
+ JsonNode postJsonNode(@Body final JsonNode message);
+}
+@Service
+private class SampleService {
+
+ private final EchoApiRestClient echoApiRestClient;
+
+ @Inject
+ public SampleService(final EchoApiRestClient echoApiRestClient) {
+
+ this.echoApiRestClient = echoApiRestClient;
+ }
+
+ public String mySample() {
+
+ return this.echoApiRestClient.echo("Hello World");
+ }
+}
+Getting Started
Core
Relational Databases
NoSQL Databases
Cache
Internationalization
REST Client
Scheduler
Sendmail
Template
Virtual File Storage
Web
Testing
Advanced
Getting Started
Core
Relational Databases
NoSQL Databases
Cache
Internationalization
REST Client
Scheduler
Sendmail
Template
Virtual File Storage
Web
Testing
Advanced
If your application have to schedule a job in a scheduled way (ie: every 5 seconds), not dependending when the application has started, you can schedule job by using delay. In other hand, if you want a precise scheduling not depending on when the application has started, you can use a CRON expression.
To enable this module, just add following lines to the pom.xml file of your project.
<dependency>
+ <groupId>dev.voidframework</groupId>
+ <artifactId>voidframework-scheduler</artifactId>
+ <version>1.16.0</version>
+</dependency>
+To define a job, simply use the @Scheduled annotation on one or more methods of a bindable class. As a reminder, a class is said to be bindable when it is annotated with, at least, the @Bindable annotation.
The @Scheduled annotation accepts the following parameters:
fixedDelay the time in milliseconds between the end of the last execution and the next execution. The default value is 0 (disabled).fixedRate the time in milliseconds between each execution. The default value is 0 (disabled).initialDelay the time in milliseconds to wait before the first execution of “fixedDelay” or “fixedRate”. The default value is 0 (disabled).cron a CRON-like expression. The default value is empty (disabled).cronZone the time zone for which the CRON expression will be resolved. The default value is UTC.The format of the CRON expression have to be composed of : second, minute, hour, day of month, month and day of week.
| Unit | Value | Step Value | Details |
|---|---|---|---|
| Second | 0 – 59 | 1 – 60 | |
| Minute | 0 – 59 | 1 – 60 | |
| Hour | 0 – 23 | 1 – 24 | |
| Day of Month | 1 – 31 | 1 – 32 | |
| Month | 1 – 12 | 1 – 13 | 1: January, 2: February, …, 12: December |
| Day of Week | 0 – 6 | 1 – 7 | 0: Sunday, 1: Monday, …, 6: Saturday |
To improve readability, you can also replace Month and Day of Week with an abbreviation of the desired value.
Month: JAN, FEB, MAR, APR, MAY, JUN, JUL, AUG, SEP, OCT, NOV, DECDay of Week: SUN, MON, TUE, WEB, THU, FRI, SAT@BindClass
+public final class Ping {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(Ping.class);
+
+ @Scheduled(fixedDelay = 5000, initialDelay = 10000)
+ public void doPingEveryFiveSecondsAfter10Seconds() {
+ LOGGER.info("PING!!");
+ }
+
+ @Scheduled(cron = "0 */5 * * * *")
+ public void doPingEveryFiveMinutesUTC() {
+ LOGGER.info("PING!!");
+ }
+
+ @Scheduled(cron = "0 0 0 * * mon", cronZone = "Europe/Paris")
+ public void doPingEveryMondayAtMidnightEuropeParis() {
+ LOGGER.info("PING!!");
+ }
+
+ @Scheduled(initialDelay = 1000)
+ public void doPingOnceAfter1Second() {
+ LOGGER.info("PING!!");
+ }
+}
+Getting Started
Core
Relational Databases
NoSQL Databases
Cache
Internationalization
REST Client
Scheduler
Sendmail
Template
Virtual File Storage
Web
Testing
Advanced
Getting Started
Core
Relational Databases
NoSQL Databases
Cache
Internationalization
REST Client
Scheduler
Sendmail
Template
Virtual File Storage
Web
Testing
Advanced
This implementation of the mailer uses the Apache Commons Email library. It allows you to easily send email via SMTP.
<dependency>
+ <groupId>dev.voidframework</groupId>
+ <artifactId>voidframework-sendmail-commonsemail</artifactId>
+ <version>1.16.0</version>
+</dependency>
+To enable this mailer, you have to set voidframework.sendmail.engine to dev.voidframework.sendmail.engine.ApacheCommonsEmailMailerEngine in your application configuration file.
The following configuration keys can be used in the configuration file of your application:
voidframework.sendmail.commonsemail.username user name for authentication. The default value is null.voidframework.sendmail.commonsemail.password password for authentication. The default value is null.This module also includes many of the configuration keys of the Java Mail properties. To use it, simply prefix them with voidframework.sendmail.commonsemail.. Here is the list of possible properties:
https://javaee.github.io/javamail/docs/api/javax/mail/internet/package-summary.html
mail.mime.address.usecanonicalhostnamemail.mime.allowencodedmessagesmail.mime.applefilenamesmail.mime.base64.ignoreerrorsmail.mime.charsetmail.mime.contenttypehandlermail.mime.decodefilenamemail.mime.decodeparametersmail.mime.decodetext.strictmail.mime.encodeeol.strictmail.mime.encodefilenamemail.mime.encodeparametersmail.mime.foldtextmail.mime.ignoremultipartencodingmail.mime.ignoreunknownencodingmail.mime.ignorewhitespacelinesmail.mime.multipart.allowemptymail.mime.multipart.ignoreexistingboundaryparametermail.mime.multipart.ignoremissingboundaryparametermail.mime.multipart.ignoremissingendboundarymail.mime.parameters.strictmail.mime.setcontenttypefilenamemail.mime.setdefaulttextcharsetmail.mime.uudecode.ignoreerrorsmail.mime.uudecode.ignoremissingbeginendmail.mime.windowsfilenameshttps://javaee.github.io/javamail/docs/api/com/sun/mail/smtp/package-summary.html
mail.smtp.usermail.smtp.hostmail.smtp.portmail.smtp.connectiontimeoutmail.smtp.timeoutmail.smtp.writetimeoutmail.smtp.frommail.smtp.localhostmail.smtp.localaddressmail.smtp.localportmail.smtp.ehlomail.smtp.authmail.smtp.auth.mechanismsmail.smtp.auth.login.disablemail.smtp.auth.plain.disablemail.smtp.auth.digest-md5.disablemail.smtp.auth.ntlm.disablemail.smtp.auth.ntlm.domainmail.smtp.auth.ntlm.flagsmail.smtp.auth.xoauth2.disablemail.smtp.submittermail.smtp.dsn.notifymail.smtp.dsn.retmail.smtp.allow8bitmimemail.smtp.sendpartialmail.smtp.sasl.enablemail.smtp.sasl.mechanismsmail.smtp.sasl.authorizationidmail.smtp.sasl.realmmail.smtp.sasl.usecanonicalhostnamemail.smtp.quitwaitmail.smtp.reportsuccessmail.smtp.socketFactorymail.smtp.socketFactory.classmail.smtp.socketFactory.fallbackmail.smtp.socketFactory.portmail.smtp.ssl.enablemail.smtp.ssl.checkserveridentitymail.smtp.ssl.trustmail.smtp.ssl.socketFactorymail.smtp.ssl.socketFactory.classmail.smtp.ssl.socketFactory.portmail.smtp.ssl.protocolsmail.smtp.ssl.ciphersuitesmail.smtp.starttls.enablemail.smtp.starttls.requiredmail.smtp.proxy.hostmail.smtp.proxy.portmail.smtp.proxy.usermail.smtp.proxy.passwordmail.smtp.socks.hostmail.smtp.socks.portmail.smtp.mailextensionmail.smtp.usersetmail.smtp.noop.strictGetting Started
Core
Relational Databases
NoSQL Databases
Cache
Internationalization
REST Client
Scheduler
Sendmail
Template
Virtual File Storage
Web
Testing
Advanced
This implementation is particularly useful locally during development. It simply displays the email in the console rather than sending a real email.
To enable this mailer, you have to set voidframework.sendmail.engine to dev.voidframework.sendmail.engine.DummyMailerEngine in your application configuration file.
Getting Started
Core
Relational Databases
NoSQL Databases
Cache
Internationalization
REST Client
Scheduler
Sendmail
Template
Virtual File Storage
Web
Testing
Advanced
If your application needs to send a few emails, you can easily do so via the voidframework-sendmail module. Sending emails is a not blocking operation, the emails are added to a queue that will be consumed asynchronously.
<dependency>
+ <groupId>dev.voidframework</groupId>
+ <artifactId>voidframework-sendmail</artifactId>
+ <version>1.16.0</version>
+</dependency>
+In addition to this basic module, you will need a mailer implementation. By default, the base module provides an implementation intended for local testing. For normal use, you can for example use the voidframework-sendmail-commonsemail module
The following configuration keys can be used in the configuration file of your application:
voidframework.sendmail.engine the mailer implementation to use. The default value is null.voidframework.sendmail.mailQueuePollTimeout the time to wait before giving up when retrieving a mail to be sent from the queue of mail waiting to be sent when the queue is empty. The default value is 2 seconds.voidframework.sendmail.gracefulStopTimeout the time for the daemon to shut down properly before it was terminated. The default value is 15 seconds.@BindClass
+public final class Service {
+
+ private final Sendmail sendmail;
+
+ @Inject
+ public Service(final Sendmail sendmail) {
+ this.sendmail = sendmail;
+ }
+
+ public void sample() {
+ final Mail mail = new Mail();
+ mail.setSubject("Hello World!");
+ mail.setBodyContentText("Hello World!");
+ mail.setFrom("charlie.root@local.domain", "Charlie Root");
+ mail.addRecipient("john@local.domain");
+
+ sendmail.send(mail);
+ }
+}
+Getting Started
Core
Relational Databases
NoSQL Databases
Cache
Internationalization
REST Client
Scheduler
Sendmail
Template
Virtual File Storage
Web
Testing
Advanced
To generate HTML document with FreeMarker, you have to use the module voidframework-template-freemarker.
<dependency>
+ <groupId>dev.voidframework</groupId>
+ <artifactId>voidframework-template-freemarker</artifactId>
+ <version>1.16.0</version>
+</dependency>
+By default, templates should be placed in the resources/views directory.
The following configuration keys can be used in the configuration file of your application:
voidframework.template.basePackagePath the location of the templates. The default value is "/views/".By default, modules web and template-freemarker provide a set of methods and variables that will be accessible in templates.
| Method | Description |
|---|---|
config | Retrieves a value from the configuration |
displaySize | Displays a formatted number with the correct unit (Kio, Mio, Gio, …) |
i18n / _ | Translates a message |
urlfor | Retrieves an URL from the router (reverse router) |
| Variable | Description |
|---|---|
isDevMode | Indicates whether the application is in dev mode |
lang | Contains the current language |
languages | Contains all available languages |
voidFrameworkVersion | Contains the current Void Framework version |
Getting Started
Core
Relational Databases
NoSQL Databases
Cache
Internationalization
REST Client
Scheduler
Sendmail
Template
Virtual File Storage
Web
Testing
Advanced
Getting Started
Core
Relational Databases
NoSQL Databases
Cache
Internationalization
REST Client
Scheduler
Sendmail
Template
Virtual File Storage
Web
Testing
Advanced
Sooner or later you will need to generate documents (HTML, PDF…). Void Framework provides all the tools you need!
public class TemplateExample {
+
+ private final TemplateRenderer templateRenderer;
+
+ @Inject
+ public TemplateExample(final TemplateRenderer templateRenderer) {
+
+ this.templateRenderer = templateRenderer;
+ }
+
+ public void renderTemplate() {
+
+ final Map<String, Object> dataModel = new HashMap<>();
+ dataModel.put("greating.msg", "Hello World!")
+
+ final String result = templateRenderer.render(
+ "renderWithDataModel.ftl",
+ Locale.ENGLISH,
+ dataModel);
+
+ System.out.println(result);
+ }
+}
+Getting Started
Core
Relational Databases
NoSQL Databases
Cache
Internationalization
REST Client
Scheduler
Sendmail
Template
Virtual File Storage
Web
Testing
Advanced
Getting Started
Core
Relational Databases
NoSQL Databases
Cache
Internationalization
REST Client
Scheduler
Sendmail
Template
Virtual File Storage
Web
Testing
Advanced
It is very important to test the code of an application. Void Framework provides tools for JUnit 5 to run your unit tests in a ready-to-use context as well as with support for Mockito annotations.
To enable this module, just add following lines to the pom.xml file of your project.
<dependency>
+ <groupId>dev.voidframework</groupId>
+ <artifactId>voidframework-test</artifactId>
+ <version>1.16.0</version>
+</dependency>
+The JUnit 5 extension will initialise the Void Framework application and initialise the variables annotated with Mockito annotations. It will also reset the mock between each test. To use it, add the @ExtendWith annotation with the value VoidFrameworkJUnitExtension.class.
In some situations, you may need to load additional modules for your application to work properly during the unit testing phase. For this, you can use the @ExtraGuiceModule annotation.
@ExtendWith(VoidFrameworkJUnitExtension.class)
+@ExtraGuiceModule({ExtraGuiceModule.class, AnotherExtraGuiceModule.class})
+final class MySimpleUnitTest {
+
+ @Mock
+ private MyRepository myRepository;
+
+ @InjectMock
+ @Spy
+ private MyService myService;
+
+ @Test
+ void myLittleTest() {
+ // Arrange
+ Mockito.when(myRepository.findAll()).thenReturn(Collections.emptyList());
+
+ // Act
+ myService.myMethod();
+
+ // Assert
+ Mockito.verify(myRepository, Mockito.times(1)).findAll();
+ }
+}
+Getting Started
Core
Relational Databases
NoSQL Databases
Cache
Internationalization
REST Client
Scheduler
Sendmail
Template
Virtual File Storage
Web
Testing
Advanced
Getting Started
Core
Relational Databases
NoSQL Databases
Cache
Internationalization
REST Client
Scheduler
Sendmail
Template
Virtual File Storage
Web
Testing
Advanced
When you need to store or retrieve files, you don’t necessarily want to deal with the complexity or you just want to hide it behind an easy to use interface. Void Framework exposes the VirtualFileStorage interface which allows you to use any storage engine very simply within your application.
To enable this module, just add following lines to the pom.xml file of your project.
<dependency>
+ <groupId>dev.voidframework</groupId>
+ <artifactId>voidframework-vfs</artifactId>
+ <version>1.16.0</version>
+</dependency>
+Different VirtualFileStorage implementations must be configured in voidframework.vfs namespace. The following configuration keys can be used.
voidframework.vfs.<name>.className the class name of the VFS implementation to use.voidframework.vfs.<name>.default indicate if the implementation is used by default and can be Guice injected without @Named annotation.
Example
voidframework {
+ vfs {
+
+ memory {
+ className = "dev.voidframework.vfs.engine.MemoryVirtualFileStorage"
+ default = true
+ }
+
+ disk1 {
+ className = "dev.voidframework.vfs.engine.DiskVirtualFileStorage"
+ default = false
+
+ basePath = /tmp/
+ }
+
+ disk2 {
+ className = "dev.voidframework.vfs.engine.DiskVirtualFileStorage"
+ default = false
+
+ basePath = /mount/netstorage/
+ }
+ }
+}
+@Singleton
+@WebController(prefixRoute = "/upload")
+public class UploadController {
+
+ private final VirtualFileStorage vfs;
+
+ @Inject
+ public UploadController(final VirtualFileStorage vfs) {
+
+ this.vfs = vfs;
+ }
+
+ @RequestRoute(method = HttpMethod.POST, name = "uploadFile")
+ public Result uploadFile(final Context context) {
+
+ final FormItem formItem = context.getRequest().getBodyContent().asFormData().getFirst("formFile");
+ if (formItem != null && formItem.fileSize() > 0) {
+ this.vfs.get().storeFile("upload", HttpContentTypes.APPLICATION_OCTET_STREAM, formItem.inputStream());
+ }
+
+ return Result.noContent();
+ }
+}
+Getting Started
Core
Relational Databases
NoSQL Databases
Cache
Internationalization
REST Client
Scheduler
Sendmail
Template
Virtual File Storage
Web
Testing
Advanced
Controllers receive incoming web requests, process them and return a result. They are the entry point to your web application. Controllers can return almost any type of data, as long as it is handled by the Result object. It is a convention imposed by Void Framework that methods which handle incoming requests must return a Result.
Example
@Singleton
+@WebController(prefixRoute = "/account")
+public class AccountController {
+
+ @RequestRoute(route = "/(?<accountId>[a-f0-9]+)")
+ public Result showAccount(@RequestPath("accountId") final String accountId) {
+ return Result.ok("Hello World");
+ }
+
+ @RequestRoute(method = HttpMethod.POST, route = "/{accountId}")
+ public Result updateAccount(@RequestPath("accountId") final String accountId,
+ @RequestBody final ProfileForm form,
+ final Context context) {
+ return Result.ok("Hello World");
+ }
+}
+A route consists of three elements: a context path, a prefix and a route itself. The first is configured in the application.conf file via the use of the voidframework.web.contextPath key (default value is /) while the other two are endpoint dependent.
A route is defined by two components, firstly @WebController defines the prefix that will be applied to all methods of the controller and by the @RequestRoute annotation providing various options. The latter tells the Void Framework that the method is an entry point.
This annotation is based on the @Controller annotation. It simply allows the class to be better specified and provides additional options. This annotation can only be used at class level.
The annotation accepts the following parameter:
prefixRoute a prefix to use on all endpoints in the class. By default, the value is empty.This annotation allows you to configure an entry point that will process an incoming request. This annotation can only be used at method level.
The annotation accepts the following parameters:
method the HTTP method. The default value is HttpMethod.GET.route the route. Regular expression with named capturing group or simplified variable (ie: {accountId}) can be used. The default value is /.name an alias name who can be used with the reverse routing. By default, the value is empty.To work, a controller will probably need some incoming data. To do this, the @RequestPath, @RequestVariable and @RequestBody annotations make it easy to retrieve information from the request. It is also possible to retrieve information from the Context itself.
This annotation allows you to extract parameters named in the URL path.
The annotation accepts the following parameter:
value the name of the parameter (correlated with the regexp used in the route definition) to extract.This annotation allows you to extract query string value (simple or array) from the URI.
The annotation accepts the following parameter:
value the name of the query string value to extract.fallback the value to use as fallback.This annotation allows you to retrieve the contents of the request (i.e. from a POST query) as a Java object. If something goes wrong, the extracted value will be null.
Example
@Singleton
+@WebController
+public class ExampleController {
+
+ @RequestRoute(route = "/")
+ public Result retrieveArrayFromQueryString(@RequestVariable("year") final int[] yearArray) {
+ return Result.ok(Yaml.toString(yearArray));
+ }
+
+ @RequestRoute(method = HttpMethod.POST, route = "/")
+ public Result retrieveListFromBody(@RequestBody("year") final List<Integer> yearList) {
+ return Result.ok(Yaml.toString(yearList));
+ }
+}
+It is sure that you will have to retrieve the parameters with their respective type (e.g. int, boolean, …). For this, Void Framework uses the converter mechanism to allow conversion from a String. By default, the following types are handled:
To handle a new type, you simply have to implement a new converter. For more information, read the Type conversion chapter.
The abstract class AbstractStaticAssetsController provides the methods needed to serve static files and webjars. It provides the reverse routing names static_file and static_webjar. Static files default folder can be changed via the voidframework.web.baseAssetResourcesDirectory configuration key.
@Singleton
+@WebController
+public class StaticAssetsController extends AbstractStaticAssetsController {
+
+ @Inject
+ public StaticAssetsController(final Config configuration) {
+ super(configuration);
+ }
+}
+<link rel="stylesheet" href="${urlfor('static_file', 'css/application.css')}">
+If you need to handle client-side translations via JavaScript, the abstract class AbstractJavaScriptInternationalizationController provides an endpoint to retrieve a helper that you can use directly on the client side. It provides the reverse routing name js_i18n.
@Singleton
+@WebController
+public final class JavaScriptInternationalizationController extends AbstractJavaScriptInternationalizationController {
+
+ @Inject
+ public JavaScriptInternationalizationController(final Internationalization internationalization) {
+
+ super(internationalization);
+
+ // Optional: only retrieve matching translations
+ this.filterKeyPatternList.add("footer.*");
+ }
+}
+<script src="${urlfor('js_i18n', 'fr')}"></script>
+
+<script>
+i18n.getMessage('msg.key');
+i18n.getMessage('msg.key', 'var');
+i18n.getMessage('msg.key', 'var1', 'var2', /*...*/'varn');
+</script>
+Getting Started
Core
Relational Databases
NoSQL Databases
Cache
Internationalization
REST Client
Scheduler
Sendmail
Template
Virtual File Storage
Web
Testing
Advanced
Filters are the middleware and are individual functions that make up the request processing pipeline.
The use of filters is done through the use of the @WithFiter annotation. It is possible to specify as many filters as you wish, the order in which they are declared representing the order in which they are used.
@FilterWith({FirstFilter.java, SecondFilter.java})
+@Singleton
+@WebController
+public class Controller {
+
+ @FilterWith({ThirdFilter.java})
+ @RequestRouting
+ public Result showHomePage() {
+ return Result.of("Hello World!");
+ }
+}
+In case the filter have to be enabled globally (it will be used for all requests), it must be declared in the application configuration file with the configuration key voidframework.web.globalFilters :
voidframework {
+ web {
+ globalFilters += "dev.voidframework.web.http.filter.csrf.CSRFFilter"
+ }
+}
+If you need to pass values which should not be passed through the session from one filter to another, or simply from a filter to a controller, you can use Context attributes. This is a simple way of passing values while maintaining their type.
@Singleton
+public class ExampleFilter implements Filter {
+
+ private static final TypedMap.Key<UUID> USER_ID = TypedMap.Key.of("USER_ID");
+
+ @Override
+ public Result apply(final Context context, final FilterChain filterChain) {
+ context.getAttributes().put(USER_ID, UUID.randomUUID());
+
+ return filterChain.applyNext(context);
+ }
+}
+Getting Started
Core
Relational Databases
NoSQL Databases
Cache
Internationalization
REST Client
Scheduler
Sendmail
Template
Virtual File Storage
Web
Testing
Advanced
Getting Started
Core
Relational Databases
NoSQL Databases
Cache
Internationalization
REST Client
Scheduler
Sendmail
Template
Virtual File Storage
Web
Testing
Advanced
The web module also integrates the template module with the Freemarker rendering engine by default. This allows controllers to return html content easily.
Although you can manually use the template engine, in most cases this would not be handy. However, you can use the TemplateResult class which will make it easier to use the renderer.
By default, the module voidframework-template-freemarker provide a set of methods and variables that will be accessible in templates.
| Method | Description |
|---|---|
config | Retrieves a value from the configuration |
displaySize | Displays a formatted number with the correct unit (Kio, Mio, Gio, …) |
i18n / _ | Translates a message |
urlfor | Retrieves an URL from the router (reverse router) |
| Variable | Description |
|---|---|
flash | Contains temporary messages |
session | Contains the current session data |
isDevMode | Indicates whether the application is in dev mode |
lang | Contains the current language |
languages | Contains all available languages |
csrfToken | Contains the CSRF token |
voidFrameworkVersion | Contains the current Void Framework version |
Templates should be placed in the resources/views directory.
If you need to include static files in your HTML page (e.g. css, javascript, image, …), you can use the urlfor method with the first parameter static_file or static_webjar and the second parameter the path to the file.
controller/HomeController.java
@Singleton
+@WebController
+public class HomeController {
+
+ @RequestRouting
+ public Result homepage(final Context ctx) {
+ return Result.ok(
+ TemplateResult.of("homepage.ftl", Map.of("greating", "Hello World")));
+ }
+}
+resources/views/homepage.ftl
<!DOCTYPE html>
+<html lang="${lang!}">
+ <body>
+ <h1>${greating}</h1>
+ <img src="${urlfor('static_file', 'img/logo.png')}" alt="image"/>
+ </body>
+</html>
+Getting Started
Core
Relational Databases
NoSQL Databases
Cache
Internationalization
REST Client
Scheduler
Sendmail
Template
Virtual File Storage
Web
Testing
Advanced
Cross-site request forgery (CSRF), also known as one-click attack or session riding, is a type of malicious vector attack of a website where unauthorized commands are submitted from a user that the web application trusts.
Void Framework provides a CSRF filter that can be applied globally to all requests or only on specific endpoints.
The following configuration keys can be used in the configuration file of your application.
voidframework.web.csrf.tokenName the token name (used to retrieve token from Body or QueryString). The default value is csrfToken.voidframework.web.csrf.cookieName the name of the cookie containing the current CSRF. The default value is VOID_CSRF.voidframework.web.csrf.cookieHttpOnly is the cookie only be accessed via HTTP? The default value is true.voidframework.web.csrf.cookieSecure is the cookie secured? If true, sent only for HTTPS requests. The default value is false.voidframework.web.csrf.signatureKey the key used to digitally sign the CSRF token. The default value is ${voidframework.web.session.signatureKey}.voidframework.web.csrf.timeToLive the CSRF token TTL. The default value is 15 minutes.The activation of the CSRF filter is done via the configuration key voidframework.web.globalFilters. Read more about Filters.
voidframework {
+
+ web {
+ globalFilters += "dev.voidframework.web.http.filter.csrf.CSRFFilter"
+ }
+}
+Although you have enabled the CSRF filter, you may need to disable CSRF protection on a specific endpoint. To do this, you can use the @NoCSRF annotation which will indicate that the CSRF token does not need to be checked.
Getting Started
Core
Relational Databases
NoSQL Databases
Cache
Internationalization
REST Client
Scheduler
Sendmail
Template
Virtual File Storage
Web
Testing
Advanced
Void Framework provides a basic filter to add certain headers to HTTP responses by default. These headers allow you to add an extra level of security to your application.
The following configuration keys can be used in the configuration file of your application.
voidframework.web.securityHeaders.contentTypeOptions the value for the header “X-Content-Type-Options”. The default value is nosniff.voidframework.web.securityHeaders.frameOptions the value for the header “X-Frame-Options”. The default value is DENY.voidframework.web.securityHeaders.xssProtection the value for the header “X-XSS-Protection”. The default value is 1; mode=block.voidframework.web.securityHeaders.crossOriginResourcePolicy the value for the header “Cross-Origin-Resource-Policy”. The default value is same-origin.voidframework.web.securityHeaders.contentSecurityPolicy the value for the header “Content-Security-Policy”. The default value is default-src 'self'.null. The header will then not be sent.The activation of the CSRF filter is done via the configuration key voidframework.web.globalFilters. Read more about Filters.
voidframework {
+
+ web {
+ globalFilters += "dev.voidframework.web.http.filter.security.SecurityHeadersFilter"
+ }
+}
+Getting Started
Core
Relational Databases
NoSQL Databases
Cache
Internationalization
REST Client
Scheduler
Sendmail
Template
Virtual File Storage
Web
Testing
Advanced
If you need to store data between HTTP requests, you can store it in the session. The data stored in the session is available throughout the user’s session.
The following configuration keys can be used in the configuration file of your application.
voidframework.web.session.cookieName the name of the cookie containing the current session. The default value is VOID_SESS.voidframework.web.session.cookieHttpOnly is the cookie only be accessed via HTTP? The default value is true.voidframework.web.session.cookieSecure is the cookie secured? If true, sent only for HTTPS requests. The default value is false.voidframework.web.session.signatureKey the key used to digitally sign the session content.voidframework.web.session.timeToLive the session TTL. The default value is 7 days.You can access the session data via the Context. Methods get or getOrDefault can be used.
@Singleton
+@WebController
+public class Controller {
+
+ @RequestRouting
+ public Result sessionExample(final Context ctx) {
+ final String userId = ctx.getSession().get("USER_ID");
+ return Result.of(userId);
+ }
+}
+To store value(s), methods put, putAll and putIfAbsent can be used.
@Singleton
+@WebController
+public class Controller {
+
+ @RequestRouting
+ public Result sessionExample(final Context ctx) {
+ final String userId = ctx.getSession().put("USER_ID", "e90b88d4-3c15");
+ return Result.of(userId);
+ }
+}
+Getting Started
Core
Relational Databases
NoSQL Databases
Cache
Internationalization
REST Client
Scheduler
Sendmail
Template
Virtual File Storage
Web
Testing
Advanced
Does your application need to have web entry points (ie: http(s) or websocket) to provide an API or web pages? The voidframework-web module will best meet your expectations.
To enable this module, just add following lines to the pom.xml file of your project.
<dependency>
+ <groupId>dev.voidframework</groupId>
+ <artifactId>voidframework-web</artifactId>
+ <version>1.16.0</version>
+</dependency>
+The following configuration keys can be used.
Base
voidframework.web.gracefulStopTimeout the time (duration or milliseconds) for the web daemon to shut down properly. The default value is 15 seconds.voidframework.web.errorHandler the implementation of the error handler to use in case of http error. The default value is dev.voidframework.web.http.errorhandler.DefaultErrorHandler.voidframework.web.contextPath the default context path root. The default value is /.voidframework.web.routes a list of routing definition classes (must implement the interface AppRoutesDefinition). The default value is [].voidframework.web.globalFilters a list of routing global filter classes (must implement the interface Filter). The default value is [].voidframework.web.baseAssetResourcesDirectory the default location of static files in the “resources” directory. The default value is static.Server
voidframework.web.server.idleTimeout the time without any request to wait before the connection is closed. The default value is 30 seconds.voidframework.web.server.extraWebServerConfiguration the implementation of the interface ExtraWebServerConfiguration to apply a custom configuration to the Undertow web server. The default value is null.voidframework.web.server.ioThreads the number of I/O threads. If not specified (value = null or 0), Math.max(Runtime.getRuntime().availableProcessors(), 2) value will be used. The default value is null.voidframework.web.server.workerThreads the number of Worker threads. If not specified (value = null or 0), ioThreadsNumber * 8 value will be used. The default value is null.voidframework.web.server.maxBodySize the max body content size. The default value is 1 MiB.voidframework.web.server.fileSizeThreshold the maximum size allowed in memory before the uploaded file was stored on the disk rather than in memory. The default value is 256 KiB.voidframework.web.server.tempFileLocation the location where temporary files will be stored. The default value is null (default Java temporary folder).HTTP
voidframework.web.server.http.listenHost the interface on which the server will listen for non-secure HTTP requests. The default value is 127.0.0.1.voidframework.web.server.http.listenPort the port on which the server will listen for non-secure HTTP requests. The default value is 9000.HTTPS
voidframework.web.server.https.listenHost the interface on which the server will listen for HTTPS requests. The default value is 127.0.0.1.voidframework.web.server.https.listenPort the port on which the server will listen for HTTPS requests. The default value is 9001.voidframework.web.server.https.ssl.protocols the protocols to use. The default value is ["TLSv1.2", "TLSv1.3"].voidframework.web.server.https.ssl.ciphers the allowed ciphers. If list is empty, all ciphers will be allowed. The default value is [].voidframework.web.server.https.ssl.keyStorePath the path where is located the key store. Could be a “resources” path, a URL or a simple path. The default value is null.voidframework.web.server.https.ssl.keyStoreType the type of the key store. The default value is JKS.voidframework.web.server.https.ssl.keyStorePassword the password to use to open the key store. The default value is null.voidframework.web.server.https.ssl.keyAlias the alias of the key to use. If “null” the key will be choosen automatically. The default value is null.voidframework.web.server.https.ssl.keyPassword the key password. The default value is ${voidframework.web.server.https.ssl.keyStorePassword}.Language
voidframework.web.language.availableLanguages the available languages. The default value is ["en", "fr"].voidframework.web.language.cookieName the name of the cookie containing the current language. The default value is VOID_LANG.voidframework.web.language.cookieHttpOnly if the cookie can only be accessed via HTTP. The default value is false.voidframework.web.language.cookieSecure if the cookie must be secured, if true, the cookie will be only sent via HTTPS. The default value is false.Flash messages
voidframework.web.flashMessages.cookieName the name of the cookie containing the current flash messages. The default value is VOID_FLASH.voidframework.web.flashMessages.cookieHttpOnly if the cookie can only be accessed via HTTP. The default value is false.voidframework.web.flashMessages.cookieSecure if the cookie must be secured, if true, the cookie will be only sent via HTTPS. The default value is false.Session
voidframework.web.session.cookieName the name of the cookie containing the current session. The default value is VOID_SESS.voidframework.web.session.cookieHttpOnly is the cookie only be accessed via HTTP? The default value is true.voidframework.web.session.cookieSecure is the cookie secured? If true, sent only for HTTPS requests. The default value is false.voidframework.web.session.signatureKey the key used to digitally sign the session content.voidframework.web.session.timeToLive the session TTL. The default value is 7 days.CSRF
voidframework.web.csrf.tokenName the token name (used to retrieve token from Body or QueryString). The default value is csrfToken.voidframework.web.csrf.cookieName the name of the cookie containing the current CSRF. The default value is VOID_CSRF.voidframework.web.csrf.cookieHttpOnly is the cookie only be accessed via HTTP? The default value is true.voidframework.web.csrf.cookieSecure is the cookie secured? If true, sent only for HTTPS requests. The default value is false.voidframework.web.csrf.signatureKey the key used to digitally sign the CSRF token. The default value is ${voidframework.web.session.signatureKey}.voidframework.web.csrf.timeToLive the CSRF token TTL. The default value is 15 minutes.Security headers
voidframework.web.securityHeaders.contentTypeOptions the value for the header “X-Content-Type-Options”. The default value is nosniff.voidframework.web.securityHeaders.frameOptions the value for the header “X-Frame-Options”. The default value is DENY.voidframework.web.securityHeaders.xssProtection the value for the header “X-XSS-Protection”. The default value is 1; mode=block.voidframework.web.securityHeaders.crossOriginResourcePolicy the value for the header “Cross-Origin-Resource-Policy”. The default value is same-origin.voidframework.web.securityHeaders.contentSecurityPolicy the value for the header “Content-Security-Policy”. The default value is default-src 'self'.+2024-09-17
| Security | Upgrade undertow version to 2.3.17.Final (CVE-2024-7885) |
| Improved | Replace deprecated Bucket4J usage with new API |
| Improved | Upgrade dependencies |
+2024-06-27
| Security | Upgrade undertow version to 2.3.14.Final (CVE-2024-6162) |
| Improved | Upgrade dependencies |
+2024-05-01
| Security | Upgrade guava version to 32.0.1 (CVE-2023-2976 & CVE-2020-8908) |
| Fixed | Force close BlockingHttpExchange |
| Improved | Upgrade dependencies |
+2024-03-10
| New | Simplified URL variable |
| Improved | Upgrade dependencies |
+2024-01-02
| New | Add ClassMethodUtils class |
| Fixed | Constants PARENTHESIS_OPEN and PARENTHESIS_CLOSE are no longer inverted |
| Improved | Error page (dev mode): display filename & partial content |
| Improved | Upgrade dependencies |
+2023-12-05
| Security | Upgrade logback-classic version to 1.4.14 (CVE-2023-6378) |
+2023-11-23
| Fixed | Fix SQLDialect detection KO (jOOQ) |
| Fixed | Fix can't override Hibernate dialect |
+2023-10-19
| New | Add KryoUtils class |
| New | Add new method ClassResolverUtils::isClassAvailable |
| New | Add new Kryo serializers |
| New | Add MockitoUtils class (spy lambda) |
| Fixed | Application must shutdown on "start" lifecycle failure |
| Fixed | H2 web console fail to start because password not encoded |
| Improved | AbstractCacheEngine now using KryoUtils |
| Improved | Add TypedMap static "constructor" methods |
| Improved | Redis ACL authentication (username/password) |
| Improved | Upgrade dependencies |
+2023-09-11
| New | REST Client authentication (API Key, Basic, Bearer) |
| Improved | New constant values: CharConstants.EXCLAMATION_MARK, CharConstants.PIPE, StringConstants.EXCLAMATION_MARK and StringConstants.PIPE |
| Improved | Upgrade dependencies |
+2023-07-13
| New | AspectJ |
| New | REST client |
| New | Bucket4J (rate-limiting) |
| New | Add ConfigurationUtils class |
| Improved | Upgrade dependencies |
| Improved | New constant value: CharConstants.TAB and StringConstants.TAB |
+2023-16-16
| Improved | Add exception AppLauncherException.ModuleConstructorNotFound |
+2023-05-18
| New | Proxyable interface |
| New | H2 web console |
| Fixed | Guice module implementing interface not recognized |
| Improved | Bind automatically interface with single implementation |
| Improved | Updated version of all dependencies |
+2023-04-12
| Security | Upgrade Undertow version to 2.3.5 (CVE-2022-4492) |
| New | Conditional feature RunInDevModeConditionalFeature |
| New | Sendmail module |
| New | Sendmail module implementation Apache Commons Email |
| New | Add @CachePut annotation |
| Fixed | Transactional annotation multiple calls |
| Improved | Javadoc |
| Improved | HttpClient is now used instead of raw URL connection (Remote Configuration) |
| Improved | Result: method badRequest(final String content, final String contentType) |
| Improved | Updated version of all dependencies |
+2023-02-24
| Fixed | HTTP webserver graceful shutdown not honored |
+2023-02-11
| New | Web module can now respond to requests securely via HTTPS |
| New | NotInstance validation constraint |
| New | Utility class DurationUtils |
| Improved | Graceful stop timeout is now a duration (with time unit) |
| Improved | Commercial at "@" constant now available in CharConstants and StringConstants classes |
| Improved | Annotation RequestVariable can now use fallback value |
| Improved | Updated version of all dependencies |
+2023-01-07
| Fixed | JavaScript i18n reverse routing name (js_i18n) |
+2023-01-02
| New | CUID validator |
| New | Internationalization for JavaScript |
| New | Conditional feature |
| Improved | Http Result methods now accept charset |
| Improved | Rename bindable annotation from @BindClass to @Bindable |
| Improved | Clean thread data (Transactional) |
| Improved | Updated version of all dependencies |
+2022-12-05
| New | Guice module prioritisation |
| New | jOOQ module |
| New | Web context path is now customizable (voidframework.web.contextPath) |
| Improved | Deserialization: Unknown enum value to null |
| Improved | Scheduler "run once" mode |
| Improved | Static files/webjars controller: add route name (static_file and static_webjar) |
| Improved | Updated version of all dependencies |
+2022-11-14
| Fixed | NullPointerException when requested config is null on FreeMarker |
+2022-11-07
| Fixed | NullPointerException if header Accept-Language not provided |
+2022-11-07
| Security | Upgrade Jackson Databind version to 2.14.0 (CVE-2022-42003) |
| New | WebSocket support |
| New | Can configure temporary files location (voidframework.web.server.tempFileLocation) |
| New | Mockito annotation support |
| New | CUID: a Collision-resistant Unique ID |
| New | Add HttpHeaderNames class |
| New | Virtual File Storage |
| New | No HTTP request timeout (voidframework.web.server.idleTimeout) |
| Fixed | The abbreviation of "december" is now correct (scheduler) |
| Fixed | "Day of Week" and "Month" can't be zero (Scheduler) |
| Fixed | Redis methods addInList, getFromList and decrement now working correctly |
| Fixed | Force close all InputStream of uploaded files |
| Fixed | Can use template-freemarker without web |
| Improved | Configuration Key/Value from CLI JVM properties (-D flag) |
| Improved | Annotation @RequestVariable can now retrieve array |
| Improved | Move cache annotations to subpackage annotation |
| Improved | Use wrap/unwrap on Memory cache engine |
| Improved | Updated version of all dependencies |
| Improved | Redis parameter connectionTimeout is now a duration |
| Improved | Rename all utility classes |
| Improved | Add new redirection methods on Result |
| Improved | HttpRequestBodyContent::asRaw is now an InputStream |
| Improved | Redis::addInList now return the current list size |
| Improved | ValidationError is now a record |
+2022-09-15
| Fixed | The "Modules loaded" log is displayed too early |
| Fixed | Remove unused "GlobalFilterDefinition" interface |
| Improved | Add converter String to Locale (Web). Used to extract Locale from incoming request (route path or query parameter) |
+2022-09-05
| Security | Upgrade Undertow version to 2.2.19 (CVE-2022-2053) |
| New | Add SecurityHeadersFilter |
| New | Add unit test JUnit extension and annotation |
| New | Add ability to fetch remote configuration provider & "etcd" remote configuration provider |
| New | Add "http" remote configuration provider |
| New | Add CharConstants & StringConstants classes |
| Improved | Force EntityManagerFactory initialization during boot sequence |
| Improved | Add new Result methods to handle empty content easily |
| Improved | Validation constraints can be grouped |
| Improved | Clean code |
| Improved | Better boolean support (Freemarker / config) |
| Improved | Refactoring module "web" (move files, new constant values, unit tests) |
| Improved | Updated version of all dependencies |
+2022-08-02
| Fixed | EntityManagerFactory don't find models on multiple-jars project |
+2022-07-24
| Fixed | Error 500 CSRF when POST request render template |
| Improved | Change CSRF signature algorithm to HMACSHA256 |
+2022-07-22
| New | Methods on Reflection util class: getAnnotatedField(Object, Class) and getAnnotatedField(Class, Class) |
| New | Configuration keys to define number of I/O and Worker threads (Web) |
| New | Redis (base + cache + healthcheck) |
| New | CSRF token |
| Fixed | Cookie revocation not working |
| Fixed | C3P0/HikariCP settings |
| Fixed | Issue on routes sorting when regex and capture regex are used |
| Fixed | Assets not found when devMode=false |
| Fixed | Hibernate : Models not found when application is compiled |
| Improved | No longer necessary for HealthChecker to be Singleton |
| Improved | Add cache abstraction helper |
| Improved | Session signature key configuration is now required |
| Improved | Rename ApplicationLauncher to VoidApplication |
| Improved | Updated version of all dependencies |
+2022-07-08
| New | Ability to retrieve XML body content as Java object |
| Fixed | Removed I18N reference from Hibernate module |
| Fixed | Transactional annotation : rollback not occur on unchecked exception |
| Fixed | Routes are randomly sorted |
| Improved | Cache management : rename the annotation Cache to CacheResult and add CacheRemove |
+2022-07-02
Initial release