You will not believe it, but in 2018 you still need to develop Desktop applications.
Imagine such a club of anonymous Java programmers, drunken and fast-boned, who sit and share their problems.
- Hello, my name is Yuri, I am writing a Desktop application in 2018.
- Hello, Yuri, let him pat, he was able to share his problem!
')
Yes, indeed, we are still writing Desktop applications. Usually, I don’t really want to do this, most often these are legacy projects. But it happens that you need to write and new desktop applications.
Why are we still doing this if there is a web with its new advanced features: Progressive Web Apps, Service Worker, Web RTC, Web GL, etc?
Under the cut I will tell you how to live with it and what does Java have to do with it.
Unfortunately, web and desktop still cannot be compared in terms of number of possibilities. The Web will never give us anything that a user machine gives us. First of all, it is working with local files and devices. We can process big data on the client, use specific equipment, well, we can refer to what we want.
For example, we can use a fingerprint scanner and other fancy devices. Most often, additional equipment is used in enterprise applications: banks, ERP, accounting, etc. Here it is all exactly needed.
In addition, there are tasks that can be badly solved in the web. The main one is server and network independence.
It is especially important if your application will be used on laptops. We assume that in 2018 laptops finally won and now can not get away from them. Very often, people work with a laptop on trains and airplanes offline and they need to be able to work locally with data that is already loaded on their car. The web is still difficult to implement this behavior.
And what about Java?
Recently, Java desktop technologies are not evolving: Swing is frozen forever, there are no new features in SWT. The latest live desktop technology is Java FX.
The impact of web technologies on desktop frameworks is very large. Qt invented its own Qt Script and QML based on JavaScript, and in Java FX they implemented support for CSS. Sadly, CSS in Java FX is partially implemented and all properties of components differ from properties in web-UI.
As a result, we must again study the specific framework for the desktop, instead of reusing a huge amount of ready-made developments for the UI from the world of the web.
And the personnel question comes to the fore here: no one is eager to write desktop applications on Swing and Java FX, but at the same time, the market is full of good web-UI specialists.
Java on the desktop causes pain not only because of Java FX / Swing. Recently,
Oracle decided to kill the Web Start technology .
Everything goes to one thing - specialized UI frameworks for desktop applications die.
Where to run!?
And now, about how to reuse your experience in web development and web-UI in the development of desktop systems in Java. So that you do not have to develop separate web and desktop applications.
Electron.js is a fairly well-known framework that allows you to use web technologies for developing desktop applications. This is the technology on which the GitHub Atom editor is built. Atom is the first widely-known desktop application built on HTML / JavaScript / CSS and Node.js.
Electron.js is an Open Source framework that allows you to write UI for desktop applications on a web stack. You might be surprised, but a whole bunch of tools and applications are now built on this framework: Slack / Skype and VS Code are used for UI by Electron.js.
In a nutshell, Electron consists of server-side JavaScript - Node.js and Chromium integrated web browser in one executable process, which allows the use of native operating system features: windows, notifications, tray icons and much more.
Until recently, this framework assumed the development of applications only in JavaScript. But we figured out how to use it for our Java applications!
There are two options for developing applications:
- Compile Java to JS
- Run the JVM unnoticed by the user and show the UI in the web browser
If you already have a frontend on JS, then there is nothing to think about, it remains to pack it into Electron.js and get a desktop application. So do, for example, in the Slack messenger.
And if you have a lot of Java code? And while it can not just be collected in JS, you will immediately lose your reflection, access to hardware and the possibility of using common libraries.
In this case, you can use a special Node.js module -
child_process , which allows you to start child processes in all major operating systems. In essence, we need to implement one JS file that will launch the JVM and open the Electron window:
if (platform === 'win32') { serverProcess = require('child_process') .spawn('cmd.exe', ['/c', 'demo.bat'], { cwd: app.getAppPath() + '/demo/bin' }); } else { serverProcess = require('child_process') .spawn(app.getAppPath() + '/demo/bin/demo'); }
The main thing is not to forget to kill this process afterwards, when the application window is closed, which is easy to do with the
tree-kill module:
const kill = require('tree-kill'); kill(serverProcess.pid, 'SIGTERM', function (err) { console.log('Server process killed'); serverProcess = null; mainWindow.close(); });
The full code of such integration is in my
tutorial . It uses Vaadin as a framework for the UI, which allows you to write all Java code without JS, and the Jetty servlet container is integrated directly into the application. We can run the application without deployment, like the same Spring Boot.
In this embodiment, our frontend will access the JVM over the network, which is somehow strange. I sweetened the pill a bit by changing the transport between the browser and the JVM to the WebSocket protocol. Vaadin makes it
extremely easy to do. So we significantly reduce the time to send messages from the frontend to the JVM over the network, literally up to 1ms and get rid of unnecessary HTTP garbage: headers and cookies, and also do not create a connection, but always use ready-made.
On this stack I wrote a
small TODO application .
There you will find some more cool tricks:
- loading static files (CSS / JS) directly from disk bypassing the JVM
- access to Electron.js API from Java
- custom header windows on HTML and CSS
- automatically installing Node.js from Gradle
Well, now let's get rid of the net! For a long time in operating systems * nix and Windows there is an API for interprocess communication - IPC. On Windows, it is called named pipes, and on * nix, it is called Unix sockets. This is a buffer / virtual file in RAM that is controlled by the OS and allows two independent processes to transfer data.
For the sake of diversity, I implemented this approach on
Kotlin and Kotlin.js .
In Node.js, we can easily create both a named pipe and a Unix socket using the -
net module:
const pipeServer = net.createServer(function(stream) { stream.on('data', function(c) { console.log('Pipe: ', c.toString().trim()); }); stream.on('end', function() { pipeServer.close(); }); pipeStream = stream; console.log('Pipe is connected'); global.pipe.send('hello', {}); }).listen(PIPE_PATH, function () {
To work with named pipe from Java / Kotlin you need to use the RandomAccessFile class:
val pipe: RandomAccessFile try { pipe = RandomAccessFile("\\\\.\\pipe\\demo", "rw") } catch (f: FileNotFoundException) { println("Unable to open communication pipe") exitProcess(-7) } var data: String? = pipe.readLine() while (data != null) {
So we can completely get rid of the network and not start the HTTP server on the user's machine. And of course, the performance of such a solution is better than transferring data through the network stack.
Why do we need this?
As you probably know, we are making a tool for developers -
CUBA Studio , which allows you to quickly write business applications on the CUBA Platform.
We used the Vaadin framework in the CUBA Platform and CUBA Studio, which allowed us to reuse a large number of developments. From the very first release, CUBA Studio has been a web application that runs locally and shows the UI in a web browser.
Using this approach, we were finally able to give developers the convenience of using a desktop application: windows, browser independence, Alt + Tab switching, and an icon in the taskbar.
CUBA Studio still uses the network, but WebSocket is involved instead of AJAX. This is enough for users to feel no UI delays.
The Electron.js ecosystem pleased us with
additional tools .
- modules for creating installation files and packages
- smooth auto update
As you might guess, CUBA Studio is not the ultimate goal.
How to replace Swing in our applications
From the very first public release of the platform, we have provided two technologies for building a UI: a web client based on Vaadin and a desktop client based on Swing. The implementation of Generic UI allows us to write code that works in two versions automatically, of course, with appropriate restrictions.
The desktop client was developed in 2011 on the basis of Swing, because then there was no other stable and viable technology for desktop applications in Java.
But everything changes.
Today we are faced with new requirements for desktop user interfaces: responsive UI, animation, integration with network services such as Google. In essence, applications move towards the web UI technologies, and we cannot ignore this movement.
I see only one alternative to Swing and Java FX - Electron.js. Therefore, I tried to adapt this approach for our applications:
- We take a web client
- Add Electron.js
- We pack everything and give it to the client
Applications on the CUBA Platform can be distributed as a
Uber JAR . This version of deployment allows you to start the application with a simple command:
> java -jar app.jar
We just need to add a special launch script on Electron and get a Desktop client from a ready-made web application!
You can find the full application code
here .
Yes, you are crazy!
I also thought that the guys from GitHub were crazy. But gradually penetrated. I want to have a flexible UI creation tool, and the most flexible tool today is HTML / JS / CSS.