How to Wrap BootsFaces (or JSF in General) as a Native Desktop Application

When I showed the draft of my last post (the “Java on the Desktop” survey article), my friends surprised me by saying that nowadays desktop applications are sort of exotic. Everybody’s doing mobile or at least web applications. But the good old desktop has fallen into oblivion. That’s pretty strange, given that most of us do most of their daily work at desktop PCs or decent-sized laptops.

No matter what you do, it’s always a compromise

My previous post already mentioned some of the disadvantages of using the browser as your operating system. But most developers I know aren’t familiar with traditional desktop libraries and languages, such as JavaFX, Microsoft UWP, Microsoft Foundation Classes, and so on. Each of these approaches is valuable, no doubt about that. But there’s a catch: if an API truly embraces the desktop, it’ll inevitably neglect the web. Not to mention mobile platforms (with the possible exception of UWP). Wouldn’t it be nice to use one of the web programming APIs without neglecting the peculiar needs of desktop users?

Web application frameworks are both an opportunity and a restriction

Shortly after publishing the first version of this article, I watched the video announing electron. Electron is the topic of an article yet to be published, but the video reminded me of two important issues I’ve forgotten to mention in the first place.

First of all, web applications are platform independent. That’s also the reason why they offer so little features. They offer the least common denominator. Platforms like electron or my approach to wrapping a BootsFaces application in a desktop application alleviate this. Both electron and my approach run on Windows, Linux and OSX.

The second issue is deploying software on the client computer. As far as I can see, this is the principal reason why the web became such a dominant player. Using a web application automatically means to always use the current version of an application. Running software installed locally usually amount to run into all kinds of caching problems. Mind you, a local installation of a program is just that: a possibly outdated copy of the current version of the program. In other words, you’re running your application from a cache which may or may not be up-to-date.

Enter BootsFaces (or – more generally speaking – JSF, or any other web framework)

As it turns out, there are only two or three ingredients you need to bake your desktop application using a web API. First, you have to use a down-stripped browser. You need the rendering engine, but little more. There are at least two options doing just that: the JavaFX browser control, and its SWT counterpart. My example repository only shows how to use these two options.

Embedding the browser in a desktop window

The source code is surprisingly simple:

public final class SWTBrowser {
	public static final void launch() {
		Display display = new Display();
		final Shell shell = new Shell(display);
		Rectangle clientArea = display.getClientArea();
		shell.setSize(clientArea.width, clientArea.height);
		shell.setLocation(0, 0);
		GridLayout gridLayout = new GridLayout();
		gridLayout.numColumns = 1;
		gridLayout.marginWidth = 0;
		gridLayout.marginHeight = 0;
		shell.setLayout(gridLayout);

		final Browser browser = new Browser(shell, SWT.NONE);
		GridData data = new GridData();
		data.horizontalAlignment = GridData.FILL;
		data.verticalAlignment = GridData.FILL;
		data.horizontalSpan = 1;
		data.grabExcessHorizontalSpace = true;
		data.grabExcessVerticalSpace = true;
		data.widthHint = clientArea.width;
		data.heightHint = clientArea.height;
		browser.setLayoutData(data);

		shell.open();
		browser.setUrl("http://localhost:8080");

		while (!shell.isDisposed()) {
			if (!display.readAndDispatch())
				display.sleep();
		}
		display.dispose();
	}
}

Embedding the server

Second, you need an embedded application server. Strictly speaking, it doesn’t have to be embedded, but you get more control over the server if you’re running it in embedded mode. For those who haven’t encountered an embedded application server yet: the traditional paradigm of web development is that you deploy your application in an application server. However, many application servers allow you to do it the other way round. You can package the application server as part of your application. This gives you an executable jar file. Double-clicking this file starts the application server and your application within.

Again, the source code is far from being difficult. I’m told you can use TomEE as an embedded application server. There’s even a Maven plugin generating the executable JAR file: mvn tomee:exec. My example uses a more modest project setup. It combines a Tomcat with CDI and JSF. Basically, the Java code starting the server looks like so:

