Friday, December 30, 2022

JavaFX - Overview

JavaFX - Overview, Oracle Java Exam, Oracle Java Prep, Oracle Java Tutorial and Materials, Oracle Java Career, Java Skills, Java Job

Rich Internet Applications are those web applications which provide similar features and experience as that of desktop applications. They offer a better visual experience when compared to the normal web applications to the users. These applications are delivered as browser plug-ins or as a virtual machine and are used to transform traditional static applications into more enhanced, fluid, animated and engaging applications.

Unlike traditional desktop applications, RIA’s don’t require to have any additional software to run. As an alternative, you should install software such as ActiveX, Java, Flash, depending on the Application.

In an RIA, the graphical presentation is handled on the client side, as it has a plugin that provides support for rich graphics. In a nutshell, data manipulation in an RIA is carried out on the server side, while related object manipulation is carried out on the client side.

We have three main technologies using which we can develop an RIA. These include the following −

◉ Adobe Flash
◉ Microsoft Silverlight
◉ JavaFX

Adobe Flash

This software platform is developed by Adobe Systems and is used in creating Rich Internet Applications. Along with these, you can also build other Applications such as Vector, Animation, Browser Games, Desktop Applications, Mobile Applications and Games, etc.

This is the most commonly used platform for developing and executing RIA’s with a desktop browser penetration rate of 96%.

Microsoft Silverlight

Just like Adobe flash, Microsoft Silverlight is also a software application framework for developing as well as executing Rich Internet Applications. Initially this framework was used for streaming media. The present versions support multimedia, graphics, and animation as well.

This platform is rarely used with a desktop browser penetration rate of 66%.

JavaFX

JavaFX is a Java library using which you can develop Rich Internet Applications. By using Java technology, these applications have a browser penetration rate of 76%.

What is JavaFX?


JavaFX is a Java library used to build Rich Internet Applications. The applications written using this library can run consistently across multiple platforms. The applications developed using JavaFX can run on various devices such as Desktop Computers, Mobile Phones, TVs, Tablets, etc.

To develop GUI Applications using Java programming language, the programmers rely on libraries such as Advanced Windowing Toolkit and Swing. After the advent of JavaFX, these Java programmers can now develop GUI applications effectively with rich content.

Need for JavaFX


To develop Client Side Applications with rich features, the programmers used to depend on various libraries to add features such as Media, UI controls, Web, 2D and 3D, etc. JavaFX includes all these features in a single library. In addition to these, the developers can also access the existing features of a Java library such as Swing.

JavaFX provides a rich set of graphics and media API’s and it leverages the modern Graphical Processing Unit through hardware accelerated graphics. JavaFX also provides interfaces using which developers can combine graphics animation and UI control.

One can use JavaFX with JVM based technologies such as Java, Groovy and JRuby. If developers opt for JavaFX, there is no need to learn additional technologies, as prior knowledge of any of the above-mentioned technologies will be good enough to develop RIA’s using JavaFX.

Features of JavaFX


Following are some of the important features of JavaFX −

◉ Written in Java − The JavaFX library is written in Java and is available for the languages that can be executed on a JVM, which include − Java, Groovy and JRuby. These JavaFX applications are also platform independent.

◉ FXML − JavaFX features a language known as FXML, which is a HTML like declarative markup language. The sole purpose of this language is to define a user Interface.

◉ Scene Builder − JavaFX provides an application named Scene Builder. On integrating this application in IDE’s such as Eclipse and NetBeans, the users can access a drag and drop design interface, which is used to develop FXML applications (just like Swing Drag & Drop and DreamWeaver Applications).

◉ Swing Interoperability − In a JavaFX application, you can embed Swing content using the Swing Node class. Similarly, you can update the existing Swing applications with JavaFX features like embedded web content and rich graphics media.

◉ Built-in UI controls − JavaFX library caters UI controls using which we can develop a full-featured application.

◉ CSS like Styling − JavaFX provides a CSS like styling. By using this, you can improve the design of your application with a simple knowledge of CSS.

◉ Canvas and Printing API − JavaFX provides Canvas, an immediate mode style of rendering API. Within the package javafx.scene.canvas it holds a set of classes for canvas, using which we can draw directly within an area of the JavaFX scene. JavaFX also provides classes for Printing purposes in the package javafx.print.

◉ Rich set of API’s − JavaFX library provides a rich set of API’s to develop GUI applications, 2D and 3D graphics, etc. This set of API’s also includes capabilities of Java platform. Therefore, using this API, you can access the features of Java languages such as Generics, Annotations, Multithreading, and Lambda Expressions. The traditional Java Collections library was enhanced and concepts like observable lists and maps were included in it. Using these, the users can observe the changes in the data models.

◉ Integrated Graphics library − JavaFX provides classes for 2d and 3d graphics.

◉ Graphics pipeline − JavaFX supports graphics based on the Hardware-accelerated graphics pipeline known as Prism. When used with a supported Graphic Card or GPU it offers smooth graphics. In case the system does not support graphic card then prism defaults to the software rendering stack.

History of JavaFX


JavaFX was originally developed by Chris Oliver, when he was working for a company named See Beyond Technology Corporation, which was later acquired by Sun Microsystems in the year 2005.

The following points give us more information of this project −

◉ Initially this project was named as F3 (Form Follows Functions) and it was developed with an intention to provide richer interfaces for developing GUI Applications.

◉ Sun Microsystems acquired the See Beyond company in June 2005, it adapted the F3 project as JavaFX.

◉ In the year 2007, JavaFX was announced officially at Java One, a world wide web conference which is held yearly.

◉ In the year 2008, Net Beans integrated with JavaFX was available. In the same year, the Java Standard Development Kit for JavaFX 1.0 was released.

◉ In the year 2009, Oracle Corporation acquired Sun Microsystems and in the same year the next version of JavaFX (1.2) was released as well.

◉ In the year 2010, JavaFX 1.3 came out and in the year 2011 JavaFX 2.0 was released.

◉ The latest version, JavaFX8, was released as an integral part of Java on 18th of March 2014.

Source: tutorialspoint.com

Wednesday, December 28, 2022

How to build applications with the WebSocket API for Java EE and Jakarta EE

Oracle Java Certification, Java Prep, Java Certification Exam, Java Tutorial and Materials, Java Career, Java Skills, Java Jobs



WebSocket is a two-way communication protocol that lets clients send and receive messages over a single connection to a server endpoint. The Jakarta WebSocket API, part of the Jakarta EE platform, can be used to develop WebSocket server endpoints as well as WebSocket clients. This article provides a brief overview of the Jakarta WebSocket specification, and I’ll show how to construct an application using WebSockets.

I’ll cover the Jakarta WebSocket API as it stands as part of the Jakarta EE 9 platform release. That said, the examples in this article will work with Jakarta EE 8 or Java EE 7 or Java EE 8 applications. The main difference is that the namespace for Jakarta EE 9 is jakarta.*; in earlier releases, it was javax.*. Therefore, if you are using a previous release, change the namespace to javax.*.

WebSocket is a vendor-independent standard. If you’re curious about the WebSocket protocol, it’s covered in depth in IETF RFC 6455. Many tutorials are published online. You can also read the documentation for the WebSocket interface in JDK 15.

To communicate with WebSocket, you must configure a server endpoint. The simplest endpoint is a standard Java class that either is annotated with @ServerEndpoint or extends the jakarta.websocket.Endpoint abstract class.

An endpoint also contains a method that’s annotated with @OnMessage. The @ServerEndpoint annotation accepts the URI at which the WebSocket server will accept messages that need to be sent. The URI can also be used to register clients as recipients for WebSocket messages.

The following simple endpoint accepts a string-based message at the endpoint URI /basicEndpoint and performs an activity with that message once it has been received. A client can connect to the server endpoint URI to open the connection, which will remain open for sending and receiving messages for the duration of the session.

