Quarkus-powered Java Web Application Framework
Current version : 0.1.0. A preview release made for the Quarkus Hackathon. You can use it as a showcase of what Weblith could be, but at this time is still too experimental to start a production target project.
Quarkus Hackathon category winner !! Follow us on twitter for more updates about this projet.
- A little bit of history
- Routes & Results
- Request Context
- Forms Management
- Freemarker Templates
- Fomantic UI library
- TL;DR HOW TO USE IT
- Authentication
- Roadmap
At its early stage, Weblith.io is an internal framework developed at Zileo.net derived from other frameworks like Spring Boot, Ninja Framework, MangooIO. The goal is to provide more . Technically, it is based on stable well-known libraries : Google Guice, Lightbend Config, Undertow, Hibernate ORM, Hibernate Validator, Jackson, Freemarker... Q3-2020 the plan was open source it, by creating the missing documentation and archetype, and make it publicly available through Weblith.io.
Weblith has been made to offer a quick and easy way to create Server Side Rendered web applications, focusing on developer joy by opinionated decisions allowing to minimize the developer tools needed, reduce boiler plate code, and even offer nice UI components. It uses stable and well-know libraries to avoid a long learning curve. It will produces one single Jar file with an embedded HTTP server that can run with few resources. Not that it doesn't reinvent the monolithic applications ; depending on the size of your project you should still use micro or external services when it makes sense, even with a front end application made using Weblith.
Following news on the Java battlefield, it was very interesting to see Quakus and Micronaut emerge. One problem we still wanted to fix with Weblith was the startup time (mainly slowed by Guice and Hibernate). With the Quarkus Global Hackathon launched, there was a good opportunity to try migrating some Weblith layers to Quarkus.
Important Note By this time (July 2020), you are reading a condensed documentation of a Preview Release, that have been succesfully set up for the Hackathon. If you're interested in this projet, see the Roadmap chapter for more information. Please note that - as requested by the Hackathon rules - no update or issue will be adressed before the end of the Hackathon.
On a purely Quarkus point of view, Weblith works along with quarkus-resteasy
. It will automatically register JAX-RS endpoints having to GET
and POST
resources. It also means that you can mix Reasteasy code with Weblith.
To declare new routes for your application, you only need to know three annotations : @Controller
, @Get
and @Post
. Each annotation can have a value to represent the corresponding path
, or it will be taken automaticaly from the class name (minus a Controller
suffix) or method name.
@Controller
class MyFirstController {
@Get
public Response myPage() { ... }
@Get
public Response myPage2(@PathParam String id) { ... }
@Post
public Response myAction() { ... }
}
Will be recognized as /MyFirst/myPage
, /MyFirst/myPage2/{id}
, (GET) and /MyFirst/myAction
(POST). Response
is the standard JAX-RS returned object ; you'll see later that Weblith will help you on that part too.
@Controller("/Controller")
class MySecondController {
@Get("/page")
public Response myPage() { ... }
@Get("/page2/{id}")
public Response myPage2(@PathParam("id") String identifier) { ... }
@Post("/action")
public Response myAction() { ... }
}
Will be recognized as /Controller/page
, /Controller/page/{id}
(GET) and /MyFirst/action
(POST).
There is no specific annotation for managing parameters of your routes. As you may have noticed on the previous examples, you can use the standard JAX-RS ones : @PathParam
, @QueryParam
, @HeaderParam
, @MatrixParam
, @CookieParam
and @FormParam
. Be sure to import the ones from org.jboss.resteasy.annotations.jaxrs
package so that the name
of each one is optional (taken from the method parameter name by default). @DefaultValue
is also available.
While you can configure yourself your JAR-RS Response
objects, Weblith offers an handy way to manage the results of your routes via different Result
objects :
TextResult
to render plain textRawResult
to render bytesStreamResult
to render aFile
or anURL
JsonResult
to render some JSONHtmlResult
will be addressed in the Freemarker Templates chapter
Result classes have some shortcut methods to configure the future response, like adding Cookies and changing the charset (UTF-8
by default). You can also define your own custom Result
classes if needed.
@Get
public Result myPage() {
return new TextResult("Hello World").charset(StandardCharsets.UTF_8);
}
One last very import one is the Redirect
result. All your POST actions will be CSRF-protected (unless marked with @NotCsrfProtected). So GET methods should never be used to modify things ; and successful POST actions should never directly render results, but rather redirect to a GET method, with some optional information. The Redirect
result will help you this way.
@Post
public Result doIt() {
return new Redirect("/Test/myPage").withSuccess("You did it !");
}
While in a controller or any other internal service, you can inject a RequestContext
instance. This class is a center piece for all request related concepts, and give you access to :
SessionScope
: a client-side cookie-based session (server-side session not supporter by Weblith)FlashScope
: a specific cookie-based scope that keep data from one request to an otherLocaleHandler
: a handler that can detect the current user-defined localeseed()
andget
: a map for storing some objects during the whole request executionHttpRequest
andUriInfo
: JAX-RS request information
While you can use the standard @Form
JAX-RS way for getting form information, and the @Valid
to throws ConstraintViolationException
, Weblith offers a nicer way to manage your forms. Simply embed your form object in a Form
parameter like this :
@Transactional
@Post
public Result save(Form<SimpleEntity> form) {
SimpleEntity dto = form.getValue();
... map dto field to real entity instance
... save entity
return new Redirect("/SimpleEntity/list");
}
The SimpleEntity
object will be filled with your submitted data. It uses a Jackson ObjectMapper
underneath that maps a Map
of all parameters to the required object.
Form
will allow you to trigger the validation of the filled object. All validation (but also parameter conversion) error messages will then be available as a list of Violation
objects. Welbith UI components will handle those messages.
@Transactional
@Post
public Result save(Form<SimpleEntity> form) {
if (!form.validate()) {
return formTemplate.render(form);
}
...
}
During our migration to Quarkus, we tried to reimplement our Freemarker components with Qute, the template engine proposed but the Quarkus team. But it was lacking some features needed for it. As the components base was already well-defined and stable, the choice have been made to continue using Freemarker. The downsides will be the lack of reactive support (but not intended for Weblith) and the native build support that seems hard to get working.
Simply return a HtmlResult
to render Freemarker template. This result must point to a template name, and a directory (or the controller name, by default).
@Get("/")
public HtmlResult home() {
return new HtmlResult("Main", "home").render("MyData","MyValue");
}
This will render the template templates/Main/home.ftlh
(stored in the classpath, so in src/main/resources
), with a MyData
available variable. Some default variables are always added by Weblith :
hostname
,requesPath
andparams
: relative to the current requestcontextPath
: value ofquarkus.http.root-path
lang
: current used defined languageflash
: map with current Flash scope data
Refer to the Freemarker documentation for more information about how to use this engine.
A better way to use templates is to inject instances of FreemarkerTemplate
in the controllers. This way, template paths are checked at build-time.
@Controller
public class SimpleEntityController {
@Inject
FreemarkerTemplate list;
@Get
public HtmlResult list() {
return list.render("SimpleEntities", SimpleEntity.listAll());
}
}
The path of the template will automatically set to templates/SimpleEntityController/list.ftlh
. It can be changed by using the @TemplatePath
annotation.
quarkus.locales
can be set to the locales supported by the application, and quarkus.default-locale
to the default one. Then messages.properties
files can be configure in a i18n
directory (stored in the classpath, so in src/main/resources
). A Weblith LocaleHandler
will be used to detect the right language to use, but can be switched by passing a lang
parameter to any request (can be changed via quarkus.weblith.switchLanguageParam
).
While a Messages
interface can be injected in any controller of internal service, two methods are also available in the templates :
i18n(key,param1,param2,...)
: translates the given keyprettyTime(date)
: displays date relatively
One third Quarkus extension, that will be your real time-saver for building web applications, is a set of Freemarker tags that will allow you to quickly have a nice UI that fits with your Weblith backend code. The archetype you can use to boostrap your application will even offer a primary structure for your own base templates.
Sadly, this documentation draft do not enter into each component details (too early). But take a quick look at the two following example, as they are pretty clear on what they will produce.
<@page.app title='User list' selectedIcon='users'>
<@layout.title />
<@t.table rows=Users align='left' noButtons=true>
<@t.column name='role' title='Role' align='center' />
<@t.column name='title' title='Title' />
<@t.column name='email' title='E-mail' interpret='<@layout.mailto value />' />
</@t.table>
</@page.app>
<@f.form 'SimpleEntity' SimpleEntity.id>
<@f.text name='name' label='Name' required=true />
<@f.text name='quantity' label='Quantity' type='number' />
<@f.calendar name='date' label='Date' />
<@f.buttonBar>
<@f.cancel />
<@f.submit />
</@>
</@>
A Maven archetype exists, so that you can easily bootstrap a Weblith showcase application. Run this method :
mvn archetype:generate \
-DarchetypeGroupId=net.zileo \
-DarchetypeArtifactId=weblith-archetype \
-DarchetypeVersion=0.1.0 \
-Dversion=1.0.0-SNAPSHOT \
-DgroupId=org.acme \
-DartifactId=my-weblith
Then you should be able to cd my-weblith
and run mvn compile quarkus:dev
.
The archetype immediately work with quarkus-security-jpa
. A set of different User with roles will be created at startup and be available for login through the FormAuthenticationMechanism
provided by Quarkus. Weblith only supplies the corresponding front end paths and login page. It should also integrates well with other security extensions available for Quarkus ; you'll find in the archetype application.properties
an example block defining an OIDC connection to Auth0.
Thats means you can protect your pages via properties configuration or @RolesAllowed
annotations, like any other Quakrus application. The archetype provide a showcase page for this part. Getting back relevant SecurityIdentify
information must still be analyzed.
As said before, the current status of the project is a Preview Release. Several existing code parts of the original Weblith project must still be migrated.
- Extensive documentation : Each Weblith concept should be documented appart with more details and configuration properties.
- UI components documentation : The goal is to analyze the relavance of
Storybook
- allowing serverside components since last version - for showcasing all Weblith UI components. - Router : In the original project, there is a central Router allowing for example reverse routing in templates.
- Multitenancy : domain management + dynamic tenant configuration
- Unit tests : a lot of unit tests must still be reused