; last updated - 8 minutes read

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 exotic. Everybody's making 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 our daily work on desktop PCs or decent-sized laptops.

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

My previous post mentioned some 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 announcing 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 few features, and 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 the web became such a dominant player. Using a web application automatically means always using the current version of an application. Installing software locally usually results in all kinds of caching problems. A local program installation 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 that may or may not be up-to-date.

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

As it turns out, you need only two or three ingredients 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. At least two options are 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, the traditional paradigm of web development is that you deploy your application on 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, giving 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. 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, and the example code at GitHub is a bit more elaborate.

When you start the application, you'll notice differences from a traditional browser window:

  • There's no URL field.
  • You've got more screen real estate.
  • 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. CTRL+S always annoys me in a browser application by 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 with a double click isn't exactly what we need. But it's close. The crucial point is that the server starts 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, making your program look almost like a desktop application. You can start it with a double-click, and 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, the back button, and 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 several restrictions. But these restrictions are much less challenging to deal with than the classical client-server paradigm. For once, the server runs on the client. So the server's file system is the user's file system. 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. Registering Java methods as JavaScript functions on the browser engine is possible. The JavaScript code is executed when the UI code calls this JavaScript function. 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 to get a clean and maintainable architecture.

Wrapping it up

You need to go genuinely native to yield the best support of your native operating 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 of running an actual desktop application. Along the way, you can eliminate some annoying stumbling blocks 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 unsure if the BrowserFunctions are powerful enough to support importing an Excel file using drag and drop. But this is probably 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 precisely which browser is used. When my team and I developed this approach ten years ago, we packaged the JRE and the Mozilla browser within the application bundle. So we had 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 needs to be protected from viruses, worms, and trojan horses that are abundant on 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


Comments