@ServerEndpoint(value = "/basicEndpoint")
public class BasicEndpoint { 
    @OnMessage
    public void onMessage(Session session,
                                  String message){
        // perform an action
    }
}

In the following sections, you’ll see the wide variety of options available for developing more-sophisticated WebSocket solutions. However, the overall concept for generating a WebSocket endpoint remains very much the same as the previous example.

Digging into the specification


You can develop WebSocket endpoints using either an annotation-based or programmatic approach. You can use the @ServerEndpoint annotation to specify that a class is used as a WebSocket server endpoint. The alternative to using @ServerEndpoint is to extend the jakarta.websocket.Endpoint abstract class. The examples for this article use the annotation approach. Similarly, you can use the @ClientEndpoint annotation to specify that a standard Java class is used to accept WebSocket messages. @ServerEndpoint and @ClientEndpoint can specify the following attributes:

◉ value: Specifies a URI path at which the server endpoint will be deployed.
◉ decoders: Specifies a list of classes that can be used to decode incoming messages to the WebSocket endpoint. Classes implement the Decoder interface.
◉ encoders: Specifies a list of classes that can be used to encode outgoing messages from the WebSocket endpoint. Classes implement the Encoder interface.
◉ subprotocols: Specifies a string-based list of supported subprotocols.
◉ configurator: Lists a custom implementation of ServerEndpointConfiguration.Configurator.

The specification defines a number of annotations that can be placed on method declarations of a WebSocket endpoint class. Each of the annotations can be used only once per class, and they are used to decorate methods which contain implementations that are to be invoked when the corresponding WebSocket events occur. The method annotations are as follows:

◉ @OnOpen: When it is specified on a method, it will be invoked when a WebSocket connection is established. The method can optionally specify Session as the first parameter and EndpointConfig as a second parameter.
◉ @OnMessage: When it is specified on a method, it will be invoked when a message is received. The method can optionally specify Session as the first parameter and String (message) as a second parameter.
◉ @OnClose: When it is specified on a method, it will be invoked when a WebSocket connection is closed. The method can optionally specify Session as the first parameter and CloseReason as a second parameter.
◉ @OnError: When it is specified on a method, it will be invoked when an Exception is being thrown by any method annotated with @OnOpen, @OnMessage, or @OnClose. The method can optionally specify Session as the first parameter along with Throwable parameters.

Configuring a WebSocket project


To get started with Jakarta WebSocket, you must either add the websocket-api dependency to a project or add the jakarta-ee dependency to make use of the entire platform. Both the Jakarta EE full profile and the web profile contain the Jakarta WebSocket dependency.

<dependency>
            <groupId>jakarta.platform</groupId>
            <artifactId>jakarta.jakartaee-api</artifactId>
            <version>${jakartaee}</version>
</dependency>

For projects that will contain an @ClientEndpoint, you must add an implementation as a dependency. In this case, I add the Tyrus client implementation by adding the following dependency. (Project Tyrus, from Oracle, is a JSR 356 Java API for WebSocket reference implementation.)

<dependency>
            <groupId>org.glassfish.tyrus.bundles</groupId>
            <artifactId>tyrus-standalone-client</artifactId>
            <version>2.0.0-M3</version>
 </dependency>

Creating a chat application using WebSocket


Here’s an application that uses WebSocket server endpoints with a JavaScript WebSocket client to send and receive messages. This particular example, called AcmeChat, uses Maven, but another build system such as Gradle would work just as well. This example will be deployed to Payara 5.202 running on Jakarta EE 9.

To follow along, you can clone the source code from GitHub.

The WebSocket endpoint. To begin, create a Maven web application and add the Jakarta EE 9 API dependency, along with any others that may be used, as shown in Listing 1. In this situation, you could also use the Jakarta EE Web Profile to make the application lighter.

Listing 1. Adding the Jakarta EE 9 API dependency

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.employeeevent</groupId>
    <artifactId>AcmeChat</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>war</packaging>
    <name>AcmeChat-1.0-SNAPSHOT</name>
    
    <properties>
        <maven.compiler.source>1.8</maven.compiler.source>
        <maven.compiler.target>1.8</maven.compiler.target>
        <endorsed.dir>${project.build.directory}/endorsed</endorsed.dir>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <failOnMissingWebXml>false</failOnMissingWebXml>
        <jakartaee>9.0.0-RC3</jakartaee>
    </properties>
    
    <dependencies>
        <dependency>
            <groupId>jakarta.platform</groupId>
            <artifactId>jakarta.jakartaee-api</artifactId>
            <version>${jakartaee}</version>
        </dependency>
        <dependency>
            <groupId>org.primefaces</groupId>
            <artifactId>primefaces</artifactId>
            <version>8.0</version>
        </dependency>
        <dependency>
            <groupId>org.glassfish.tyrus.bundles</groupId>
            <artifactId>tyrus-standalone-client</artifactId>
            <version>2.0.0-M3</version>
        </dependency>
    </dependencies>
    
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.1</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                    <compilerArguments>
                        <endorseddirs>${endorsed.dir}</endorseddirs>
                    </compilerArguments>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-war-plugin</artifactId>
                <version>2.3</version>
                <configuration>
                    <failOnMissingWebXml>false</failOnMissingWebXml>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-dependency-plugin</artifactId>
                <version>2.6</version>
                <executions>
                    <execution>
                        <phase>validate</phase>
                        <goals>
                            <goal>copy</goal>
                        </goals>
                        <configuration>
                            <outputDirectory>${endorsed.dir}</outputDirectory>
                            <silent>true</silent>
                            <artifactItems>
                                <artifactItem>
                                    <groupId>jakarta.platform</groupId>
                                    <artifactId>jakarta.jakartaee-api</artifactId>
                                    <version>${jakartaee}</version>
                                    <type>pom</type>
                                </artifactItem>
                            </artifactItems>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
</project>

Oracle Java Certification, Java Prep, Java Certification Exam, Java Tutorial and Materials, Java Career, Java Skills, Java Jobs
Next, create the WebSocket server endpoint class named com.employeeevent.acmechat.ChatEndpoint. The source code for this class is shown in Listing 2. Annotate the class with @ServerEndpoint and specify a URI path of "/chatEndpoint/{username}" for the value attribute. Note the path parameter that is enclosed in curly braces at the end of the URI. This allows the endpoint to accept a parameter. In this case, I will be sending a message that’s composed of a Java object. Therefore, I need to use an encoder and decoder to translate the message from the client to the server. I can specify an encoder and decoder via attributes of @ServerEndpoint.

Listing 2. Creating the WebSocket server endpoint class

package com.employeeevent.acmechat;

import jakarta.inject.Inject;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArraySet;
import jakarta.websocket.EncodeException;
import jakarta.websocket.OnClose;
import jakarta.websocket.OnError;
import jakarta.websocket.OnMessage;
import jakarta.websocket.OnOpen;
import jakarta.websocket.Session;
import jakarta.websocket.server.PathParam;
import jakarta.websocket.server.ServerEndpoint;

@ServerEndpoint(value = "/chatEndpoint/{username}",
        encoders = {MessageEncoder.class},
        decoders = {MessageDecoder.class})
public class ChatEndpoint {
    
    @Inject
    ChatSessionController chatSessionController;

    private static Session session;
    private static Set<Session> chatters = new CopyOnWriteArraySet<>();

    @OnOpen
    public void messageOpen(Session session,
            @PathParam("username") String username) throws IOException,
            EncodeException {
        this.session = session;
        Map<String,String> chatusers = chatSessionController.getUsers();
        chatusers.put(session.getId(), username);
        chatSessionController.setUsers(chatusers);
        chatters.add(session);
        Message message = new Message();
        message.setUsername(username);
        message.setMessage("Welcome " + username);
        broadcast(message);
    }

