Written by Anton Epple, Johan Vos, Bruno Borges, and José Pereda
Client applications, as the term itself already hints, are rarely self-contained. In order to function properly, they need to access other components, for example, server applications. JavaFX, being a Java framework, allows application developers to leverage the wide Java ecosystem. Libraries created for interacting with, for example, REST APIs, web components, SOAP endpoints, and encryption APIs can be used in JavaFX applications. As such, there is nothing special about a JavaFX application vs. a non-JavaFX application. However, there are a number of features in JavaFX that enable developers to create an easy, secure, reliable connection to other (backend or server-side) components.
In this chapter, we will discuss two approaches for integrating backend components with JavaFX applications. First, we’ll discuss the WebView component. At its core, the JavaFX WebView control has most of the functionalities of a web browser, and it allows for two-way interaction between script elements on the web pages and Java functions in the JavaFX application. It is often used as an easy way to render existing functionality from a web site into a desktop application.
Integrating with the Web
With the WebView component, you can display HTML5 content in your JavaFX application. This works for a local HTML file or a web page. The component is based on WebKit and is quite powerful. The possible applications range from the display of documentation to the integration of complete web applications. What is very practical is that it is easy to interact with the content of the WebView component via a JavaScript bridge. This allows us to modify the page from JavaFX and react to user actions. With the help of the open source project “DukeScript,” you can even bind HTML elements directly to JavaFX properties.
Displaying a Web Page
Adding Navigation and History
The result is shown in Figure 9-5. That’s not much effort for building a nice basic browser. Now it’s time to improve the user experience. Currently, we get no indication of the loading process.
Showing Loading Progress
Executing JavaScript
So far, the code still looks OK; the dirty secrets are hidden in the method constructJavaScript. This and another helper method for converting a List to a comma-separated String are added now:
That’s a bit ugly, isn’t it? We construct a JavaScript String from our data. This part of the work isn't really much fun and is difficult to debug. But once you have built a proper Java API for the call, you will be rewarded with new JavaFX components that provide new functions. With executeScript, we can now execute it, and our data is displayed as shown in Figure 9-7. The advantage of this approach is that practically everything can be implemented that works in a web application, as long as the WebView supports it. JavaScript code can even be shared between web applications and desktop applications of a company.
Registering Java Callbacks
So far, we’ve used the WebView to show static content and used the JavaScript bridge to dynamically generate content and display it. But to make it really interactive, we need to be able to react in Java to events from inside the HTML page. In fact, with the WebView, we can also call Java functions from JavaScript.
When you start the application, clicking the HTML button will access this Java object via JavaScript, and the print method will pass the content of the form field as a String.
So the WebView not only provides you with a way to integrate HTML5 content in your JavaFX application; it also enables you to synchronize contents via two-way communication. This has successfully been used in many companies to provide a powerful bridge between web and desktop applications. But the process is tedious and leads to solutions which are hard to debug and difficult to maintain. In the next part, we’re looking at some open source projects which make these tasks much simpler.
FXML and the Model View ViewModel Pattern
Admittedly, creating a Java API for a JavaScript library like this is an ugly task. DukeScript (http://dukescript.com) is a framework which makes this task a lot easier. While DukeScript is a full-featured Java UI framework on its own, we will only focus on how we can use it to save us from the pains of synchronizing between Java and JavaScript in JavaFX. Fortunately, its core features are an API for painless Java and JavaScript interaction and an API for direct binding of HTML elements and attributes to Java properties.
The resulting tabbed UI is shown in Figure 9-8. This is a very popular pattern in UI development also known as Model-View-ViewModel (or MVVM) . The FXMLLoader uses the @FXML annotation for exposing functions and relies on reflection for binding user actions to method calls and View properties to properties in the ViewModel.
FXML unfortunately doesn’t fully implement MVVM. Its expression language lacks declarative bindings for loops and control constructs like if or else. Therefore, you need to use Java in many places to manipulate the Nodes directly. To do this, you can also inject Nodes into the viewmodel using the @FXML annotation, which breaks the isolation required by MVVM. But still FXML is useful, as it supports at least some declarativeness.
Writing a Controller for HTML
FXBeanInfo supports the builder pattern for easily creating the FXBeanInfo using the actual properties of your bean. You’re not required to write getters and setters for your properties, since they can be accessed using the FXBeanInfo. The consumer of the FXBeanInfo can simply call the method getProperties in order to get a map of the available properties. It’s a map instead of a set or list, so you can also easily access a property by its name.
It’s easy to see how using this could also improve the way the FXMLLoader currently works. Instead of scanning the controller via reflection for getters and setters of a bound property, the loader could simply access the Property via the FXBeanInfo. No reflection is required at all – clean, fast, and standardized access to all exposed properties.
FXML controllers are much harder to test in comparison, as you need to somehow inject the @FXML objects and initialize the JavaFX platform in order to test the logic or use an additional testing framework.
Adding the HTML View
The bindings are defined in data-bind attributes and follow a simple pattern. First, there’s the name of the binding, “text” and “click” in our example, followed by the bound property or action, in this example “labelText” and “action.” There are a lot of bindings supported for the different user interactions and controls, and there is support for loops and conditional statements. This makes it possible to declare all bindings completely in the view. Unlike with FXML, there’s no need to inject Nodes to the controller. This way the controller stays unit testable and completely view independent.
HTML Data Binding Explained
You can see how the input Property of our controller is bound to the input element via the “textInput” binding. Whenever the user enters some text, the corresponding Property will update automatically. The “add” button is bound to the add action via the “click” binding. That’s similar to the first example. Next, the UL element is bound to the “todos” Property via the “foreach” binding. This means that everything inside this element, the List Item Element, will be treated as a template and copied for each entry. After it’s copied, the TodoElement will be set as the context object for this part of the HTML, and the bindings will be applied.
With FXBeanInfo, HTML is easier to use than FXML. There’s no need for reflection, which makes the system fast and simple. With no need to inject Nodes into the viewmodel, the view logic is completely unit testable. To learn more about all the available bindings, check out https://dukescript.com/knockout4j.html#ko-bindings.
Displaying a Map with DukeScript
This example is taken directly from the Javadoc (www.dukescript.com/javadoc/leaflet4j/). There’s no JavaScript required. The API works just like a regular JavaFX component, in case you need a nice map component for your JavaFX application.
There are many more libraries which have already been adapted for use with Java-based models, including a Canvas API (https://github.com/dukescript/dukescript-canvas) and a Charts API (https://dukescript.com/javadoc/charts/) which internally uses the HighCharts library we used in our earlier demo.
From Web Sites to APIs
In this section, we’ll show how existing web sites can easily be rendered in JavaFX applications. By linking JavaScript functionality on the web sites with Java functions in the JavaFX application, it is possible to enhance the web site functionality and make it more interactive or to integrate it with functionality that is not available to web browsers (e.g., connection with devices).
While this is often a quick and simple solution for creating a desktop application based on an existing web site, in most cases it doesn’t provide the rich functionality offered by the backend, and it doesn’t leverage the rich functionality offered by the JavaFX APIs.
In the second part of this chapter, we will discuss how to access backend functionality and integrate it with JavaFX controls in a more granular way.
Building for the Cloud
Around 2012, the cloud finally went mainstream, but only with very few players that still today dominate the market. And yet, the competition is huge, with services and APIs being announced every quarter. Building applications for the cloud usually means taking advantage of these highly available and scalable infrastructure as a service, as well as highly productive and easy to use and integrate platforms as a service. Whether it’s a virtual machine or a database that is quickly provisioned for you or a face detection API that just needs a subscription key, all these resources have one thing in common: it is not you who is managing, maintaining, and scaling them, but your cloud provider.
Cloud also often means building web-based applications or web-based REST APIs that will run in the cloud and talk to resources also living in the cloud. How these applications and services are built and deployed often falls in the microservices and cloud-native architecture playbook. Scalability patterns of cloud resources apply fairly well to these web applications and microservices. And when everything is on the Internet and applications are accessed through the browser, some automated HTTP-based client, or messaging systems, many challenges that are common to desktop applications such as version update, data caching, service rerouting, and so on simply do not exist or are much easier to be solved in the cloud.
So what happens when the user-facing application is a rich client? How can developers take advantage of the cloud, and why? The focus here is in providing value in their modern client desktop application by not having to develop certain algorithms and business logic within the client itself and instead either move them to a place where developers can have better control and deliver updates faster or consume ready-to-use service APIs that would otherwise had consumed a significant time of development to be built in the rich client.
Compared to the scenario where the client is simply displaying a WebView with a web site, this approach is much more powerful. Some parts of the functionality can be implemented in the client, while other parts can be offloaded to the cloud provider.
Architecture of a JavaFX Cloud Application
Most modern enterprise systems have a multitier architecture, in which the web-based frontend and the business logic are separated. Typical web frameworks query the business tier, either using direct (Java) calls or REST APIs.
Use Case: Querying OpenWeather
We will now show a JavaFX application that queries the OpenWeather API (https://openweathermap.org) to retrieve the weather for a given location. We could have simply used a WebView and render the existing OpenWeather web site, but we want to leverage the power of the JavaFX controls, or we want to integrate seamlessly the weather data within our existing JavaFX UI application.
We will now explain how to write an application that retrieves queries from OpenWeather. In order to do so, the first thing is to sign up in the portal (https://home.openweathermap.org/users/sign_up) and get the default API key or generate a new one (https://home.openweathermap.org/api_keys).
Case 1: Jackson
A very popular framework for deserializing JSON into a Java object is the Jackson project, that provides a JSON Java parser (https://github.com/FasterXML/jackson).
Notice that there is a synchronous call to do the whole process of querying the given URL, waiting for the result, and retrieving and parsing the response. If anything goes wrong, like a timeout or a network error, there will be only an exception, and possibly the UI will freeze, so we should wrap that call in a JavaFX service.
Case 2: Connect
Gluon’s Connect library (https://github.com/gluonhq/connect) not only deserializes the JSON response like Jackson but also does it in an asynchronous way; and, what’s more, it returns a JavaFX observable list or object that can be used directly by the JavaFX UI controls.
There is a very important difference between the case where the client code resides in a cloud environment and on a desktop with an end user. In the first case, the cloud environment can in most situations be considered as a trusted environment. However, in the case where the code resides on the end user desktop, this is no longer true. The end user himself, or other malicious applications on the device, might obtain the API key.
In the case of OpenWeather queries, this is probably not the most fatal issue, but in general, API keys that provide access to critical or sensitive functionality should not be stored on end user systems.
In order to fix this issue, it is beneficial to use a middleware component that acts as a bridge between the trusted cloud infrastructure and the client device (desktop/mobile/embedded) of the user.
In the next paragraph, we will explain how to write the OpenWeather application using Gluon CloudLink (https://docs.gluonhq.com/cloudlink/), which is a middleware solution from Gluon that provides specific support for JavaFX applications.
Case 3: CloudLink
Before we add the code, we need to access the Gluon Dashboard (https://gluon.io), access with our credentials, and go to API Management to add a remote function:
Then we add two query parameters for the request: appid and q. We set the value for the former with our API key, so the client doesn’t need to do it, while the value for the latter will be set in the client. It is important to realize that the API key is stored in the middleware (hosted in the cloud) and only sent between the middleware and the remote service. The client application is not accessing this API key; hence, the risk for the key being obtained by a hacker is minimized.
As you can see, there is only one query parameter in the remote function call from the client.
The content of this file can be retrieved from the Dashboard ➤ Credentials ➤ Client.
Conclusion
JavaFX applications are regular Java applications and can use all existing Java functionality. Using the JavaFX WebView control, it is possible to render web pages in a JavaFX application and create interactions between the web page functionality (in JavaScript) and the Java engine running on the client.
For fine-grained, secure, and flexible integrations of client applications with enterprise functionality in the cloud, existing Java frameworks can be used. Developers should be very aware of the specific characteristics of client systems, especially related to security. Therefore, middleware (e.g., Gluon CloudLink) allows developers to shield the security-sensitive information from the client application.