Building a desktop application using ZK and JCEF

From Documentation
DocumentationMattBuilding a desktop application using ZK and JCEF
Building a desktop application using ZK and JCEF

Author
Matthieu Duchemin, Engineer, Potix Corporation
Date
June 7, 2022
Version
ZK 9.6.1-Eval

Introduction

ZK Framework’s main purpose is to provide a Web front end to Java applications. The classic ZK use case is having a standard web server hosting the ZK application as a war file, and connecting to other backend services (databases, business layer, authentication, etc.)

This said, not every Java application lives on a cloud server or an enterprise server bay. Sometimes, a Java application is meant to be run from a local machine, perform a task, and be terminated. They may also be instructed to perform local OS or file-level operations. In the case of these self-contained desktop applications, we only aim to serve a single user, in a much shorter time span. Instead of behaving like a website, this kind of application behaves like an executable -- It creates a UI, serves content to a single user on the same local machine, and then shuts down once the task is finished.

A good use case for this desktop client is to create a smaller-scale utility software based on existing Java code developed for a larger application. For example, if you have already created the classes and libraries to perform a certain type of operation and want to package it in a way that makes it accessible offline to end-users who have no access to your application server for extended periods.

UI options for these desktop applications include Swing, SWT, AWT, etc. But what if we could use ZK components in a familiar browser frame to control this application?

In this article I am going to build a basic project, demonstrating how you can create a desktop application using ZK, springboot, and JCEF.

Architecture considerations

This standalone structure is a departure from the standard ZK infrastructure. As introduced earlier, in a standard case, a ZK application runs on a server accessible by multiple users and has access to infrastructure layers (database, gateway, reverse proxy) to provide content and access. This classic ZK application is expected to be started and then run for an unspecified amount of time, providing an “always-on” service to users connecting to it.

Classic ZK web architecture

On the other hand, in the case of a desktop application, it creates a UI, serves content to a single user on the same local machine, and then shuts down once the task is finished. Therefore we have different considerations when building these self-contained applications.

ZK Spring boot as a self-contained package

The smallest requirement to run a ZK application is that the ZK Framework is loaded inside of a Servlet-based web server. Usually, this would mean deploying the ZK application as a War file onto a Tomcat, Wildfly, or another web server.

This said, we can package a ZK application as a self-contained application by leveraging Spring Boot and the embedded spring-boot-starter-tomcat dependency.

This dependency allows the application to run its own tomcat web server internally, launch when the application is started, and terminate when the application performs a shutdown. This architecture is convenient for quick development, and for situations in which services need to be started and terminated on-demand.

Self-contained package as an executable jar

Since the ZK Spring-boot application carries its own web services, we can run it from virtually any machine. The only requirement is that a suitable JVM is available on said machine.

ZK with JCEF as a desktop local application

Therefore, a spring-boot application already fulfills most of the criteria to be considered a local application, with one exception: by default, the application could open the default browser installed on the machine but would not have complete control over its displayed features, nor be able to register actions on the browser itself. To have this level of control, we can leverage the JCEF library.

JCEF basics

The Java Chromium Embedded Framework (JCEF) is a simple framework for embedding Chromium-based browsers in other applications using the Java programming language. By adding the JCEF dependency to our local application, we can create a Chrome browser frame as a component of a Swing UI. This means that in addition to standard swing controls, we have the option to also display an embedded web client displaying the ZK application.

Technical steps

In this section, we will discuss specific code samples used to complete the migration from a web application, to an integrated JCEF client.

Project setup

For this project, we rely on the following dependencies:

  • Zkspringboot-starter: a quick and easy way to initialize a ZK spring boot application.
  • JcefMaven: a wrapper for the jcef packages, available through maven.
  • ZK dependencies: the full list of ZK dependencies, in which we can add ZK plugins as necessary.

Creating a ZK spring boot application

We will use the existing ZK spring boot starter architecture for this application. The ZK spring-boot starter is a dependency which provides an easy-to-use automated configuration for the ZK framework as a spring-boot application.

Instead of declaring the ZK servlets, startup routines, configuration classes, etc manually, we only need to fill ZK related properties in the application.properties file located under src/main/resources.