    @OnMessage
    public void messageReceiver(Session session,
            Message message) throws IOException, EncodeException {
        Map<String,String> chatusers = chatSessionController.getUsers();
        message.setUsername(chatusers.get(session.getId()));
        broadcast(message);
    }

    @OnClose
    public void close(Session session) {
        chatters.remove(session);
        Message message = new Message();
        Map<String,String> chatusers = chatSessionController.getUsers();
        String chatuser = chatusers.get(session.getId());
        message.setUsername(chatuser);
        chatusers.remove(chatuser);
        message.setMessage("Disconnected from server");

    }

    @OnError
    public void onError(Session session, Throwable throwable) {
        System.out.println("There has been an error with session " + session.getId());
    }

    private static void broadcast(Message message)
            throws IOException, EncodeException {
       
        chatters.forEach(session -> {
            synchronized (session) {
                try {
                    session.getBasicRemote().
                            sendObject(message);
                } catch (IOException | EncodeException e) {
                    e.printStackTrace();
                }
            }
        });
    }

}

Then, the endpoint class declares a field, identified as session, that’s used to hold the WebSocket Session and another Set<Session>, identified as chatters, to hold each of the connected chat user sessions. The class also injects an @ApplicationScoped controller class entitled ChatSessionController for storing users in a simple HashMap, which is shown in Listing 3.

Listing 3. Endpoint class declaring fields to hold the WebSocket session and chat user sessions

@Named
@ApplicationScoped
public class ChatSessionController implements java.io.Serializable {
    
    private Map<String, String> users = null;
    
    public ChatSessionController(){}
    
    @PostConstruct
    public void init(){
         users = new HashMap<>();
    }

    /**
     * @return the users
     */
    public Map<String, String> getUsers() {
        return users;
    }

    /**
     * @param for the users
     */
    public void setUsers(Map<String, String> users) {
        this.users = users;
    }
    
}

The ChatEndpoint class declares four methods for handling the WebSocket server events and a method named broadcast() that’s used to broadcast messages to each of the connected clients, all of which are described below:

private static void broadcast(Message message)
            throws IOException, EncodeException {
       
        chatters.forEach(session -> {
            synchronized (session) {
                try {
                    session.getBasicRemote().
                            sendObject(message);
                } catch (IOException | EncodeException e) {
                    e.printStackTrace();
                }
            }
        });
}

◉ The broadcast() method is private and static, and it accepts a Message object. The method simply traverses the set of chat sessions, stored within the chatters field, and within a synchronized block calls upon the getBasicRemote().sendObject() method for each session, sending the Message object.

◉ The messageOpen() method, annotated with @OnOpen, is executed when the connection is opened. The method accepts a Session and an @PathParam string, which accepts the username substitute variable that’s contained within the @ServerEndpoint value attribute. Next, the Session and username are both stored, and a Message object is constructed using the username and message text, and finally the message is broadcast via the invocation of the broadcast() method.

◉ The messageReceiver() method, annotated with @OnMessage, is executed when the WebSocket message is received. The method accepts a Session and Message; it uses the ChatSessionController to obtain the username of the user associated with the session and stores it in the Message object. The message is then broadcast by passing the Message to the broadcast() method.

◉ The close() method, annotated with @OnClose, is invoked when the connection is closed. This method accepts a Session, which is then removed from the Set of chatters, as well as the chatusers Map. The session is then used to obtain the corresponding username from the ChatSessionController, and it is stored in a new Message object, which is subsequently broadcast to alert the other chatters that the user has disconnected.

◉ The onError() method, annotated with @OnError, is invoked whenever one of the other annotated methods throws an exception. This WebSocket endpoint can accept messages from any WebSocket client, as long as the client has an active session with the endpoint. To communicate with the endpoint, the client will connect to the following URI: ws://<hostname>:<port>/AcmeChat/chatEndpoint.

The WebSocket client. You can write a client in a variety of languages and still have the ability to communicate with the WebSocket endpoint. In this example, I wrote the client in JavaScript and invoked it via a Jakarta Server Faces front end.

Look at Listing 4, which contains the source code for the client. Note that the body of the client is written in Jakarta Server Faces and uses PrimeFaces components for the user interface. The user interface contains an inputText field for the username, an inputTextarea for the message, and two commandButton widgets.

One of the commandButton widgets invokes a JavaScript function named chatRelay(), which opens a connection to the WebSocket. The other button invokes a JavaScript function named send() to send the message from the inputTextarea to the WebSocket endpoint.

Listing 4. Source code for the client

<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="http://xmlns.jcp.org/jsf/html"
      xmlns:p="http://primefaces.org/ui"
      xmlns:f="http://xmlns.jcp.org/jsf/core">
    <h:head>
        <script type="text/javascript">
                var ws;
                function chatRelay()
                {
                    var username = document.getElementById("chatForm:username").valueOf();

                    if ("WebSocket" in window)
                    {
                        var json = {
                            'username': username,
                            'message': ""
                        };

                        // Open WebSocket
                        ws = new WebSocket("ws://localhost:8080/AcmeChat/chatEndpoint/" + username.value);
                        ws.onopen = function ()
                        {
                            // Perform handling when connection is opened
                        };
                        ws.onmessage = function (evt)
                        {
                            var json = JSON.parse(evt.data);
                            var currentValue = document.getElementById('output').innerHTML;
                            document.getElementById('output').innerHTML =
                                    currentValue +
                                    '<br />' +
                                    json.username + ": " + json.message;

                        };

                        ws.onclose = function ()
                        {
                            // websocket is closed.
                            alert("Connection is closed...");
                        };

                    } else
                    {
                        // The browser doesn't support WebSocket
                        alert("WebSocket NOT supported by your Browser!");
                    }
                }

            function send() {
                var username = document.getElementById('chatForm:username').valueOf();
                var message = document.getElementById('chatForm:chatText').valueOf();
                var json = {
                    'username': username.value,
                    'message': message.value
                };
                ws.send(JSON.stringify(json));
                return false;
            }
        </script>
    </h:head>
    <h:body>
        <h:form id="chatForm">
            <h:outputLabel for="username" value="Username: "/>
            <p:inputText id="username" />
            <br/>
            <p:commandButton id="wsRelay" type="button" value="Connect"
                             onclick="chatRelay();" update="chatText,sendMessage"/>
            <br/><br/>

            <p:inputTextarea id="chatText" cols="30" rows="10" style="visibility: #{chatSessionController.users ne null? 'visible':'hidden'}"></p:inputTextarea>
            <br/><br/>

            <p:commandButton id="sendMessage" type="button" value="Send"
                             style="visibility: #{chatSessionController.users ne null? 'visible':'hidden'}"
                             onclick="send();"/>
        </h:form>
        <br/><br/>
        <div id="output"></div>
    </h:body>

</html>

To open a connection to the endpoint, the chatRelay() function accepts the username from the client. Next, it checks to ensure that the client’s browser will work with WebSockets and, if it won’t, a message is presented on the client. If the browser is compatible with WebSockets, a new JSON object is created, passing the username and message text. The WebSocket is then opened by passing the URI to the WebSocket endpoint and appending the username to be passed in as a path parameter, for example:

ws = new WebSocket("ws://localhost:8080/AcmeChat/chatEndpoint/" + username.value);

At this point, the WebSocket client is listening for responses from the server, and there are callback functions that await the server responses. The ws.onopen function, shown below, is invoked when the connection is opened, invoking any handling code that may be present:

ws.onopen = function ()
        {
  // Perform handling
        };

The ws.onmessage function, shown below, accepts an event parameter. The event is the message that has been received from the server endpoint. In this case, I used the JavaScript JSON API to parse the data and populate the chat screen with the incoming message text.

