Introducing Objectos PetClinic v001! (Part 1)

I'm happy to announce the release of Objectos PetClinic v001! Objectos Petclinic is a web application written entirely in Java showcasing:
-
Objectos Way, a library for writing web applications in Java; and
-
Objectos MK, a collection of Makefiles to build Java projects using Make.
This is the first release in a planned series of releases and, while still minimal in functionality, there's already a lot to explore. So much so that I divided this article into two parts.
Let's start.
Objectos PetClinic is inspired by the Spring PetClinic Application.
Write HTML using pure Java
In an Objectos Way web application you write your UI code in pure Java. Here's the Java code that renders the sidebar of the PetClinic:
private Html.Instruction.OfElement sidebar() { return div( className("sticky top-0px w-240px h-screen shrink-0 border-r border-r-border px-16px"), className("body-compact-01"), div( className("flex items-center py-20px px-16px"), raw(UiIcon.LOGO.value), span( className("pl-8px"), text("Objectos PetClinic") ) ), nav( className("pt-16px"), dataFrame("sidebar", pageSidebar.name()), renderFragment(this::sidebarItems) ) );}
It is a regular Java method.
Notice at the bottom, inside the nav
tag, the renderFragment
method invocation.
The argument is a regular Java method reference.
Here its implementation:
private void sidebarItems() { for (UiSidebar item : UiSidebar.VALUES) { a( className("flex items-center px-16px py-8px hover:bg-background-hover"), item == pageSidebar ? className("bg-background-selected") : noop(), href(item.href), div( className("pl-2px"), raw(item.icon) ), div( className("pl-10px"), text(item.title) ) ); }}
It generates the sidebar links based on the values of the UiSidebar
enum.
Since it is a regular Java method, we can use a for loop:
for (UiSidebar item : UiSidebar.VALUES) { // render the `a` tag}
And we can use a regular Java expression to conditionally style a link as the currently selected:
item == pageSidebar ? className("bg-background-selected") : noop()
In short, you can use all of the regular Java expressions and statements to define your application UI.
Style using Tailwind-like utility classes
Objectos Way ships with a CSS generator that works similarly to Tailwind CSS. The difference is that it is implemented in pure Java and you configure it using Java code.
The HTML code in the previous section has a number of className
invocations of the form:
// an exampleclassName("sticky top-0px w-240px h-screen shrink-0 border-r border-r-border px-16px")// another exampleclassName("flex items-center py-20px px-16px")
The className
instruction generates a HTML class
attribute.
So the following Java code:
div( className("flex items-center py-20px px-16px"))
Generates the following HTML:
<div ="flex items-center py-20px px-16px"></div>
It also generates the following CSS:
.flex { display: flex }
.items-center { align-items: center }
.px-16px { padding-left: 1rem; padding-right: 1rem }
.py-20px { padding-top: 1.25rem; padding-bottom: 1.25rem }
How the Objectos CSS generator works is beyond the scope of this blog post. In the Objectos PetClinic the generation occurs in a regular HTTP handler:
private void get(Http.Exchange http) { Css.StyleSheet s; s = generateStyleSheet(); http.ok(s);}
And you configure the generation using plain Java code. For example, here's how we configure the color values in the PetClinic:
private Css.Option extendColors() { return Css.extendColors(""" background: var(--ui-background) background-hover: var(--ui-background-hover) background-selected: var(--ui-background-selected) border: var(--ui-border) text: var(--ui-text) text-secondary: var(--ui-text-secondary) """);}
So, apart from the standard color values such as lime-600
and neutral-500
, we can use:
div(className("bg-background"))
Which generates the following CSS rule:
.bg-background { background-color: var(--ui-background) }
In short, Objectos Way allows you to create the UI of a web application using pure Java.
Server-Side rendered. Single-page application feel
An Objectos Way application renders all of its pages on the server. But navigation inside the web application does not cause a full-page reload; the developer defines which parts of the DOM will be updated instead. It allows for a smooth and possibly faster transition between pages.
Organize your HTML layout using the data-frame
attribute. The Objectos Way JS library will handle the rest for you.
Here's the Java/HTML code of the sidebar of the PetClinic application:
nav( ... dataFrame("sidebar", pageSidebar.name()), ...)
The nav
element declares a data-frame
attribute.
When you are at the home page of the application the nav
element is rendered with the following value:
nav( ... dataFrame("sidebar", "HOME"), ...)
And when you are at the owners page of the application, it is rendered with the following:
nav( ... dataFrame("sidebar", "OWNERS"), ...)
We are saying that, when a user navigates from the home page to the owners page (or vice-versa),
the browser should replace the nav
contents from the old page with the contents of the new page because:
-
the frames have the same name:
sidebar
; but -
the values of the frame are different: in one it is
HOME
and in the other it isOWNERS
.
It all happens transparently. In other words, we did not write any Javascript code for it to happen.
Static files are citizens as well
A typical web application needs to serve static files like images, web fonts, Javascript files and so on.
Objectos Way provides the Web.Resources
facility for this purpose.
A Web.Resources
instance is mapped to a temporary directory in the server's file system.
Here's how the Objectos PetClinic uses it.
First, the application creates an instance of Web.Resources
during its bootstrap:
return Web.Resources.create(config -> { config.noteSink(noteSink); config.contentTypes(""" .js: text/javascript; charset=utf-8 """); config.serveFile("/ui/script.js", Script.getBytes());});
The created Web.Resources
instance:
-
contains a regular file named
script.js
in a directory namedui
; and -
will serve any file with the
.js
file extension withtext/javascript; charset=utf-8
as the content-type.
Next, in the application's HTTP module, we create the route:
route("/ui/script.js", handler(injector.webResources()));
As a result, all requests to the /ui/script.js
path will be handled by Web.Resources
instance created earlier.
The Web.Resources
instance will:
-
respond to a
GET
orHEAD
request with either200 OK
or312 Not Modified
; and -
respond to all other HTTP methods with
405 Method Not Allowed
.
Conclusion
Objectos PetClinic v001 should give you an idea on what is like to develop web applications the Objectos Way. Java developers can create web applications without leaving their language of choice.
In part 1 of this series we focused on the features related to the UI creation:
-
write HTML directly in Java allowing you to use regular Java constructs;
-
style your interface using Tailwind-like utility classes;
-
smooth page navigation with the
data-frame
attribute; and -
static files are first-class citizens.
In part 2 of this series, we will focus on the development process, SQL utilities and testing.
Check out the project on GitHub.