If you want a longer primer on ZK with spring boot, you can refer to the ZK spring boot documentation.

Adding the JCEF dependencies

Since we are using maven for dependency management, we can declare the jcef-maven dependency.

<dependency>
	<groupId>me.friwi</groupId>
	<artifactId>jcefmaven</artifactId>
	<version>${jcef.version}</version>
</dependency>

It would also be possible to download the jcef main library and build it locally, but this is outside the scope of this project.

In addition, using the jcef maven wrapper will allow the application to locate and download the browser sources matching its executed environment. With this setup, we will not need to bundle sources for every possible OS and architecture. Instead, the JCEF library will download and build the correct version of the OS for the executing system.

Browser control

Since the JCEF frame is created by our local application, we have more control over it than we would have over the default browser. Once we have built an appropriate browser for the client running this application, we can configure a browser frame and add it to our application’s swing window.

Creating the browser on first load

It would be impractical to bundle every possible variation of the browser depending on the OS, hardware configuration, etc. Therefore, the JCEF library will load and build a browser locally matching the client’s requirements. The first time the application is run on a machine, JCEF will download the required files onto the local directory selected for our application.

Once the browser has been built, subsequent application launches will use the existing browser. This significantly speeds up application startup after the first execution.

The steps are as follows:

We first need to instantiate a CefAppBuilder and give it a directory to store the embedded chrome files:

//Create a new CefAppBuilder instance
CefAppBuilder cefBuilder = new CefAppBuilder();
//Configure the builder instance
cefBuilder.setInstallDir(new File("jcef-bundle"));

Sample in github.

We can then create a CefAppBuilder which will build the application. On the first run, the builder will download and build the actual browser. On subsequent launch, the builder will directly fetch the files generated on the initial launch, reducing launch time significantly.

CefAppBuilder builder = new CefAppBuilder();

Sample in github.

Our next step to is create a cefClient. The cefClient is a connector which will dispatch all events for all possible browser instances.

client_ = cefApp_.createClient();

Sample in github.

The last step is to create a browser instance. The browser will provide a browserUi, which is a swing component. That swing component can then be used in a spring UI.

browser_ = client_.createBrowser(startURL, useOSR, isTransparent);
browerUI_ = browser_.getUIComponent();

Sample in github.

In this case, our root frame is the MainFrame class, which extends javax.swing.JFrame. Since we are in a standard spring frame, we can simply add the component directly to the frame using spring default methods:

getContentPane().add(browerUI_, BorderLayout.CENTER);

Sample in github.

Once our UI is all ready to be shown to the user, we will call toFront and repaint. This will prompt the resulting window to be brought to the front of the client’s desktop and to refresh its content.

toFront();
repaint();

Sample in github.

Terminating the application on browser shutdown

In order to prevent our application from staying active in the background if the user closes our swing window, we will also add an event listener triggering if the window becomes closed. In that case, we will simply dispose of the application and end all threads.

addWindowListener(new WindowAdapter() {
    @Override
    public void windowClosing(WindowEvent e) {
        CefApp.getInstance().dispose();
        dispose();
    }
});

Sample in github.

All put together

With this structure, we have created a Swing container, displaying a JCEF browser frame, which itself shows a ZK application.

Completed ZK and JCEF application

Use cases and limitations

It is important to remember that since this application is executed on a client’s machine, it may not have access to the usual central databases, or other business layer services that a webserver might have.

Instead, it may need to either bundle all its resources, or access a public API, such as a REST API available through the internet to retrieve external data.

As a local application, however, it can perform operations that a web-based application cannot perform. Since it has access to the local JVM, it will be able to perform local operations, generate files locally which do not need network access to be delivered, etc.

Going further: jar packaging

At this point, we have created a self-contained ZK application, packaged as a spring-boot jar. This jar is launched by the java -jar command, which is convenient for developers and technical-minded users.

To make it friendlier to end-users, it is possible to use a jar to exe wrapper (or other format depending on your target OS), to allow an end-user to simply double-click on the executable to start this application.

Conclusion

In this article, we have seen how a ZK can be used to build a rich desktop application. This application can be created from the ground up, or be reusing business code and logic already developed for a web version of similar workflows.

All of the code used in this article can be found on the matching github project.