ws.onmessage = function (evt)
        {
            var json = JSON.parse(evt.data);
            var currentValue = document.getElementById('output').innerHTML;
            document.getElementById('output').innerHTML =
                    currentValue +
                    '<br />' +
                    json.username + ": " + json.message;

        };

The ws.onclose function, shown below, is invoked when the WebSocket server connection is disconnected, performing any processing code, as required. An example would be a case where the network connection was lost or the WebSocket endpoint was shut down. In such a case, the client could be alerted that the connection was closed.

ws.onclose = function ()
        {
            // websocket is closed.
            alert("Connection is closed...");
        };

Once the client session has been started and the WebSocket client is listening, any messages received from the WebSocket endpoint will be published via the ws.onmessage handler. The JavaScript send() function, shown below, is then used to send any messages that the user types into the inputTextarea to the server endpoint for broadcasting to any listening clients. The send() function creates a JSON object from the client username and message and sends it to the endpoint using the ws.send function, along with a little help from the JSON.stringify utility to help parse the JSON.

function send() {
    var username = document.getElementById('chatForm:username').valueOf();
    var message = document.getElementById('chatForm:chatText').valueOf();
    var json = {
        'username': username.value,
        'message': message.value
    };
    ws.send(JSON.stringify(json));
    return false;
}

Using this client configuration, two or more different clients can connect to the same WebSocket endpoint and communicate with each other in chat-room style.

The decoder and encoder. When the JavaScript client sends a message to the endpoint, it is in JSON format. The WebSocket endpoint accepts a plain old Java object named Message, which contains the username and message. The decoder and encoder classes transform the client-side messages to the server-side message object, and vice versa. The Jakarta WebSocket API makes it easy to develop decoders and encoders by simply implementing the Decoder or Encoder interfaces, respectively.

Listing 5 shows the Decoder class implementation, which is named MessageDecoder. This class decodes the client-side message into a Message object for processing by the WebSocket server. The interface uses generics to implement the decoder for the accepted Java object. The class overrides four methods: init(), willDecode(), decode(), and destroy().

Much like the WebSocket endpoint, the decoder is very much event-based. The init() method accepts an EndpointConfig object, and it is invoked when the message is sent from the client to the endpoint. The willDecode() method, which accepts a string-based message, is invoked next to return a boolean indicating whether the incoming message is in the correct format. If the message is in the correct format, the decode() method is invoked, again accepting a string-based message in JSON format, and the message is decoded into the Message object for processing via the endpoint. Lastly, the destroy() method is invoked when the client session becomes invalid.

Listing 5. The Decoder class implementation

package com.employeeevent.acmechat;

import java.io.StringReader;
import jakarta.json.Json;
import jakarta.json.JsonObject;
import jakarta.websocket.DecodeException;
import jakarta.websocket.Decoder;
import jakarta.websocket.EndpointConfig;

public class MessageDecoder implements Decoder.Text<Message> {

  @Override
  public Message decode(String jsonMessage) throws DecodeException {

    JsonObject jsonObject = Json
        .createReader(new StringReader(jsonMessage)).readObject();
    Message message = new Message();
    message.setUsername(jsonObject.getString("username"));
    message.setMessage(jsonObject.getString("message"));
    return message;

  }

  @Override
  public boolean willDecode(String jsonMessage) {
    try {
      // Check if incoming message is valid JSON
      Json.createReader(new StringReader(jsonMessage)).readObject();
      return true;
    } catch (Exception e) {
      return false;
    }
  }

  @Override
  public void init(EndpointConfig ec) {
    System.out.println("Initializing message decoder");
  }

  @Override
  public void destroy() {
    System.out.println("Destroyed message decoder");
  }

}

Listing 6 shows the Encoder class implementation, which is named MessageEncoder. This class encodes the server-side Message object to a JsonObject to be passed back to the client for processing. The interface uses generics to implement the encoder for the accepted Java object.

The class then overrides three methods: init(), encode(), and destroy(). Again, much like the WebSocket endpoint, the encoder is very much event-based in that the init() method accepts an EndpointConfig object, and it’s initiated once for each client session that is opened. The encode() method accepts the object being encoded, in this case Message, and performs processing to translate that object into JSON before it’s sent back to the client. Lastly, the destroy() method is invoked when the client session becomes invalid.

Listing 6. The Encoder class implementation

package com.employeeevent.acmechat;

import jakarta.json.Json;
import jakarta.json.JsonObject;
import jakarta.websocket.EncodeException;
import jakarta.websocket.Encoder;
import jakarta.websocket.EndpointConfig;

public class MessageEncoder implements Encoder.Text<Message> {

  @Override
  public String encode(Message message) throws EncodeException {

    JsonObject jsonObject = Json.createObjectBuilder()
        .add("username", message.getUsername())
        .add("message", message.getMessage()).build();
    return jsonObject.toString();

  }

  @Override
  public void init(EndpointConfig ec) {
    System.out.println("Initializing message encoder");
  }

    @Override
    public void destroy() {
        System.out.println("Destroying encoder...");
    }
    
}

The client endpoint


You can develop a client endpoint to communicate with a WebSocket server endpoint. The simplest client endpoint is a standard Java class that is annotated with @ClientEndpoint. You can see the full source code of a ClientEndpoint example in Listing 7.

Listing 7. Code for a client endpoint

@ClientEndpoint
public class BasicClient {
    
    Session session = null;
    private MessageHandler handler;
    
