create account

Java™ - Best Practice for Remote Communication with Java RMI by murez-nst

View this thread on: hive.blogpeakd.comecency.com
· @murez-nst · (edited)
$68.66
Java™ - Best Practice for Remote Communication with Java RMI
#### Repository
https://github.com/JetBrains/intellij-community

#### What Will I Learn?
- You will learn about Java™ RMI API
- You will learn about JavaFX
- You will learn about Java™ Generics (Abstract types)
- You will learn how to simultaneously perform multiple tasks (concurrent programming) and synchronization
- You will learn how to invoke a method regardless of its declaration (Reflection) during the runtime
- You will learn how to create the service more flexible by always providing connection to the database during the runtime

#### Requirements
- The latest version of [Java™ SE Development Kit](http://www.oracle.com/technetwork/java/javase/downloads/index.html)
- [IntelliJ IDEA Community](https://www.jetbrains.com/idea/download/)
- [MySQL Community](https://dev.mysql.com/downloads/)
- Localhost server, such as [XAMPP](https://www.apachefriends.org/download.html), [MAMP](https://www.mamp.info/en/downloads/) or many others

And this is my current system states:
![Untitled 1.png](https://cdn.steemitimages.com/DQmYPTTQmMW8zQHLv4AFQaqxcnmGS243nHe548mrop4gKrE/Untitled%201.png)

#### Difficulty
- Advanced

#### <center>Tutorial Contents</center>
___

If you are looking for the same related tutorial about Java RMI, there are so many articles, and you will get stuck and can not even see the programs you have created based on those tutorials running properly. In fact, Java RMI is very easy, but I will make this tutorial more powerful and flexible. So, later on it will be more easily extended and maintainable.

Normally, you as a developer will use standard protocols (most commonly: HTTP, SMTP, FTP or SOAP) to exchange data between systems or machines with others. The main thing that underlies this problem is the **compatibility**.
For example, a browser needs data from a machine which runs Java programs. When the data has been received by the browser, it does not understand the Java *byte-code* or object (instance of the class) in the context of Java, because it understands JavaScript only. Thus, they agree on the exchange of data that can be understood by each of them, i.e. text, but by following certain rules to distinguish them from the plain text. So as of now, we already know XML, JSON or something else, and the protocol is HTTP/HTTPS.

Fortunately, we can exchange data between two separate machines without having to follow the existing standard rules, using Remote Method Invocation (RMI) in the term of Java programming language or better known in other programming languages as Remote Procedure Call (RPC), as simple as regular method invocations. And even more sophisticated, we can handle exceptions over the network.
Of course, RMI can be applied if the machines communicate with each other installed the Java Virtual Machine.

> RMI/RPC is commonly applied to distributed systems

##### Overview

There will be two systems that will communicate with each other. They are **Timeless**, as the server that authenticates users through the given parameters of the username and password, so it provides a *remote object* that has a **signin** method which then can be called by other systems remotely.
And **ChitChat**, which only focus as user interface and will send the username and password to the remote object provided by the server. But not only that, it will also send a *callback* reference (as the name bound to the remote object). So once the server has finished authenticating the user in a session, the server will invoke the *callback* method and send the response through its parameters.
The advantage of using this concept is, it does not necessarily have to create a separate thread to wait for an unpredictable authentication process.

The whole series of authentication is summarized in the following figure,

![RMI Authentication.png](https://cdn.steemitimages.com/DQmf7XiKhyPoTToRwVZT8pFkA59WQ5jV5Mx5G1eE8sxss2W/RMI%20Authentication.png)
<center>User Authentication via Java™ RMI</center>

1. Database Setup
2. Build up **Timeless** Project — As the server
   1. Create **DataPackage** — As entity class to store data
   2. Create **Authenticable** — Interface in which the instance can be a remote object for authentication
   3. Create **Executor** — Abstract thread can perform any task in general
   4. Create **SQLMaintainable** — Interface to get maintainable database connection
   5. Create **Processor** — As the implementation of `Executor` for authentication
   6. Create **RemoteAuthenticator** — As the implementation of `Authenticable`
   7. Create **Main** — As the launcher of this system and manager of client requests
3. Build up **ChitChat** Project — As the client.
   1. Create **EventListenable** — Interface in which the instance can be a remote object for callback reference
   2. Create **SigninPane** — As the provider of Signin layout
   3. Create **SigninPane.EventImplementer** — As the implementation of `EventListenable`
   4. Create **Main** — As launcher of this system
4. Deployment and Running up

<center>1. Database Setup</center>
___

- Login as **root** and enter your password
`mysql -u root -p`

- Create database **Murez**
`CREATE DATABASE Murez`

- Create table of **User**
<code>CREATE TABLE &#96;User&#96;(
&#96;Email&#96; VARCHAR(128) PRIMARY KEY NOT NULL,
&#96;Password&#96; VARCHAR(256) NOT NULL,
&#96;Name&#96; VARCHAR(256) NOT NULL,
&#96;Gender&#96; ENUM('1', '0') NOT NULL,
&#96;Birthday&#96; DATE NOT NULL,
&#96;Status&#96; TEXT)</code>

- Insert one record for testing
<code>INSERT INTO &#96;User&#96; VALUE("murez.nasution@gmail.com",SHA1("myPass987"),"Murez&#160;Nasution","1","1989-09-29",NULL)</code>

- Create a new database user as Timeless for login
`CREATE User 'Timeless'@'localhost' IDENTIFIED BY 'myPass123'`

- Grant access to user of **Timeless**
`GRANT ALL ON Murez.* TO 'Tester'@'localhost'`

Now the **Timeless** app has gained access to the database by using:
Username: "`Timeless`", and Password: "`myPass123`".
And also we can sign in to the system via **ChitChat** app by using:
Username: "`murez.nasution@gmail.com`", and Password: "`myPass987`".

More details for database setup can be learned [here](https://steemit.com/utopian-io/@murez-nst/mureztutorial002) on section **(1)** *Settings up Database*
<br />
<center>2. Build up **Timeless** Project</center>
___
* Open your IntelliJ IDEA.
* On the **Welcome to IntelliJ IDEA** dialog, select **Create New Project**.
Then will appear *New Project* dialog.
* On the left panel, select **Java** and then check **Kotlin/JVM**. Click **Next**.
* Change your **Project location** to **D:\Murez\Home\Project\Java\\**, and then type "**Timeless**" on the **Project name**.
* Finally click **Finish**.

And here is the project structure that we will create,
<center>![Untitled 6.png](https://cdn.steemitimages.com/DQmNU2u7Fb8mdCBZyeQu4XxZuF94B94HuZ9VjA5DQYWnd6C/Untitled%206.png)</center>
<br />
<center>2.1. Create **DataPackage**</center>
___

Actually, we can use the key-value pairs in which all objects that implement <code>[java.util.Map](https://docs.oracle.com/javase/8/docs/api/java/util/Map.html)</code> interface as alternative data structure which is flexible for various conditions, but here we need to read the simple one directly without having to search to the pairs. Thus, we provide two variables as options that can store numbers or string in addition to the `PACKAGE` (pairs of the keys map to the values).

Furthermore, we want to give flexibility to the next developers to decide for themselves the suitable type of the values mapped to the keys. In this case we can assume them as **`T`**, which **`T`** is an abstract type (Generic) that can be substituted by various types (actually classes). A class whose members have an abstract type (especially field, since the method can have its own generic) must declare it as a parameter, as in the following figure,

![Untitled 2.png](https://cdn.steemitimages.com/DQmcVEo2hpjprbw5PEXgBHU3FAh1dyAZTJ74pZKzSQxPDAM/Untitled%202.png)

If we pass the value of `String`, then we will get the value of `getPackage` as the pairs of the keys map to the value of String as well.
<code>
Map<String, String> pairs = new DataPackage<<b>String</b>>(0, "", new HashMap<>()).getPackage();
</code>
or,
<code>
Map<String, String> pairs = new DataPackage<>(0, "", new HashMap<String, <b>String</b>>()).getPackage();
</code>
For more flexible functionality, we will also provide a static method that will returns the instance of `DataPackage<String>` with the default implementation of the key-value pairs as <code>[java.util.HashMap](https://docs.oracle.com/javase/8/docs/api/java/util/HashMap.html)</code>.
And the following is the source codes of `DataPackage` class,

```
package com.murez.entity;

import java.util.Map;

public class DataPackage<T> implements java.io.Serializable {
    private final Number N;
    private final String S;
    private final Map<String, T> PACKAGE;

    public DataPackage(Number n, String s, Map<String, T> instance) {
        N = n;
        S = s;
        if((PACKAGE = instance) == null)
            throw new IllegalArgumentException();
    }

    public static DataPackage<String> create(Number n, String s) {
        return new DataPackage<>(n, s, new java.util.HashMap<>());
    }

    public final Number getNumber(Number defaultValue) { return N == null? defaultValue : N; }

    public final String getString(String defaultValue) { return S != null && S.length() > 0? S: defaultValue; }

    public Map<String, T> getPackage() { return PACKAGE; }
}
```
<br />
<center>2.2. Create **Authenticable**</center>
___
As explained earlier, on the server side we need to provide a remote object that has a `signin` method to authenticate the users.
In Java, all remote objects must be implementations of the <code>[java.rmi.Remote](https://docs.oracle.com/javase/10/docs/api/java/rmi/Remote.html)</code> interface or their sub-classes. And the method that should be visible to the client to be invoked remotely must be declared by throwing <code>[java.rmi.RemoteException](https://docs.oracle.com/javase/10/docs/api/java/rmi/RemoteException.html)</code> exception. As simple as the following,

```
package com.murez.remote;

import com.murez.entity.DataPackage;

public interface Authenticable extends java.rmi.Remote {
    void signin(String username, String password, DataPackage<String> dataPack) throws java.rmi.RemoteException;
}
```
<br />

> For the methods which can be invoked remotely, then:
> 1. Must be declared by throwing <code>[java.rmi.RemoteException](https://docs.oracle.com/javase/10/docs/api/java/rmi/RemoteException.html)</code> exception
> 2. As a member of the class that implements <code>[java.rmi.Remote](https://docs.oracle.com/javase/10/docs/api/java/rmi/Remote.html)</code> or its sub-classes.

<br />

<center>2.3. Create **Executor**</center>
___

The most important stuff that must be managed properly by the server is the thread. There are so many clients who will access some limited resources. If we do not have some tricks to maintain available resources, they will become inconsistent. The best solution to solve this case is to use sync. It is about serializing the parallel tasks, so there will be no thread that will try to precede the other threads as long as they have not completed their task.

We need a thread that will always wait to perform a certain process as long as the thread is not notified. The notifiers can not be accessed in the common way, because some resources must be initialized first and we need to keep them consistent. So, we provide a method that will returns `null` if the current thread is still active, or some resumable object if it is waiting for and through that object we notify it to be active, of course **after initializing** the necessary resources which are needed during the process.
![Untitled 3.png](https://cdn.steemitimages.com/DQmdG4NeSJEkCDiocX3toZJ7JhHWXBP3XGHwYajK1k7UDiv/Untitled%203.png)
![Untitled 4.png](https://cdn.steemitimages.com/DQmWmfPnuCqFwo6T45xCbvPJRQpTzdZozmwJGtU2y4yhhWL/Untitled%204.png)

Therefore, we designed an abstract class Executor as special thread that starts immediately and already manages the states well and the next developers only need to implement the callbacks which are already available,
- `T onActive()` — The core process and must returns the abstract type of the result.
- `void onFinish(T)` — This method is in the synchronized block which will receive the result as parameter and just deliver it.
- `void onClose()` — This method will be called if the currently waiting thread is interrupted.

![Untitled 5.png](https://cdn.steemitimages.com/DQmejtHHWy4AemYK4rc7CniM4gricJXkLQFb6vo9z6XNXAa/Untitled%205.png)

And the following is the source codes of `Executor<T>` class,
```
package com.murez.util;

public abstract class Executor<T> implements Runnable {
    private final Object LOCK = new Object();
    private final Resumable R;
    private Boolean active;
    private boolean resist;

    protected Executor() {
        R = () -> {
            synchronized(this) {
                synchronized(LOCK) { LOCK.notify(); }
                resist = false;
            }
        };
        new Thread(this).start();
    }

    @Override
    public final void run() {
        for(T out = null; ; out = onActive())
            synchronized(LOCK) {
                if(active != null)
                    onFinish(out);
                active = false;
                try { LOCK.wait(); }
                catch(InterruptedException e) {
                    onClose();
                    break;
                }
                active = !active;
            }
    }

    protected Resumable acquires() {
        synchronized(R) {
            if(!resist) {
                boolean deactived;
                synchronized(LOCK) {
                    deactived = active != null && !active;
                }
                if(deactived) {
                    resist = !resist;
                    return R;
                }
            }
            return null;
        }
    }

    protected abstract T onActive();

    protected abstract void onFinish(T response);

    protected abstract void onClose();

    public interface Resumable {
        void resume();
    }
}
```
<br />
<center>2.4. Create **SQLMaintainable**</center>
___
We need to keep connections to the database alive as long as the app runs. If they're lost, the app will attempt to regain them. So, we provide an interface to be able to get database connections bound to properties file. Because it is possible to change the source from database A to B during the runtime. Instead we have to *re-code* to replace the database parameters which causes the app to be recompiled and then restarted, we can make them as the properties file. So, we will simply replace the database parameters through the properties file without having to change the source code and the app keeps on alive.

And the following is the source codes of `SQLMaintainable` interface

```
package com.murez.sql;

public interface SQLMaintainable {
    java.sql.Connection getConnection() throws java.io.IOException;
}
```
<br />
<center>2.5. Create **Processor**</center>
___
The class of `Processor` is the sub-class of `Executor<T>` abstract class. It will perform the main process to authenticate the users. Each user will be handled by a processor. If an authentication has been completed, then it's ready to handle the others. It will implement three abstract methods, `onActive()`, `onFinish(response)` and `onClose()` inherited by its super class, `Executor<T>`. And here's the explanation.

**`onActive()`**
![Untitled 7.png](https://cdn.steemitimages.com/DQmT921t3PPFKQGzsEeVVm7dAoapD7rCTGXdZm11SYoMQXK/Untitled%207.png)
![Untitled 8.png](https://cdn.steemitimages.com/DQmdEPYBUpD8UDNoA7pt8apDpj1oQnmViXPNSFm1GLEKy21/Untitled%208.png)

**`onFinish(response)`**
![Untitled 9.png](https://cdn.steemitimages.com/DQmZJfREC5uVhxMUh3pAeWhWwZK4vvrryQzkF6GY7KRYZQL/Untitled%209.png)

Whenever the server has finished authenticating the users, it must return the result by calling a particular method through a remote object. As we know that the <code>lookup(<i>name</i>)</code> method will return the remote object as the <code>[java.rmi.Remote](https://docs.oracle.com/javase/10/docs/api/java/rmi/Remote.html)</code> instance and we never know what class to be used in order to cast and then invoke the callback method correctly. We do not want the server to adjust to the various callbacks supplied by different clients. Therefore, we can use Java™ Reflection to solve this problem.

We expect clients to provide callbacks with the following signatures:
`void onFinish(DataPackage dataPack)`.
So, the steps you should do are:
1. Get the class definition of the related remote object by executing: <code>remote.[getClass()](https://docs.oracle.com/javase/8/docs/api/java/lang/Object.html#getClass--)</code>.
2. Search the related method by its name "`onFinish`" and list of parameter types which the same as `DataPackage.class`.
3. Finally, invoke the target method. By passing the real instance of remote object and the instance of its parameter.

Thus the server is only concerned with the host address, port number, the name bound to the remote object and the callback name provided by the clients. And the client can replace its callback references repeatedly and let the server stay alive.

**`onClose()`**
```
@Override
public void onClose() {
    try {
        if(!connector.isClosed())
            connector.close();
    } catch(Exception e) { connector = null; }
}
```

Close the database connection at any time the processor will be terminated. Each processor holds one connection, so leaving the connection still active while the processor is no longer used will lead to a resource leak.

**Notify the Processor**
```
public void resume(String username, String password, DataPackage<String> dataPack) {
    Executor.Resumable notifier = acquires();
    if(notifier != null) {
        if(!TEXT.apply(username))
            throw new IllegalArgumentException("Username shouldn't be empty");
        else this.username = username;
        if(!TEXT.apply(password))
            throw new IllegalArgumentException("Password shouldn't be empty");
        else this.password = password;
        String[] keys = { "name" };
        if(dataPack == null
            || dataPack.getNumber(null) == null
            || dataPack.getString(null) == null
            || !dataPack.getPackage().keySet().containsAll(Arrays.asList(keys)))
            throw new IllegalArgumentException("Properties of client's remote callback should be defined correctly");
        else this.dataPack = dataPack;
        notifier.resume();
    } else
    throw new UnsupportedOperationException("This processor was unavailable");
}
```

As described recently, we must first obtain a resumable object before it can notify the processor to be active. We can get this object by calling the inheritance method `acquires`. Always check the returned objects, if not `null`, then we can notify by first completely validating and initializing all of the resources to be used during the process.

Because the source code is too long, then I attach it here, [Processor.java](https://github.com/murez-nst/tutorials/blob/master/Timeless/src/com/murez/util/Processor.java).
<br />
<center>2.6. Create **RemoteAuthenticator**</center>
___
The class is the implementation of `Authenticatable` interface which means that the instance of this one will be exportable into a remote object. It's quietly so simple. When the user sign in, then the remote method will try to fetch the processors in the queue. For three seconds, if the processor is still not available then the server will send the "Service Unavailable" in response. If it's available, the remote object will pass the username, password and data as the arguments of `resume` method which after validating and initializing them will notify the processor. But, if the validation fails, the authentication can not continue and then the server sends the "Bad Request" in response.

```
package com.murez.net;

import com.murez.util.Processor;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.TimeUnit;

public class RemoteAuthenticator implements com.murez.remote.Authenticable {
    private final BlockingQueue<Processor> PROCESSORS;

    public RemoteAuthenticator(BlockingQueue<Processor> processors) {
        PROCESSORS = processors;
    }

    @Override
    public void signin(String username, String password, com.murez.entity.DataPackage<String> dataPack) {
        Processor instance;
        try {
            instance = PROCESSORS.poll(3, TimeUnit.SECONDS);
        } catch(InterruptedException e) {
            instance = null;
        }
        if(instance == null)
            throw new UnsupportedOperationException("503 Service Unavailable");
        else
            try { instance.resume(username, password, dataPack); }
            catch(IllegalArgumentException e) {
                e.printStackTrace();
                throw new IllegalArgumentException("400 Bad Request", e);
            }
            catch(Exception e) {
                throw new UnsupportedOperationException("500 Internal Server Error", e);
            }
    }
}
```
<br />
<center>2.7. Create **Main**</center>
___
And finally, it's time to set up the main method to run the Timeless as a server. Before you can start it properly, first you should prepare the following set of dependencies:
1. Create the properties file. If you notice it also as the supplier of the maintainable database connection, because it implements `SQLMaintainable` interface. So, you have to write into the file using the Text Editor the three pairs of keys and their values depending on the database that will be used by the app. Based on the section **(1)** *Database Setup* we have done before, then you should write as follows:
**`database=Murez`
`username=Timeless`
`password=myPass123`**
and finally save as **`timeless.properties`** in the directory: **D:\Murez\Home\Project\Java\Timeless**. Alternatively, you can customize the name and/or path by passing the new one to the app arguments. But, you can't trick if the new path has space separator.
![Untitled 11.png](https://cdn.steemitimages.com/DQmc7dAzsamTNdLfXMHKD8Y1nwa4CkNmn2ahzXEczBufo1H/Untitled%2011.png)
Then if at any time you want to change the source of database for maintenance, do not necessarily have to change the parameters through the source code, but simply replace it from the properties file and let the server stay alive.

2. Install the Security Manager and create the policy file.
![Untitled 12.png](https://cdn.steemitimages.com/DQmUzYwEjpsFBVLWm3TKL1Z37zx5r25F2spQxcK4anLiRek/Untitled%2012.png)
We will install a security manager in this app, so it will restrict the tasks to perform any operations that require permissions and only to the trusted resources. Then we will grant all the permissions to the local class path of Timeless's app and also JAR of MySQL Connector as the app dependency we will import later in the next point.
Open your Text editor and write down these codes:
   ```
   grant codeBase "file:/D:/Murez/Home/Project/Java/Timeless/out/production/Timeless" {
       permission java.security.AllPermission;
   };
   grant codeBase "file:/C:/Program Files (x86)/MySQL/Connector J 8.0/mysql-connector-java-8.0.11.jar" {
       permission java.security.AllPermission;
   };
   ```
   and save as **`server.policy`** in the same directory: **D:\Murez\Home\Project\Java\Timeless**.

3. Download MySQL Connector/J, extract the JAR file and then import it into your project in the following way:
   1. Click **File** ⇢ **Project Structure...** or simply press **Ctrl** + **Alt** + **Shift** + **S**, and then new dialog window will appear.
   2. Click **+** (plus sign) at the most right ⇢ **JARs or directories**.
   3. Browse to your **mysql-connector-java-XXX.jar** file.
If you've installed Connector/J via the *MySQL Installer*, you can simply find it at **C:\Program Files&#160;(x86)\MySQL\Connector J 8.0\\**. Alternatively you can download it [here](https://dev.mysql.com/downloads/connector/j/).
   4. Finally click **OK**.
![Untitled 10.png](https://cdn.steemitimages.com/DQmcUeay7aPTPA7qw94oAdvUsYpxiJ6xK8oqKXDjdQsb5DF/Untitled%2010.png)
<br />
4. Prepare the Processors, export a remote object and register the Stub.
![Untitled 13.png](https://cdn.steemitimages.com/DQmadMquxxiJv5rkC1FR6J4FXYNf7EX8A9se37HG75EGSyZ/Untitled%2013.png)

Again the source code is too long, you can look at [here](https://github.com/murez-nst/tutorials/blob/master/Timeless/src/com/murez/Main.java).

Finally, we're done in order building the Timeless app. Before step forward to build the next project, we have to export the `Authenticable` and `DataPackage` classes as a JAR file. Third parties will never be able to compile their projects without them. And here are the steps,
- Open your Command Prompt.
- IntelliJ IDEA set its default output directory at out/production/[Project-Name]. So, change your working directory to **D:\Murez\Home\Project\Java\Timeless\out\production\Timeless**.
- Type as follows, **`jar cvf auth.jar com/murez/remote/Authenticable.class com/murez/entity/DataPackage.class`**. Then **auth.jar** file will appear in the current directory.
Send this file to the ChitChat developers, and we can start to the next development.
![Untitled 14.png](https://cdn.steemitimages.com/DQmSQdFdgFucdLeXtMvfHWqWtnN5h5NAQhPq1miwppkN3Nq/Untitled%2014.png)
<br />
<center>3. Create **EventListenable**</center>
___
Setup a new project as the same as at the section **(2)**, but change its name to **ChitChat** and the structure will look like the following,
<center>![Untitled 15.png](https://cdn.steemitimages.com/DQmRGvPNRytuNRqRP2RwakBjs9Di5LgV5kUimXaSgYYa1WP/Untitled%2015.png)</center>
<br />
<center>3.1. Create **EventListenable**</center>
___
Based on the previous deal that we can define our own remote object as a callback reference, but the server only accepts there must be one method named `onFinish` and the argument is a response with the `DataPackage` type.
```
package com.julisa.remote;

public interface EventListenable extends java.rmi.Remote {
    void onFinish(com.murez.entity.DataPackage<String> response) throws java.rmi.RemoteException;
}
```
<br />
<center>3.2. Create **SigninPane**</center>
___
This layout is so simple, we just need two text fields for username and password, a button for submit this form, a label as notifier and progress indicator to show the user that this app is still in authentication. There's no special one in this code, just finish it as simple as create an instance, customize as needed and register every controls according to their container at last. Remember, we just need an instance of this layout per app during its lifecycle, so we can apply this class as singleton.
```
package com.julisa.ui;

import com.julisa.remote.EventListenable;
import com.murez.entity.DataPackage;
import com.murez.remote.Authenticable;
import javafx.application.Platform;
import javafx.beans.binding.DoubleExpression;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.control.Button;
import javafx.scene.control.Hyperlink;
import javafx.scene.control.Label;
import javafx.scene.control.PasswordField;
import javafx.scene.control.ProgressIndicator;
import javafx.scene.control.TextField;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Pane;
import javafx.scene.layout.VBox;
import java.rmi.registry.LocateRegistry;
import static sample.Main.*;

public class SigninPane {
    private static SigninPane instance;

    public static synchronized Pane getInstance() {
        return (instance == null? instance = new SigninPane() : instance).ROOT;
    }

    public static synchronized EventListenable getListener() {
        return (instance == null? instance = new SigninPane() : instance).listener;
    }

    private final VBox BOX;
    private final Label NOTIFIER;
    private final GridPane ROOT;
    private final Button SUBMIT;
    private final TextField USERNAME, PASSWORD;
    private EventListenable listener;

    private SigninPane() {
        EventImplementer listener = new EventImplementer();
        (USERNAME = new TextField()).setPromptText("Your Email");
            USERNAME.setStyle("-fx-font-size:15px");
            USERNAME.setOnAction(listener);
        (PASSWORD = new PasswordField()).setPromptText("Password");
            PASSWORD.setStyle("-fx-font-size:15px");
            PASSWORD.setOnAction(listener);
        (NOTIFIER = new Label()).setStyle("-fx-text-fill:red");
        (SUBMIT = new Button("Sign in")).setPrefWidth(120);
            SUBMIT.setStyle("-fx-font-size:16px;-fx-padding:5px 20px");
            SUBMIT.setOnAction(listener);
        HBox box = new HBox(20);
            box.setAlignment(Pos.CENTER_RIGHT);
            box.getChildren().addAll(NOTIFIER, SUBMIT);
        Hyperlink register = new Hyperlink("Don't you have an account? Register now");
        //register.setOnAction(e -> ROOT.getScene().setRoot(signupContainer()));
        (BOX = new VBox(10)).setAlignment(Pos.CENTER_RIGHT);
            BOX.getChildren().addAll(box, register);
        Label title = new Label("Welcome to Priceless");
            title.setStyle(STYLE_TITLE);
        (ROOT = new GridPane()).setPadding(new Insets(85, 25, 15, 25));
            ROOT.setAlignment(Pos.TOP_CENTER);
            ROOT.setVgap(10);
            ROOT.setHgap(5);
            ROOT.widthProperty().addListener(observable -> USERNAME.setPrefWidth(((DoubleExpression) observable).get() - 350));
            ROOT.add(title, 0, 0);
            ROOT.add(USERNAME, 0, 1);
            ROOT.add(PASSWORD, 0, 2);
            ROOT.add(BOX, 0, 3);
    }
}
```
<br />
<center>3.3. Create **SigninPane.EventImplementer**</center>
___
This class is basically an event listener for submit button, also username and password text fields if the Enter is pressed. During authentication we will show the progress indicator and freeze the text fields and buttons, so that the user does not spam the request until the process has been completed by getting the response from the server, then we reset the controls as early. It should be noted that JavaFX does not allow its controls to be mutated by other threads except its original one. So, we need the `Platform.runLater` static method to modify our controls.
![Untitled 16.png](https://cdn.steemitimages.com/DQmWLPxnDw43mEUxeNVGq4xGZBV2qXSKVphBESX5kFtZ9d2/Untitled%2016.png)
You'll notice that the `EventListenable` object is initialized not on the constructor of the class that owns it, but instead in the `EventImplementer`'s inner class. This is because the action on the `onFinish` method depends on the `indicator` variable as a member of the `EventImplementer`, so it will be easier if doing trick like this.
```
private class EventImplementer implements EventHandler<ActionEvent> {
    private final String NAME = "Authentication";
    private ProgressIndicator indicator;
    private boolean active;

    private EventImplementer() {
        listener = (response) -> {
            final int CODE = response.getNumber(0).intValue();
            if(CODE == 200) {
                if(response.getPackage().get("session") == null) {
                    Platform.runLater(() -> {
                        NOTIFIER.setText(response.getPackage().get("message"));
                        USERNAME.requestFocus();
                    });
                    USERNAME.setEditable(true);
                    USERNAME.setOpacity(1);
                    PASSWORD.setEditable(true);
                    PASSWORD.setOpacity(1);
                    SUBMIT.setDisable(false);
                } else {
                    Platform.runLater(() -> {
                        NOTIFIER.setText("Hello, " + response.getPackage().get("name"));
                        NOTIFIER.setStyle("-fx-text-fill:green");
                        NOTIFIER.requestFocus();
                    });
                }
                USERNAME.setText("");
                PASSWORD.setText("");
            }
            Platform.runLater(() -> BOX.getChildren().remove(indicator));
            synchronized(NAME) {
                if(active) active = false;
            }
        };
    }

    @Override
    public void handle(ActionEvent event) {
        String username, password;
        if((username = TEXT.apply(USERNAME)) == null) return;
        if((password = TEXT.apply(PASSWORD)) == null) return;
        synchronized(NAME) {
            if(!active)
                active = !active;
            else
                return;
        }
        BOX.getChildren().add(indicator = new ProgressIndicator());
        USERNAME.setEditable(false);
        PASSWORD.setEditable(false);
        SUBMIT.setDisable(true);
        USERNAME.setOpacity(.5);
        PASSWORD.setOpacity(.5);
        DataPackage<String> dataPack = DataPackage.create(0, LOCALHOST);
        dataPack.getPackage().put("name", "AuthCallback");
        try {
            final Authenticable REMOTE;
            try {
                REMOTE = (Authenticable) LocateRegistry.getRegistry().lookup(NAME);
            } catch(java.rmi.NotBoundException e) {
                throw new UnsupportedOperationException("There's no remote reference for name \"" + NAME + "\"", e);
            }
            REMOTE.signin(username, password, dataPack);
        } catch(Exception e) { e.printStackTrace(); }
    }
}
```
<br />
<center>3.4. Create **Main**</center>
___
Here are the ChitChat's dependencies that have to set before it can run as a client:
1. Install the Security Manager and create the policy file.  Save this code as **`client.policy`** in the directory: **D:\Murez\Home\Project\Java\ChitChat**
   ```
   grant codeBase "file:/D:/Murez/Home/Project/Java/ChitChat/out/production/ChitChat" {
       permission java.security.AllPermission;
   };
   ```
   The statements are not much different from the previous Timeless project.

2. Import **auth.jar** file and set the codebase as this app properties. Put this JAR file in the same directory as **D:\Murez\Home\Project\Java\ChitChat** and the rest do the same import steps as in the previous section.
The RMI registry should know where the locations/URL it should download the required non-local class paths before being able to accept the stub registration. They are codes that come from the `auth.jar` file and the `EventListenable` class. So, we have two references as codebase that must be separated by the spaces depending on the rules. Because there is a space, we can not pass it as an app arguments. So we set it up (hard code) using the `System.setProperty` method with `java.rmi.server.codebase` as the key and its value is:
`http://localhost/chitchat/ file:/D:/Murez/Home/Project/Java/ChitChat/auth.jar`

3. Export a remote object and register the Stub. Again still the same as the previous way, but use `AuthCallback` name and `SigninPane.getListener` method to get an instance of remote object as the callback reference.
```
package sample;

import com.julisa.ui.SigninPane;
import javafx.application.Application;
import javafx.beans.binding.DoubleExpression;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.Accordion;
import javafx.scene.control.ListView;
import javafx.scene.control.TextField;
import javafx.scene.control.TextInputControl;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.Pane;
import javafx.stage.Stage;
import java.rmi.registry.LocateRegistry;
import java.rmi.server.UnicastRemoteObject;
import java.util.function.Function;

public class Main extends Application {
    public final static String STYLE_TITLE =
          "-fx-font-size:25px;"
        + "-fx-text-fill:#9a9a9a;"
        + "-fx-font-family:\"Cambria\";"
        + "-fx-font-weight:bold;"
        + "-fx-effect:innershadow(three-pass-box, rgba(0, 0, 0, 0.7), 6, 0.0, 0, 2)";
    public final static String LOCALHOST;
    public final static Function<TextInputControl, String> TEXT = textCtrl -> {
        String text = textCtrl.getText();
        if((text = text != null && text.length() > 0? text : null) == null)
            textCtrl.requestFocus();
        return text;
    };

    static {
        String localhost;
        try {
            localhost = java.net.InetAddress.getLocalHost().getHostAddress();
        } catch(Exception e) {
            e.printStackTrace();
            localhost = null;
        }
        LOCALHOST = localhost;
    }

    public static void main(String[] args) {
        System.setProperty("java.security.policy", "client.policy");
        System.setProperty("java.rmi.server.useCodebaseOnly", "false");
        System.setProperty("java.rmi.server.codebase", "http://localhost/chitchat/ file:/D:/Murez/Home/Project/Java/ChitChat/auth.jar");
        if(System.getSecurityManager() == null)
            System.setSecurityManager(new SecurityManager());
        try {
            LocateRegistry.getRegistry().rebind("AuthCallback", UnicastRemoteObject.exportObject(SigninPane.getListener(), 0));
        } catch(Exception e) {
            e.printStackTrace();
            return;
        }
        launch(args);
    }

    @Override
    public void start(Stage primaryStage) {
        primaryStage.setScene(new Scene(SigninPane.getInstance(), 800, 400));
        primaryStage.setTitle("Priceless");
        primaryStage.setMinHeight(350);
        primaryStage.setMinWidth(650);
        primaryStage.show();
    }
}
```
<br />
At last, we have finished Timeless and ChitChat projects completely.
<br />
<center>4. Deployment and Running up</center>
1. Start RMI Registry by opening Command Prompt and then type this following command,
**`rmiregistry -J-Djava.rmi.server.useCodebaseOnly=false`**
2. Start the **Timeless**.
Focus on the Main.java document, then press **Ctrl** + **Shift** + **F10** to execute `main` method. You will get errors, but just ignore it. It happens normally because we have not specified the codebase yet as our JVM option, and the RMI Registry can not find the required classes in its local class path and do not know where to download them. So, we have to provide the path of JAR file containing `Authenticable` and `DataPackage` classes as a codebase by performing these following steps:
   - Click **Run** ⇢ **Edit Configurations...**, then **Run/Debug Configurations** dialog will appear.
   - Click **Configuration** tab and type on the **VM options** text field as follows: `-Djava.rmi.server.codebase=file:/D:/Murez/Home/Project/Java/Timeless/auth.jar`.
   ![Untitled 17.png](https://cdn.steemitimages.com/DQmPazGYUNmfCq3ADDPBSYDrjf6rohBuMDJsHVfxBnBLQNN/Untitled%2017.png)
   - Finally, click **OK** and rerun app by pressing **Ctrl** + **F5**.
3. Start the **ChitChat**.
Focus on the Main.java document, but before starting this app, make sure we are already exporting the `EventListenable` class to the public directory. And here are the steps:
   - Start your localhost server by opening MAMP (I've installed this one) and start **Apache Server** service.
   - Browse to location: **D:\Murez\Home\Project\Java\ChitChat\out\production\ChitChat\com\julisa\remote** and copy `EventListenable.class` file. Then paste it on the target directory: **C:\MAMP\htdocs\chitchat\com\julisa\remote**. If the parent directories don't exist, create them.
   - Finally, run this app by pressing **Ctrl** + **Shift** + **F10**.
4. Test!
   - Case #1: username: "**intruder@unknown.com**" and password: "**123**".
![Untitled 18.png](https://cdn.steemitimages.com/DQmYRvgWDQviXSiiXruZB3sNvkeyob4WtcFg3Zhje5QxXTP/Untitled%2018.png)
   - Case #2: username: "**murez.nasution@gmail.com**" and password: "**123**".
![Untitled 19.png](https://cdn.steemitimages.com/DQmRdSEbks8YvpQs9B39VPfihqzDZcrdcujZcpWx81gmrS8/Untitled%2019.png)
   - Case #3: username: "**murez.nasution@gmail.com**" and password: "**myPass987**".
![Untitled 20.png](https://cdn.steemitimages.com/DQmV4dvLH299SmMpP7aRkBtMAPXW8Eesz7d7QyKfjC6hT4d/Untitled%2020.png)
___
*I'm still wondering how to make a GIF in order to show you what activities I'm currently doing. As we know, recording screen activity will produce output with a large size. So, this is all I can present and then I will do better.*
<br />
___
#### Curriculum
- [Java - How to Create a Simple Server Using Java Socket](https://steemit.com/utopian-io/@murez-nst/mureztutorial005)
- [Java (Spring Framework) - First App with Connection to MySQL Database](https://steemit.com/utopian-io/@murez-nst/mureztutorial002)
- [JavaFX - First App with Modern Graphical User Interface](https://steemit.com/utopian-io/@murez-nst/mureztutorial010)

#### Proof of Work Done
- https://github.com/murez-nst/tutorials/tree/master/Timeless
- https://github.com/murez-nst/tutorials/tree/master/ChitChat
👍  , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , ,
properties (23)
authormurez-nst
permlinkmureztutorial012
categoryutopian-io
json_metadata{"community":"busy","app":"steemit/0.1","format":"markdown","links":["https://github.com/JetBrains/intellij-community","http://www.oracle.com/technetwork/java/javase/downloads/index.html","https://www.jetbrains.com/idea/download/","https://dev.mysql.com/downloads/","https://www.apachefriends.org/download.html","https://www.mamp.info/en/downloads/","https://steemit.com/utopian-io/@murez-nst/mureztutorial002","https://docs.oracle.com/javase/8/docs/api/java/util/Map.html","https://docs.oracle.com/javase/8/docs/api/java/util/HashMap.html","https://docs.oracle.com/javase/10/docs/api/java/rmi/Remote.html","https://docs.oracle.com/javase/10/docs/api/java/rmi/RemoteException.html","https://docs.oracle.com/javase/8/docs/api/java/lang/Object.html#getClass--","https://github.com/murez-nst/tutorials/blob/master/Timeless/src/com/murez/util/Processor.java","https://dev.mysql.com/downloads/connector/j/","https://github.com/murez-nst/tutorials/blob/master/Timeless/src/com/murez/Main.java","https://steemit.com/utopian-io/@murez-nst/mureztutorial005","https://steemit.com/utopian-io/@murez-nst/mureztutorial010","https://github.com/murez-nst/tutorials/tree/master/Timeless","https://github.com/murez-nst/tutorials/tree/master/ChitChat"],"tags":["utopian-io","tutorials","java","intellij","rmi"],"image":["https://cdn.steemitimages.com/DQmYPTTQmMW8zQHLv4AFQaqxcnmGS243nHe548mrop4gKrE/Untitled%201.png","https://cdn.steemitimages.com/DQmf7XiKhyPoTToRwVZT8pFkA59WQ5jV5Mx5G1eE8sxss2W/RMI%20Authentication.png","https://cdn.steemitimages.com/DQmNU2u7Fb8mdCBZyeQu4XxZuF94B94HuZ9VjA5DQYWnd6C/Untitled%206.png","https://cdn.steemitimages.com/DQmcVEo2hpjprbw5PEXgBHU3FAh1dyAZTJ74pZKzSQxPDAM/Untitled%202.png","https://cdn.steemitimages.com/DQmdG4NeSJEkCDiocX3toZJ7JhHWXBP3XGHwYajK1k7UDiv/Untitled%203.png","https://cdn.steemitimages.com/DQmWmfPnuCqFwo6T45xCbvPJRQpTzdZozmwJGtU2y4yhhWL/Untitled%204.png","https://cdn.steemitimages.com/DQmejtHHWy4AemYK4rc7CniM4gricJXkLQFb6vo9z6XNXAa/Untitled%205.png","https://cdn.steemitimages.com/DQmT921t3PPFKQGzsEeVVm7dAoapD7rCTGXdZm11SYoMQXK/Untitled%207.png","https://cdn.steemitimages.com/DQmdEPYBUpD8UDNoA7pt8apDpj1oQnmViXPNSFm1GLEKy21/Untitled%208.png","https://cdn.steemitimages.com/DQmZJfREC5uVhxMUh3pAeWhWwZK4vvrryQzkF6GY7KRYZQL/Untitled%209.png","https://cdn.steemitimages.com/DQmc7dAzsamTNdLfXMHKD8Y1nwa4CkNmn2ahzXEczBufo1H/Untitled%2011.png","https://cdn.steemitimages.com/DQmUzYwEjpsFBVLWm3TKL1Z37zx5r25F2spQxcK4anLiRek/Untitled%2012.png","https://cdn.steemitimages.com/DQmcUeay7aPTPA7qw94oAdvUsYpxiJ6xK8oqKXDjdQsb5DF/Untitled%2010.png","https://cdn.steemitimages.com/DQmadMquxxiJv5rkC1FR6J4FXYNf7EX8A9se37HG75EGSyZ/Untitled%2013.png","https://cdn.steemitimages.com/DQmSQdFdgFucdLeXtMvfHWqWtnN5h5NAQhPq1miwppkN3Nq/Untitled%2014.png","https://cdn.steemitimages.com/DQmRGvPNRytuNRqRP2RwakBjs9Di5LgV5kUimXaSgYYa1WP/Untitled%2015.png","https://cdn.steemitimages.com/DQmWLPxnDw43mEUxeNVGq4xGZBV2qXSKVphBESX5kFtZ9d2/Untitled%2016.png","https://cdn.steemitimages.com/DQmPazGYUNmfCq3ADDPBSYDrjf6rohBuMDJsHVfxBnBLQNN/Untitled%2017.png","https://cdn.steemitimages.com/DQmYRvgWDQviXSiiXruZB3sNvkeyob4WtcFg3Zhje5QxXTP/Untitled%2018.png","https://cdn.steemitimages.com/DQmRdSEbks8YvpQs9B39VPfihqzDZcrdcujZcpWx81gmrS8/Untitled%2019.png","https://cdn.steemitimages.com/DQmV4dvLH299SmMpP7aRkBtMAPXW8Eesz7d7QyKfjC6hT4d/Untitled%2020.png"]}
created2018-06-23 20:40:42
last_update2018-06-23 21:04:18
depth0
children4
last_payout2018-06-30 20:40:42
cashout_time1969-12-31 23:59:59
total_payout_value51.817 HBD
curator_payout_value16.848 HBD
pending_payout_value0.000 HBD
promoted0.000 HBD
body_length41,074
author_reputation2,096,769,434,632
root_title"Java™ - Best Practice for Remote Communication with Java RMI"
beneficiaries[]
max_accepted_payout1,000,000.000 HBD
percent_hbd10,000
post_id61,959,144
net_rshares34,357,457,806,554
author_curate_reward""
vote details (64)
@steemitboard ·
Congratulations @murez-nst! You have completed the following achievement on Steemit and have been rewarded with new badge(s) :

[![](https://steemitimages.com/70x80/http://steemitboard.com/notifications/payout.png)](http://steemitboard.com/@murez-nst) Award for the total payout received

<sub>_Click on the badge to view your Board of Honor._</sub>
<sub>_If you no longer want to receive notifications, reply to this comment with the word_ `STOP`</sub>



**Do not miss the last post from @steemitboard:**
[SteemitBoard World Cup Contest - Russia vs Croatia](https://steemit.com/steemitboard/@steemitboard/steemitboard-world-cup-contest-russia-vs-croatia)

---
**Participate in the [SteemitBoard World Cup Contest](https://steemit.com/steemitboard/@steemitboard/steemitboard-world-cup-contest-collect-badges-and-win-free-sbd)!**
Collect World Cup badges and win free SBD
Support the Gold Sponsors of the contest: [@good-karma](https://v2.steemconnect.com/sign/account-witness-vote?witness=good-karma&approve=1) and [@lukestokes](https://v2.steemconnect.com/sign/account-witness-vote?witness=lukestokes.mhth&approve=1)

---

> Do you like [SteemitBoard's project](https://steemit.com/@steemitboard)? Then **[Vote for its witness](https://v2.steemconnect.com/sign/account-witness-vote?witness=steemitboard&approve=1)** and **get one more award**!
properties (22)
authorsteemitboard
permlinksteemitboard-notify-murez-nst-20180707t092538000z
categoryutopian-io
json_metadata{"image":["https://steemitboard.com/img/notify.png"]}
created2018-07-07 09:25:36
last_update2018-07-07 09:25:36
depth1
children0
last_payout2018-07-14 09:25:36
cashout_time1969-12-31 23:59:59
total_payout_value0.000 HBD
curator_payout_value0.000 HBD
pending_payout_value0.000 HBD
promoted0.000 HBD
body_length1,345
author_reputation38,975,615,169,260
root_title"Java™ - Best Practice for Remote Communication with Java RMI"
beneficiaries[]
max_accepted_payout1,000,000.000 HBD
percent_hbd10,000
post_id63,759,212
net_rshares0
@utopian-io ·
Hey @murez-nst
**Thanks for contributing on Utopian**.
We’re already looking forward to your next contribution!

**Want to chat? Join us on Discord https://discord.gg/h52nFrV.**

<a href='https://v2.steemconnect.com/sign/account-witness-vote?witness=utopian-io&approve=1'>Vote for Utopian Witness!</a>
properties (22)
authorutopian-io
permlinkre-mureztutorial012-20180627t160510z
categoryutopian-io
json_metadata"{"app": "beem/0.19.29"}"
created2018-06-27 16:05:12
last_update2018-06-27 16:05:12
depth1
children0
last_payout2018-07-04 16:05:12
cashout_time1969-12-31 23:59:59
total_payout_value0.000 HBD
curator_payout_value0.000 HBD
pending_payout_value0.000 HBD
promoted0.000 HBD
body_length301
author_reputation152,955,367,999,756
root_title"Java™ - Best Practice for Remote Communication with Java RMI"
beneficiaries[]
max_accepted_payout1,000,000.000 HBD
percent_hbd10,000
post_id62,503,000
net_rshares0
@yokunjon · (edited)
I thank you for your contribution. Here is my thought;

* Well done, @murez-nst. It's one of the best tutorials I've ever seen which covers advanced and complex concepts. I thank you for your valuable time which dedicated to writing this. I liked the way you describe your algorithm and what you're doing with it. I await your next posts. Good luck!

----
Your contribution has been evaluated according to [Utopian policies and guidelines](https://join.utopian.io/guidelines), as well as a predefined set of questions pertaining to the category.

To view those questions and the relevant answers related to your post, [click here](https://review.utopian.io/result/8/12211213).

---- 
Need help? Write a ticket on https://support.utopian.io/. 
Chat with us on [Discord](https://discord.gg/uTyJkNm). 
[[utopian-moderator]](https://join.utopian.io/)
👍  
properties (23)
authoryokunjon
permlinkre-murez-nst-mureztutorial012-20180627t140819329z
categoryutopian-io
json_metadata{"tags":["utopian-io"],"users":["murez-nst"],"links":["https://join.utopian.io/guidelines","https://review.utopian.io/result/8/12211213","https://support.utopian.io/","https://discord.gg/uTyJkNm","https://join.utopian.io/"],"app":"steemit/0.1"}
created2018-06-27 14:08:18
last_update2018-06-27 14:09:27
depth1
children1
last_payout2018-07-04 14:08:18
cashout_time1969-12-31 23:59:59
total_payout_value0.000 HBD
curator_payout_value0.000 HBD
pending_payout_value0.000 HBD
promoted0.000 HBD
body_length846
author_reputation19,266,807,595,513
root_title"Java™ - Best Practice for Remote Communication with Java RMI"
beneficiaries[]
max_accepted_payout1,000,000.000 HBD
percent_hbd10,000
post_id62,490,183
net_rshares1,299,738,117
author_curate_reward""
vote details (1)
@murez-nst ·
Your welcome @yokunjon, I like your response about my contribution. Hopefully my next tutorial will also be valuable for the open source community.
👍  
properties (23)
authormurez-nst
permlinkre-yokunjon-re-murez-nst-mureztutorial012-20180627t143509811z
categoryutopian-io
json_metadata{"tags":["utopian-io"],"users":["yokunjon"],"app":"steemit/0.1"}
created2018-06-27 14:35:21
last_update2018-06-27 14:35:21
depth2
children0
last_payout2018-07-04 14:35:21
cashout_time1969-12-31 23:59:59
total_payout_value0.000 HBD
curator_payout_value0.000 HBD
pending_payout_value0.000 HBD
promoted0.000 HBD
body_length147
author_reputation2,096,769,434,632
root_title"Java™ - Best Practice for Remote Communication with Java RMI"
beneficiaries[]
max_accepted_payout1,000,000.000 HBD
percent_hbd10,000
post_id62,493,179
net_rshares2,271,553,511
author_curate_reward""
vote details (1)