public static void main(String[] args) throws Exception {
  File root = getRootFolder();
  System.setProperty("org.apache.catalina.startup.EXIT_ON_INIT_FAILURE", "true");
  Tomcat tomcat = new Tomcat();
  Path tempPath = Files.createTempDirectory("tomcat-base-dir");
  tomcat.setBaseDir(tempPath.toString());
        
  tomcat.setPort(8080);
  File webContentFolder = new File(root.getAbsolutePath(), "src/main/webapp/");
  if (!webContentFolder.exists()) {
       webContentFolder = Files.createTempDirectory("default-doc-base").toFile();
  }
  StandardContext ctx = (StandardContext) tomcat.addWebapp("", webContentFolder.getAbsolutePath());
  //Set execution independent of current thread context classloader (compatibility with exec:java mojo)
  ctx.setParentClassLoader(Main.class.getClassLoader());
  WebResourceRoot resources = new StandardRoot(ctx);

  WebResourceSet = new EmptyResourceSet(resources);
  resources.addPreResources(resourceSet);
  ctx.setResources(resources);

  tomcat.start();
  ... // to be continued below
}

Of course, that’s a very minimalistic example. The example code at GitHub is a bit more elaborate.

When you start the application, you’ll notice some differences to a traditional browser window: there’s no URL field, you’ve got more screen real estate, and the F1 key doesn’t show how to use the browser. Instead, you can catch it in JavaScript to show your application’s help. Likewise, you can use CTRL+S to save the application data instead of saving the HTML source to disk.

Creating the two-in-one package

We want to create an application that looks and feels like a desktop application, so starting the server by a double click isn’t exactly what we need. But it’s close. The crucial point being that the server is started by calling a simple main() method. This method, in turn, can be wrapped by another method.

And that’s exactly what my example does. It combines these two ingredients, resulting in something looking almost like a desktop application. You can start it with a double-click. This double click starts both the server and the browser window with a predefined URL. So the UI is shown in a window without the address bar, without the back button, and without the browser’s context menu. Plus, when you close the application window, the server is stopped, too.

public static void main(String[] args) throws Exception {
  ...
  tomcat.start();  // that's where the example above ended

  SWTBrowser.launch();
  tomcat.stop();
}

From a technical point of view, that’s still a full-blown web application. You can publish it on the internet by simply publishing it on a Tomcat. But at the same time, you can use it as a desktop application.

Yes, it’s a compromise!

The application still runs in a browser, amounting to a number of restrictions. But these restrictions are much less hard to deal with than with the classical client-server paradigm. For once, the server runs on the client. So the file system of the server is the file system of the user. So the server can read and write local files (unless your system administrator restricts the user access rights). It can also start Excel directly.

However, I’m not sure I’d recommend this approach. The drawback is that there’s no way back to the internet. Once the server starts to rely on local resources, you’re doomed. You’ve written a web application that can’t be hosted on a remote web server.

Luckily, there’s an alternative, at least with the SWT approach. It’s possible to register Java methods as JavaScript functions on the browser engine. When the UI code calls this JavaScript function, the JavaScript code is executed. Read the full story at at Lars Vogel’s blog.

Of course, this approach breaks the web paradigm, too, but in my eyes, it’s a bit cleaner and less invasive than the naive approach. In any case, you need to be careful in order to get a clean and maintainable architecture.

Wrapping it up

It goes without saying that you need to go truly native to yield the best support of your native operation system. However, it’s astonishing how much you can achieve using JSF. You can use the server-side programming model you’re probably already familiar with while giving the user the illusion to run a true desktop application. Along the way, you can eliminate a couple of annoying stumbling block of web applications, such as the always-editable URL field, the back button and the lacking access to local resources. Granted, it’s a compromise. For instance, I’m not sure if the BrowserFunctions are powerful enough to support importing an Excel file using drag and drop. But probably even this is possible, so you’ll make your users happy with this approach. From the programmers or operations point of view, there’s another advantage: you know exactly which browser is used. When my team and I developed this approach some ten years ago, we even packaged the JRE and the Mozilla browser within the application bundle. So we had a fine-grained control over our runtime environment, which spared us a lot of nasty surprises, such as a broken application after a Windows update. However, if you do such things, don’t forget to keep the JDK and the browser up-to-date. Even a closed system such as a browser-based desktop application should be protected from viruses, worms and trojan horses which are so abundant in the internet.

Dig deeper

example repository using an embedded server and embedded browser
How to run an embedded Tomcat
How to run CDI on a Tomcat
Lars Vogel on SWT BrowserFunctions

Leave a Reply

Your email address will not be published.