    public BasicClient(URI endpointURI) {
        try {
            WebSocketContainer container = ContainerProvider.getWebSocketContainer();
            container.connectToServer(this, endpointURI);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
    
    @OnOpen
    public void onOpen(Session session){
        this.session = session;
        try {
        session.getBasicRemote().sendText("Opening connection");
        } catch (IOException ex){
            System.out.println(ex);
        }
    }
    
    public void addMessageHandler(MessageHandler msgHandler) {
        this.handler = msgHandler;
    }
    
    @OnMessage
    public void processMessage(String message) {
        System.out.println("Received message in client: " + message);
    }
    
    public void sendMessage(String message) {
        try {
            this.session.getBasicRemote().sendText(message);
        } catch (IOException ex) {
            Logger.getLogger(BasicClient.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    
     public static interface MessageHandler {

        public void handleMessage(String message);
    }

In this example, the ClientEndpoint is named BasicClient. A Session and MessageHandler are declared within the class, and the constructor accepts a URI. Upon instantiation via the constructor, a ContainerProvider.getWebsocketContainer() is called to obtain a WebsocketContainer instance identified as container. The container.connectToServer() method is then invoked, passing the endpoint URI to instantiate the client connection.

The client contains a method named onOpen(), which is annotated with @OnOpen, and accepts a Session. This method is invoked when the ClientEndpoint connection is open, and it sets the session and then calls upon the getBasicRemote().sendText() method to send a message to the client to indicate the connection is open.

The client also contains a method named processMessage(), annotated with @OnMessage, which accepts a string. This method is called upon when a message is received from the ServerEndpoint. The client sendMessage() method also accepts a string, and it calls upon the session.getBasicRemote().sendText() method to send the message to the ServerEndpoint.

This particular example also contains an internal MessageHandler interface and an addMessageHandler() method, which are used to send the messages from the client. You can use the following code to work with the client:

// open websocket
final BasicClient clientEndPoint = new BasicClient(
        new URI("ws://localhost:8080/AcmeChat/basicEndpoint"));
// add listener
clientEndPoint.addMessageHandler(new BasicClient.MessageHandler() {
    public void handleMessage(String message) {
        System.out.println(message);
    }
});
// send message to websocket
clientEndPoint.sendMessage("Message sent from client!");

WebSocket customization


Sometimes you have a requirement to develop custom implementations, such as client/server handshake policies or state processing. For such cases, the ServerEndpointConfig.Configurator provides an option allowing you to create your own implementation. You can implement the following methods to provide customized configurations:

◉ getNegotiatedSubProtocol(List<String> supported, List<String> requested): Allows a customized algorithm to determine the selection of the subprotocol that’s used
◉ getNegotiatedExtensions(List<Extension> installed, List<Extension> requested): Allows a customized algorithm to determine the selection of the extensions that are used
◉ checkOrigin(String originHeaderValue): Allows the specification of an origin-checking algorithm
◉ modifyHandshake(ServerEndpointConfig sec, HandshakeRequest req, HandshakeResponse res): Allows for modification of the handshake response that’s sent back to the client
◉ getEndpointInstance(Class<T> endpointClass): Allows a customized implementation for the creation of an Endpoint instance

The same holds true for the ClientEndpoint.Configurator, in that the configurator allows for customization of some algorithms during the connection initialization phase. You can customize the configuration using these two methods:

◉ beforeRequest(Map<String, List<String>> headers): Allows for the modification of headers before a request is sent
◉ afterResponse(HandshakeResponse res): Allows for the customization of the processing for a handshake response

Source: oracle.com

Monday, December 26, 2022

Curly Braces #7: Complex math, BigDecimal, and infinity

Fortran has built-in functions for complex math. How do you handle that in Java?

My love for math has varied through the years and encompassed everything up to advanced math learned for a college physics class. I excelled—as long as I was applying the concepts to solve problems. As a software developer, I’ve had to leverage some of this background when working on financial systems early in my career and, more recently, on autonomous vehicle software.

Math is still a hobby to me, and I enjoy reading books on the subject. For instance, in a previous Curly Braces article about null, I mentioned the book Zero: The Biography of a Dangerous Idea, by Charles Seife. I also recently read The Calculus Wars, by Jason Bardi, and Descartes’s Secret Notebook, by Amir Aczel. Those led me to wonder about how Java compares to other languages, such as Fortran, when it comes to building math-intensive applications.

Comparing Java to Fortran

I dislike comparing languages, but for complex math operations it’s fair to compare Java to Fortran, because Fortran is often the choice for math- and science-based applications.

Fortran—short for Formula Translating System—is considered a general-purpose programming language, as it was in the 1950s when it was developed. This is a characteristic Fortran shares with Java.

Fortran was developed as a high-level and source-code-portable alternative to assembly language programming, which made Fortran more accessible and popular with those who were using computers primarily as a tool for another discipline (such as scientists, mathematicians, physicists, and so on). Dedicated programmers were more comfortable with assembly language than were scientists developing algorithms, for example, to help predict weather patterns. This one reason was enough for scientists and mathematicians to use Fortran.

It’s important to note that, unlike Java, Fortran has robust built-in primitive support for different types of numbers—including integers, real numbers, and complex numbers—and their operations. It was precisely the complex number data type that propelled Fortran for use in the sciences decades ago and, as a result, Fortran compilers were further optimized for these numerical use cases.

Complex numbers

If you do a search for the term complex math programming, a lot of information comes up related to the term complex numbers, and the concepts for these two terms are not the same. A complex number is one that contains real and imaginary elements, where the imaginary part contains i, the square root of -2. That is, i2 = -1. A complex number would be of the form a + bi.

Complex numbers, which are essential to algebra and calculus, help solve polynomial equations, and they are directly applicable to problems in the natural world, as well as to the laws of electricity and the world of electronics. Table 1 shows some examples of how Fortran makes programming complex numbers approachable.

Table 1. Examples of programming complex numbers in Fortran

Oracle Java, Oracle Java Programming, Oralce Java Career, Java Skills, Oracle Java Preparation, Java Guides, Java Tutorial and Material

In Fortran, once a complex number is created, you can easily operate on the complex number as a single value, or you can work on the real and imaginary parts individually, as follows:

real:: theta, modulus

complex:: z

modulus= cabs(z)

theta= atan(imag(z)/real(z))

Unlike Fortran, Java does not have native support for complex numbers, that is, numbers or variables that contain both a real and an imaginary component. Fortunately, there are libraries available that provide this functionality. The most comprehensive implementation I’ve found is in the Apache Commons Mathematical Library, which includes a Complex class to represent complex numbers and operations on them. It also provides ComplexUtils for conversion functionality and ComplexFormat to display complex numbers and operations.

Noncomplex math

This is all well and good, but what about math that doesn’t involve complex numbers but uses simple integers or floating-point numbers?

Java provides primitive types and classes for floating-point math: float and Float, as well as double and Double. Since the classes mainly wrap the primitives, while adding some additional functionality, they are equivalent in the way they store and operate on floating-point numbers. This is good in terms of consistency, but it means that neither set is ideal for precise floating-point math, which is required for monetary calculations.

Java’s floating-point arithmetic for float and double floating-point types conforms to the IEEE 754 standard for floating-point arithmetic. An issue is how the numbers are stored in binary format, and this results in unexpected imprecision in certain cases. For example, the following code doesn’t yield the expected output, which is 0.40:

System.out.println("1.00 - 0.60 = " + (1.00 - 6 * 0.10) );

Unfortunately, the output is 0.3999999999999999. I first ran into this issue in a financial application in certain cases when I saw $-0.00 as the output when the output should have been $0.00 for currency display. In those cases, the debugger showed the actual value as -0.000000000053518078857450746.

Joshua Bloch writes extensively about this issue in his book, Effective Java, but in brief, the problem derives from how float and double values are stored internally by the JVM.

Unlike int and long (and other types) that can be stored as exact binary representations of the numbers they’re assigned to, shortcuts are taken with the float and double types. Internally, Java stores values for these types with an inexact representation, using only a portion of the 64 bits for the significant digits. As a result, Java doesn’t store, calculate, or return the exact representation of the actual floating-point value. This seemingly intermittent behavior can be somewhat annoying because it becomes apparent only with specific combinations of numbers and operations. For someone concerned about high accuracy, of course, this situation can be more than merely annoying.

It’s important to note that this isn’t a deficiency in Java and isn’t unique to the JVM; you’ll find it whenever you’re dealing with the IEEE 754 style of floating-point values.

BigDecimal to the rescue. Java provides the java.math.* class, which includes a class called BigDecimal that can be used to alleviate the rounding and loss of precision issues that are often seen with floating-point arithmetic. BigDecimal lets you specify precisely how the rounding behavior should work using the java.math.MathContext class. For instance, the number of digits to be returned can be specified with the object as well. (Frank Kiwy wrote a nice article, “Four common pitfalls of the BigDecimal class and how to avoid them.”)

The following are some examples of using BigDecimal:

// The following code returns: 

// 1.5500000000000000444089209850062616169452667236328125

BigDecimal bd = new BigDecimal(1.55);

// The following code returns: 1.550000

BigDecimal bd = new BigDecimal(1.55, MathContext.DECIMAL32);

// The following code returns: 1.550000000000000

BigDecimal bd = new BigDecimal(1.55, MathContext.DECIMAL64);

In the example above, notice how the constructor allows you to specify the precision used to store and work with the floating-point value. Below is how to specify rounding, which must occur when the exact value cannot be represented with the precision used. Note that the scale of the BigDecimal floating-point value indicates the number of digits to the right of the decimal point.

Both of the following code samples return 1.55:

BigDecimal bd = new BigDecimal(1.55, MathContext.DECIMAL32);

bd = bd.setScale(2);

BigDecimal bd = new BigDecimal(1.55, MathContext.DECIMAL64);

bd = bd.setScale(2);

However, the following throws an exception indicating that rounding is necessary:

BigDecimal bd = new BigDecimal(1.55);

bd = bd.setScale(2);

There are multiple rounding types (to round up, round down, use ceiling or floor operators, and so on), and you can specify the rounding type as the second parameter when you set the scale as the first parameter.

BigDecimal bd = new BigDecimal(1.55);

bd = bd.setScale(2, BigDecimal.ROUND_DOWN);

In each of the examples above, notice that bd is reassigned after the call to setScale. This is done because BigDecimal is immutable; therefore, calling setScale on a BigDecimal object has no effect. This goes for arithmetic operations also; to illustrate, the following example shows floating-point operations that yield unexpected currency results:

double a = 106838.81;

double b = 263970.96;

double c = 879.35;

double d = 366790.80;

double total = 0;

total += a;

total += b;

total -= c;

total -= d;

At the end of this operation, the expected value is 3139.62, but instead the result is 3139.6200000000536.

BigDecimal can deliver the expected results.

BigDecimal total = new BigDecimal(0, MathContext.DECIMAL64);

total = total.setScale(2);

total = total.add(new BigDecimal(a, MathContext.DECIMAL64));

total = total.add(new BigDecimal(b, MathContext.DECIMAL64));

total = total.subtract(new BigDecimal(c, MathContext.DECIMAL64));

total = total.subtract(new BigDecimal(d, MathContext.DECIMAL64));

In the example above, the precision is set to 64 bits, and the scale is set to 2 to adequately represent currency values. The results of calls to add and subtract are reassigned to the original BigDecimal object since it’s immutable. You can avoid the verbose code (and the typing required) to set the precision and scale and instead use helper methods using this code instead:

private BigDecimal doubleToBD32(double val) {

  return new BigDecimal(val, MathContext.DECIMAL64).setScale(2);

}

private BigDecimal doubleToBD64(double val) {

  return new BigDecimal(val, MathContext.DECIMAL64).setScale(2);

}

private BigDecimal doubleToBD128(double val) {

  return new BigDecimal(val, MathContext.DECIMAL128).setScale(2);

}

private BigDecimal doubleToBD(double val) {

  return new BigDecimal(val, MathContext.UNLIMITED).setScale(2);

}

The final code looks like the following, which is more pleasing to me:

double a = 106838.81;

double b = 263970.96;

double c = 879.35;

double d = 366790.80;

BigDecimal total = doubleToBD64(0);

total = total.add( doubleToBD64(a) );

total = total.add( doubleToBD64(b) );

total = total.subtract( doubleToBD64(c) );

total = total.subtract( doubleToBD64(d) );

Although there are many, many more details about using BigDecimal and MathContext (and the entire java.math package, for that matter), this quick overview should help if you ever get surprised by Java’s binary representation of floating-point and double floating-point numbers and arithmetic operations.

The road to infinity

Fortran has native support for infinity, which is useful when you’re working with real numbers. For example, you can assign (and later check for) positive and negative infinity.

real :: a, b

a = -infinity

b = +infinity

Did you know that Java can work with infinities using the Float or Double classes?

double a = Double.POSITIVE_INFINITY;

double b = Double.NEGATIVE_INFINITY;

System.out.println("a="+a+", b="+b);

Source: oracle.com

Wednesday, December 21, 2022

Quiz yourself: Lambda expressions and local variables in Java


Given the following Capture class

Oralce Java, Java Exam Prep, Java Tutorial and Materials, Java Certification, Java Learning, Java Prep, Java Preparation, Oracle Java Lambda

import java.util.function.Supplier;
public class Capture {
    public static void main(String[] args) {
        System.out.println(supp1().get() + supp2().get());
    }
    static Supplier<String> supp1() {
        var val = "Supp 1 ";
        Supplier<String> s = (() -> { return val; });
        val = "Supp 1 New ";
        return s;
    }
    static Supplier<String> supp2() {
        var val = new StringBuilder("Supp 2 ");
        Supplier<String> s = (() -> { return val.toString(); });
        val.append("New");
        return s;
    }
}

What is the result? Choose one.

A. Supp 1 Supp 2 is the result.
B. Supp 1 Supp 2 New is the result.
C. Supp 1 New Supp 2 New is the result.
D. Compilation fails due to supp1() method.
E. Compilation fails due to supp2() method.

Answer. This question covers several topics including implementing the Supplier interface, the syntax of lambda expressions, the concept and syntax of a closure, and mutation of values in closures. As is often the case with seemingly complex questions in an exam, only one of these topics is crucial to finding the correct answer.

The exam question has two options that suggest that the code might fail to compile, so you need to bear that in mind, because if compilation fails, there is no output.

First, in the main method, notice that the code invokes the get() methods of the objects returned from two functions: supp1() and supp2(). These functions both return Supplier<String>, which is a functional interface with the single abstract method String get(). So, at this point, it seems that the output, if any, would in fact be the concatenation of the text returned by those two Supplier objects.

Next, consider the bodies of the supp1() and supp2() methods. Both return a lambda expression, and broadly speaking, the lambdas are structurally correct for implementing the Supplier<String> interface. Both lambdas take zero arguments, and they both return a String, which makes them consistent with the signature of the get() method required for a Supplier<String>.

On closer inspection, you should notice that both lambdas use variables that are local to their surrounding method. Of course, the lifetime of a method’s local variable is normally limited to the duration of that method’s execution, but the objects created by the lambda have lifetimes that exceed that; they are objects, so they exist at least as long as they are reachable.

Because the objects represented by the lambdas are returned from the methods, it’s guaranteed that their lifetime exceeds that of a normal local variable in the method that creates them. That raises a philosophical question: What is the value of a variable that doesn’t exist? That is, if the lambda is executed after the return from the method that created that lambda, what value will be found for the local variable enclosed in the lambda?

Java provides a simple solution to this: In effect, Java embeds a copy of the value of the local variable into the object represented by the lambda. (Note that this discussion doesn’t necessarily reflect how the implementation is performed but describes the effect as seen from the source language. Lambdas have several different approaches to efficient implementation.)

Well, a copy of a variable is somewhat dangerous, because if either of the copies were to change, the two become inconsistent, so which should now be considered valid? Java solves this by insisting that any variable that’s used in this way must never be changed. (Prior to Java 8, such a variable could be used in an anonymous inner class provided it was marked final. Beginning with Java 8 and the advent of lambda syntax, it’s sufficient that the variable be effectively final, that is, the variable must never be reassigned.)

In the case of supp2(), the variable val is, in fact, not reassigned. The object to which it refers (a StringBuilder) is mutated, but that’s not the same as reassigning the reference variable in the variable itself. So, this method is valid, and it would return the text Supp 2 New if the resulting Supplier were accessed.

However, in the case of supp1(), the variable val (the one referred to in the lambda) is reassigned. This is prohibited, and the code for supp1() would not compile. Based on this observation, you can see that option D is correct, and options A, B, C, and E must be incorrect.

Here are a couple of side notes.

First, the idea of capturing a local variable into a lambda expression (or anonymous inner object) is often referred to—in Java and other languages—as a closure. Some people object to the restriction that the value must never be changed, but in practice this is not really a problem because you can always modify the code to use a final reference to a mutable data item, as shown in supp2(). If you need to use a primitive value, simply using an array of a single element solves the problem.

Second, some people feel the requirement for final or effectively final values in closures results in something that is not really a valid closure. However, keep in mind that a closure is primarily a functional programming concept, and in the purest forms of functional programming, all data is immutable anyway. Indeed, that kind of side effect is prone to creating difficult-to-identify bugs.

Conclusion. The correct answer is option D.

Source: oracle.com

Monday, December 19, 2022

Capitalize English Titles With Java, Quarkus & GraalVM

Over the years, I’ve been writing a lot in English, which isn’t my mother tongue, and for writing titles (headlines, article titles, etc.) I always have to think which words to capitalize. Thus, I’ve created a small tool, of course written in Java, that I use as command line tool, powered by Quarkus, Picocli, and GraalVM.

What I want to have is a method that takes a single line or multiple lines and capitalizes them. For my writing, I use the APA style. You can check out different ways of capitalization in online tools such as this one.

I’ve created a small Java project that uses Quarkus, Picocli, and GraalVM to run the code as standalone application on the command line. Especially with native executables built by GraalVM, we can speed up the time the command line program takes. While the JAR/JVM version takes a few hundred milliseconds to complete, the native executable runs pretty much instant. For command line tools, you will notice a difference.

You can find the code on GitHub.

The CapitalizeCommand class can run the whole thing as command line program. If you want to build the project yourself, you simply need to clone and execute mvn package -P native and copy the resulting target/captalize-title-runner executable into your $PATH.

The produced executable works as follows:

Core Java Certification, Java Career, Java Skills, Java Jobs, Java Prep, Java Preparation, Java Tutorial and Materials

You can see some of the particularities when capitalizing titles, with regards to prepositions, hyphenated words, etc. It’s also important to mention that a simple software solution that doesn’t perform any Grammar analysis won’t always produce correct results since some words might occur differently as prepositions, conjunctions, adjective, or adverbs (e.g. yet, out, so, etc). But for my purposes, this solution is good enough ™.

It’s also very nice to include the command in my usual writing editor, Vim in my case. The command ignores trailing special characters, which makes it easy to invoke it in formats such as AsciiDoc:

Core Java Certification, Java Career, Java Skills, Java Jobs, Java Prep, Java Preparation, Java Tutorial and Materials

For Vim, you can trigger the tool as follows:

" in your .vimrc
nmap <leader>c :.!capitalize-title -<CR>
vnoremap <leader>c :.!capitalize-title -<CR>

Source: javacodegeeks.com

Friday, December 16, 2022

Quiz yourself: Sealed and non-sealed classes and interfaces

Oracle Java, Java Certification, Java Career, Oracle Java Preparation, Java Tutorial and Material, Oracle Java Tutorial and Material


Given the following code

sealed interface Super permits Sub1, Sub2, Sub3 {}
non-sealed class Sub1 implements Super {}
record Sub2(Super s) implements Super {}
enum Sub3 implements Super { SUB_A{}, SUB_B{} }

Which statement is true? Choose one.

A. The code compiles as it is.
B. The record Sub2 is invalid.
C. The enum Sub3 is invalid.
D. Both record Sub2 and enum Sub3 are invalid.

Answer. The purpose of sealed types is to let you limit the set of types that can be assignment-compatible with a particular base type.

A common example of inheritance would be a base type Animal with child classes for Lion, Tiger, Bear, and so on. This structure makes perfect sense if you decide to add Snake, Dog, Bird, and any number of additional types that are subtypes of Animal.

Sometimes, however, you might need to model a situation where there are a limited number of possible or permissible subtypes. An example would be a base type Quadrilateral (four-sided geometric shape). The possible subtypes of this would be Square, Rectangle, Parallelogram, Trapezium, Trapezoid, Rhombus, and Kite. (Let’s not get picky about the geometric definitions, as we recall that two of those have different meanings on opposite sides of the Atlantic Ocean!)

There are good reasons (some of which are related to the form of switch/case construction that’s a preview feature in Java 17 and Java 18) why it can be helpful to the integrity of the design to be able to enforce this restriction and not allow any additional subtypes. It’s beyond this article’s scope to discuss those reasons, but it’s sufficient to know that the sealed class syntax is provided to allow you to enforce this.

To make the sealed class syntax work, the syntax must allow you to define a base type (which can be an interface, abstract class, or concrete class) and enumerate the permitted subtypes. Further, the syntax must enforce controls on the subtypes of those subtypes. This goal suggests the following rules:

◉ A sealed type must (generally—but this article will not get into the exception) explicitly declare the permitted subtypes.
◉ Each permitted subtype must either be sealed or final (in which case it cannot have any subtypes).
◉ It’s also permitted to have a subtype that’s explicitly declared as non-sealed, in which case it allows any number of subtypes.

All sealed classes have some interactions with existing type rules. For example, abstract classes and interfaces cannot be final, so that possibility is removed and either of them can be sealed or non-sealed, but they cannot be final.

You can probably guess that enums, which prohibit arbitrary subclassing, and record types, which are implicitly final, also interact with these rules. That will be explored shortly.

Now that you’ve got a good starting point, consider the elements presented in this question.

The declaration of Super is valid. An interface can be sealed, the syntax is good, and the declaration enumerates the three permitted subtypes: Sub1, Sub2, and Sub3.

The declaration of the class Sub1 is also valid. It demonstrates the use of the non-sealed idea. It’s permitted to have arbitrary subtypes, and it’s explicit about that. Thus it is correct.

The record Sub2 declaration is also valid. It’s not labeled final, sealed, or non-sealed, but a record is implicitly final, so it’s acceptable in that respect. Also, although a record cannot declare a parent class, it’s fine for a record to implement an interface, as this one does. You could add the final modifier explicitly, but that is not required, so the code for Sub2 compiles.

The Sub3 enum is interesting. Unlike the record Sub1, an enum is not necessarily final. It’s true that you can’t use extends from an enum, but you can create subclasses if you do so inside the enum itself—and the code in the question does just that.

Both the constant values SUB_A and SUB_B are followed with curly brace pairs, and this means that each instance is a subclass of the Sub3 enum. Because the subtypes of an enum are so tightly controlled, the Java Language Specification treats an enum that contains children in this way as being implicitly sealed. Section 8.9 of the specification for Java 17 says

An enum class is either implicitly final or implicitly sealed, as follows:

◉ An enum class is implicitly final if its declaration contains no enum constants that have a class body.
◉ An enum class E is implicitly sealed if its declaration contains at least one enum constant that has a class body. The permitted direct subclasses of E are the anonymous classes implicitly declared by the enum constants that have a class body.

In the situation where no class bodies were provided for any of the individual enum constants, the enum class would be implicitly final and would work too.

Because the Sub1 class is explicitly marked non-sealed, the record Sub2 is implicitly final, and the enum Sub3 is implicitly sealed, all three are valid and the syntax of the code presented is entirely correct and compiles. In view of that, you can see that option A is correct, and therefore options B, C, and D must be incorrect.

Conclusion. The correct answer is option A.

Source: oracle.com

Wednesday, December 14, 2022

Nothing is better than the Optional type. Really. Nothing is better.


JDK 8 introduced the Optional class, a container that is either empty or contains a non-null value.

Core Java, Java Exam, Java Exam Prep, Java Tutorial and Materials, Java Guides, Java Skills, Java Jobs

Optional has numerous problems without countervailing benefits. It does not make your code more correct or robust. There is a real problem that Optional tries to solve, and this article shows a better way to solve it. Therefore, you are better off using a regular and possibly null Java reference rather than Optional.

The web and blogosphere are full of claims that the Optional class solves the problem of null pointer exceptions. This is not true. Changing your code to use the Optional class has the following negative effects:

◉ Optional transforms a NullPointerException into a NoSuchElementException, which still crashes your program.
◉ Optional creates new problems that were not a danger before.
◉ Optional clutters your code.
◉ Optional adds space, time, and coding overhead.

When your code throws a NullPointerException or NoSuchElementException, the underlying logic bug is that you forgot to check all possibilities when processing data. It’s best to use a tool that guarantees you don’t forget. That helps you to understand and fix the underlying problem.

(These criticisms aren’t specific to Java’s implementation of Optional. Other languages that claim to have solved the null pointer problem have also merely transformed it into a different manifestation.)

To be clear, the Optional class isn’t all bad: If you need to use Optional, it defines methods for reducing code clutter when dealing with possibly present data. However, you should still avoid the Optional class.

The rest of this article expands on the points raised above.

Changing the exception that is thrown doesn’t fix the defect or improve the code


Consider the following Cartesian point class with fields x and y (this discussion is equally applicable to getter methods):

class Point { int x; int y; }

Because a Java reference may be null, there is a danger of a NullPointerException whenever you dereference it, as in myPoint.x below.

Point myPoint;
...
... myPoint.x ...

If myPoint is null and does not refer to a real point, then myPoint.x throws a NullPointerException and the program crashes. The following is a way to write this code using Optional:

Point myPoint;
Optional<Point> myOPoint = Optional.ofNullable(myPoint);
...
... myOPoint.get().x ...

If myOPoint does not contain a real point, then myOPoint.get().x throws a NoSuchElementException and the program crashes. This isn’t any better than the original code, because the programmer’s goal is to prevent all crashes, not just NullPointerException crashes!

It is possible to prevent the exception and crash by doing a check first.

if (myPoint != null) {
  ... myPoint.x ...
}

or

if (myOPoint.isPresent()) {
  ... myOPoint.get().x ...
}

Again, the code is very similar and Optional is not superior to using a regular Java reference.

Optional is prone to misuse


Optional is a Java class; therefore, the variable myOPoint of type Optional<Point> might be null. Thus, the expression myOPoint.get() might throw a NullPointerException or a NoSuchElementException! You really need to write the following:

if (myOPoint != null && myOPoint.isPresent()) {
  ... myOPoint.get().x ...
}

You can express complex data using the distinction between a null Optional value, a non-null Optional with data absent, and a non-null Optional with data present, but this is complex and confusing. Alternatively, you could decide to forgo those possibilities and to be careful and disciplined about not letting variables of type Optional be null. However, if you trusted yourself about that, you wouldn’t have had any null pointer exceptions in the first place and you wouldn’t be using Optional.

Optional is a wrapper, so uses of value-sensitive operations are error-prone, including reference equality checks (==), identity hash codes, and synchronization. You need to remember to not use these.

This isn’t a new issue: Back in 2016, Stuart Marks provided a longer list of rules to avoid mistakes in the use of Optional.

Optional clutters your code


With the Optional library, your code is more verbose, as shown in the following examples:

◉ Code for type names: Optional<Point> versus Point
◉ Code for checking a value: myOPoint.isPresent() versus myPoint == null
◉ Code for accessing data: myOPoint.get().x versus myPoint.x

None of these is a deal-breaker alone, but overall, Optional is cumbersome and ugly to use. For some concrete examples, see the Code Project blog’s “Why we should love ‘null’” post and search for the word cumbersome.

Optional introduces overhead


Optional introduces space overhead: An Optional is a separate object that consumes extra memory.

Optional introduces time overhead: Its data must be accessed via an extra indirection, and calling methods on it is more expensive than Java’s efficient test for null.

Optional introduces coding overhead: You must deal with Optional’s incompatibility with existing interfaces that use null, with the fact that it is not serializable, and so on.

The real problem: Remembering to perform checks


A NullPointerException or NoSuchElementException occurs because the programmer forgot to perform a check to see if data is present, via != null or .isPresent(), before trying to use the data.

Many people say that the main benefit of Optional is that with Optional, you are less likely to forget to perform the check. If true, that is good! Nonetheless, it’s not enough to make a problem somewhat less likely, in the few places where Optional is written. It is better to have a guarantee that eliminates the problem everywhere.

One way would be to force the programmer to always perform the check before accessing the data. (This is what some programming languages do, by offering a destructuring or pattern-match operator.) This would result in many redundant checks in places where the check has already been performed or it is not really needed. (As an analogy, think about how some programmers react to checked exceptions, which force the programmer to do a check whether the programmer wants to do it or not.)

A better approach is to have a tool that guarantees that you do not forget to check but that also doesn’t require redundant checks. Luckily, such tools exist; examples include Nullness Checker of the Checker Framework, NullAway, and Infer. (Note: I am the creator of the Checker Framework.)

As an example, consider Nullness Checker. It works at compile time, and it examines every dereference in your program and requires that the receiver is known to be non-null. That could be because you have already checked it or because it was generated by a source that never produces null.

Nullness Checker uses powerful analysis to keep track of whether a reference might be null. By comparison to the use of Optional, this reduces the number of warnings and the number of redundant checks that are needed. By default, Nullness Checker assumes that references are non-null, but you can specify possibly missing data by writing @Nullable, as in the type @Nullable Point.

Writing @Nullable Point is analogous to Optional<Point>, but with the following significant benefits:

◉ There is less clutter, because you write the @Nullable annotation on fields and method signatures—typically not within method bodies.
◉ It is compatible with existing Java code and libraries. There is no need to change your code to call methods of Optional. There is no need to change interfaces and clients to use the Optional type and no need to convert between Optional instances and regular references.
◉ There is no runtime overhead.
◉ You get a compile-time guarantee or a warning, never a runtime crash.
◉ The code is better documented. If Optional is not present on a type, you don’t know whether the programmer forgot it, Optional could not be written because of backward compatibility, or the data is really always present. With the static analysis of Nullness Checker, the annotations are machine-checked at compile time, so the program has @Nullable on every reference that might be null.
◉ You get guarantees about sources of null pointer exceptions, such as partially initialized objects and calls to Map.get, that Optional is not applicable to. It can also express method preconditions, which are useful for fields containing possibly missing data.

Nullness Checker achieves the goal of guaranteeing that you never forget to check for the presence of data, anywhere in your code, in a way that is less disruptive than the use of Optional. Other tools such as NullAway and Infer give similar guarantees with different trade-offs.

Since every programmer error related to null references is possible with Optional, and Optional makes new types of errors possible, programmers need support to avoid making all those errors. The Checker Framework also contains a compile-time Optional Checker that does exactly that, and it is useful if you need to use Optional (such as to interface with a library that uses Optional).

Optional’s handy methods


Although Optional tends to clutter your code, if you need to use Optional, it provides methods that reduce the clutter. Here are two examples.

◉ Its orElse method returns the value if it is present, or else it returns a default value.
◉ Its map method abstracts the pattern by doing the following:
  ◉ It takes as input a value.
  ◉ If the value is null, it returns null.
  ◉ Otherwise, it applies a function to the value and returns the result.

There are libraries that do the exact same things for regular Java references. An example is the Opt class that is distributed with the Checker Framework. For each instance method in Optional, the Opt class includes a static method.

Other methods such as filter and flatMap are described in the API documentation for Optional. These eliminate much of the need for calling Optional.isPresent() and Optional.get(), which is a great benefit. However, they don’t eliminate all the need, and the other disadvantages of Optional remain.

Counterarguments


Not everyone claims that Optional solves the problem underlying null pointer exceptions. For instance, Oracle’s JDK team does not claim this.

A general programming rule is to avoid, as much as possible, the situation that data is not present. This reduces the need to write a type such as Optional<Point> or @Nullable Point. All the arguments in this article continue to hold wherever in your program data might not be present.

Some people suggest (see Stuart Marks’ presentation on bikesheds) that programmers should use Optional sparingly, such as only on method return types and never on fields. If you use Optional less, there is less clutter, overhead, and potential for misuse. However, the only way to eliminate Optional’s problems is to not use it. Furthermore, if you use Optional less, you obtain fewer of its benefits. Null pointer exceptions are important no matter what their source or syntactic form, so the best solution is one that handles every reference in your program, not just some of them.

The main argument for Optional on return types is, “It’s too easy for clients to forget to handle the possibility of a null return value. Optional is ugly and in your face, so a client is less likely to forget to handle the possibility of an empty return value. Everywhere else, programmers should continue to use normal references, which might be null.” By contrast, my suggestion is that programmers should continue to use normal references everywhere in their program but use a tool to ensure that at every possible location—not just at method calls—the program checks against null when needed.

If you find a style of using Optional that solves part of your problems, at an acceptable cost to you, then good for you—use it.

Source: oracle.com