Java shine and poverty for desktops

    image

    You won’t believe it, but in 2018 you still need to develop Desktop applications.

    Imagine such a club of anonymous Java programmers, drunken and unbridled, who sit and share their problems.

    - Hello, my name is Yuri, I am writing Desktop applications in 2018.
    - Hello, Yuri, let's pat him, 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 new desktop applications.

    Why do we still do this if there is a web with its new advanced features: Progressive Web Apps, Service Worker, Web RTC, Web GL, etc.?

    Under the cat I’ll tell you how to live with it and what does Java have to do with it.

    Unfortunately, web and desktop can still not be compared in terms of number of features. The Web will never give us everything the user machine gives us. First of all, it is work with local files and devices. We can process big data on the client, use specific equipment, and we’ll want to use whatever.

    For example, we can use a fingerprint scanner and other fashionable devices. Most often, additional equipment is used in enterprise applications: banks, ERP, accounting, etc. That's where all this is definitely needed.

    In addition, there are tasks that can be poorly solved on the web. The main one is server and network independence.

    It is especially important if your application will be used on laptops. We will assume that in 2018 the laptops finally won and there is nowhere to go from them. Very often people work with laptops on trains and planes offline and they need to be able to work locally with data that is already loaded on their machine. It is still difficult to implement this behavior on the web.

    What about Java?


    Recently, Java technologies for desktop systems do not evolve: Swing development is frozen forever, there are no new features in SWT. The latest living technology for desktop is Java FX.

    The impact of web technologies on desktop frameworks is very large. Qt invented its own Qt Script and JavaScript-based QML, while Java FX implemented CSS support. Unfortunately, CSS in Java FX is partially implemented and all properties of components differ from properties in web-UI.

    As a result, we should again study a specific framework for desktop, instead of reusing a huge amount of ready-made developments for UI from the web world.

    And the personnel issue here comes to the fore: no one is eager to write desktop applications in Swing and Java FX, but at the same time, the market is full of good web-UI specialists.

    Java on desktop is not only painful due to Java FX / Swing. Recently, Oracle decided to kill Web Start technology .

    Everything goes to one thing - specialized UI frameworks for desktop applications die.

    Where to run!?


    And now, on how to reuse your experience in web development and web-UI when developing 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 to develop 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 the web stack. You may 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 Electron.js.

    In a nutshell, Electron consists of server-side JavaScript - Node.js and an integrated Chromium web browser in one executable process, which allows you to use native features of the operating system: windows, notifications, tray icons and much more.

    Until recently, this framework was designed to develop applications only in JavaScript. But we figured out how to apply it to our Java applications!

    There are two options for developing applications:

    1. Compile Java in JS
    2. Launch the JVM unnoticed by the user and show the UI in a web browser

    If you already have a frontend on JS, then there’s nothing to think about, it remains to pack it in Electron.js and get a desktop application. This is done, for example, in the Slack messenger.

    And if you have a bunch of Java code? And at the same time, you can’t just compile it in JS, you will immediately lose reflection, access to hardware and the ability to use common libraries.

    In this case, you can use the special Node.js module - child_process , which allows you to start child processes in all major operating systems. Essentially, 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 then to kill this process when the application window is closed, which is easy to do using 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 for this 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 version, our frontend will access the JVM over the network, which is somehow strange. I slightly sweetened the pill by changing the transport between the browser and the JVM to the WebSocket protocol. Vaadin makes this extremely easy . So we significantly reduce the time it takes to send messages from the frontend to the JVM over the network, literally to 1ms, and get rid of unnecessary HTTP garbage: headers and cookies, and also do not create a connection, but always use the ready one.

    On this stack I wrotesmall todo app .

    image

    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 window header in HTML and CSS
    • silent installation of Node.js from Gradle

    Well, now let's get rid of the network completely! Since ancient times, * nix and Windows operating systems have 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, which 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 () {
            // start JVM process here
        });
    }
    

    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) {
        // do something
        data = pipe.readLine()
    }
    

    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 may 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 displays 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, switching on Alt + Tab and the icon in the taskbar.

    image

    CUBA Studio still uses the network, but instead of AJAX, WebSocket is involved. This is enough so that users do not feel any UI delays.

    The Electron.js ecosystem has 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 been providing two technologies for building a UI: a Vaadin-based Web client and a Swing-based desktop client. The implementation of Generic UI allows us to write code that works in two versions automatically, of course, with the corresponding restrictions.

    The desktop client was developed in 2011 based on Swing, since then there was no other stable and viable technology for Java desktop applications.

    But everything is changing.

    Today we are faced with new requirements for user interfaces on the Desktop: responsive UI, animation, integration with network services such as Google. In essence, applications are moving towards web UI technologies, and we cannot ignore this movement.

    I see just one alternative to Swing and Java FX - Electron.js. Therefore, I tried to adapt this approach for our applications:

    1. Take a web client
    2. Add Electron.js
    3. We pack everything and give it to the client

    image

    Applications on the CUBA Platform may be distributed as a Uber JAR . This deployment option allows you to start the application with a simple command: We can only add a special launch script on Electron and get the Desktop client from the finished web application! The full application code can be found here .

    > java -jar app.jar






    Yes, you are crazy!


    I also thought that the guys from GitHub were crazy. But gradually imbued. I want to have a flexible UI creation tool, and the most flexible tool today is HTML / JS / CSS.

    Also popular now: