Extremely Serious

Month: June 2018

J2EE Design Patterns

Design patterns are fundamental concepts in software engineering that provide reusable solutions to common problems. They help in creating more maintainable and scalable software. In this article, we will explore several design patterns and provide Java examples for each pattern's practical application.

Presentation Tier

Intercepting Filter Pattern

The Intercepting Filter Pattern is used when the intent is to perform pre or post-processing with the request or response of an application, typically in web applications. In Java, you can implement this pattern using servlet filters. Here's an example:

public class LoggingFilter implements Filter {
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
        throws IOException, ServletException {
        // Pre-processing logic
        // ...

        // Pass the request to the next filter or servlet in the chain
        chain.doFilter(request, response);

        // Post-processing logic
        // ...
    }

    // Other methods for initialization and cleanup
}

Front Controller Pattern

The Front Controller Pattern is employed when the intent is to have a centralized request handling mechanism that dispatches requests to the appropriate handlers. In Java EE applications, a servlet can act as a front controller. Here's a simplified example:

public class FrontControllerServlet extends HttpServlet {
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
        throws ServletException, IOException {
        // Determine the appropriate handler for the request
        // ...

        // Dispatch to the appropriate handler
        // ...
    }
}

Business Tier

Business Delegate Pattern

The Business Delegate Pattern is used when the intent is to provide a single point of entry for clients to access business services, especially when separating the presentation tier and the business tier. Here's a simple Java example:

public class BusinessDelegate {
    private BusinessService businessService;

    public BusinessDelegate() {
        businessService = new BusinessService();
    }

    public void doTask() {
        // Delegate the task to the business service
        businessService.doTask();
    }
}

Composite Entity Pattern

The Composite Entity Pattern is often used in database applications to manage a graph of persistent objects using a primary composite entity. Here's a basic Java example:

public class CompositeEntity {
    private CoarseGrainedObject cgo = new CoarseGrainedObject();

    public void setData(String data1, String data2) {
        cgo.setData(data1, data2);
    }

    public String[] getData() {
        return cgo.getData();
    }
}

Service Locator Pattern

The Service Locator Pattern is employed when the intent is to locate services using JNDI (Java Naming and Directory Interface) lookup. Here's a simplified example:

public class ServiceLocator {
    public Service getService(String serviceName) {
        // Perform JNDI lookup to obtain the service
        // ...

        // Return the service
        return service;
    }
}

Session Facade Pattern

The Session Facade Pattern can be used in Java EE applications when the intent is to encapsulate business-tier components and expose a coarse-grained service to remote clients. Here's a basic example:

public class SessionFacade {
    private BusinessComponent businessComponent;

    public SessionFacade() {
        businessComponent = new BusinessComponent();
    }

    public void performBusinessOperation() {
        // Encapsulate business logic and provide a coarse-grained service
        businessComponent.performOperation();
    }
}

Transfer Object (Value Object) Pattern

The Transfer Object Pattern is used to pass data with multiple attributes in one shot from the client to the server. In Java, this pattern is typically implemented using POJOs (Plain Old Java Objects) with getter and setter methods, and the objects must be serializable. Here's a simple example:

public class TransferObject implements Serializable {
    private String attribute1;
    private int attribute2;

    // Getter and setter methods for attributes
    // ...
}

Value List Handler Pattern

The Value List Handler Pattern is used for managing the results of search operations, especially when results can be paged and traversed iteratively. Here's a basic example:

public class ValueListHandler {
    public List<ValueObject> getPage(int pageNumber) {
        // Retrieve a specific page of results
        // ...
    }
}

Integration Tier

Data Access Object (DAO) Pattern

The Data Access Object (DAO) Pattern is commonly used in Java EE applications to separate data persistence logic into a separate layer. Here's a simplified example:

public class UserDao {
    public User getUserById(int userId) {
        // Data access logic to retrieve a user from the database
        // ...
    }
}

Service Activator Pattern

The Service Activator Pattern is used to invoke a service asynchronously. An example of this pattern is a JMS (Java Message Service) listener that waits for a messaging request and delegates it to an appropriate service. Here's a high-level example:

public class ServiceActivator {
    public void activateService(ServiceRequest request) {
        // Asynchronously invoke the service based on the request
        // ...
    }
}

In conclusion, design patterns are invaluable tools for designing and architecting software systems. They provide tested and proven solutions to recurring design problems. By applying these design patterns in your Java applications, you can enhance code reusability, maintainability, and scalability, ultimately leading to more robust and efficient software.

Windows Command Output to Clipboard

Sometimes it is useful to get the output of a command and paste it to a text editor. For this we can use piping and the clip (i.e. sends the output to Windows clipboard) command.

Example:

If we go to any directory and run the tree command but it returns a very long output that exceeds the screen. We can redirect the output to Windows clipboard temporarily then paste it to a text editor.

  1. Open a command terminal.
  2. Try to run the following tree command.

%USERPROFILE%>tree

Where %USERPROFILE% is normally resolves to our local home directory.

The output might not fit to command terminal screen.

  1. Try redirecting it to a Windows clipboard with the following command:

%USERPROFILE%>tree | clip

You will see no output because it is directed to a Windows clipboard.

  1. Open a text editor of our choice (or anything where we wanted to paste the output).

Check if you can confirm the last entries from step 2 exists to our pasted output.

Angular Data Bindings

Binding coordinates state transfer between the component's class and its template. The following table shows the different types of Angular data bindings.

TypeDescriptionDirectionExample
InterpolationEvaluates the expression between the double curly braces.One-way

(Component→Template)
<h1>{{header}}</h1>
Property BindingUpdates the property if there's a change from the bound component state.

This is typically denoted by the square bracket surrounding the target property.
One-way

(Component→Template)
<img [src]='imageURL'/>
Event BindingUpdates the bound component state if an event's was fired.

This is typically denoted by the parenthesis surrounding the event property.
One-way

(Component←Template)
<button (click)='onSave'>Save</button>
Two-Way BindingNormally use with the form elements.

This is typically denoted by the combined square and parenthesis surrounding the ngModel property.
Two-way

(Component↔Template)
<input type='text' [(ngModel)]='name'/>

JavaScript Prototypal Inheritance

The JavaScript's facility to provide inheritance is via prototype property of the constructor function (i.e. accessible via __proto__ in the instance context). Thus, we normally use the prototype to make properties and functions possible to be inherited. See the following code to do the inheritance properly.

Code 1 - Proper Way of Implementing Prototypal Inheritance
"use strict";

function Person(firstName, lastName) {
  this.firstName = firstName;
  this.lastName = lastName;
}

Person.prototype.middleName = "";

Object.defineProperty(Person.prototype, 'fullName', {
  get: function() {
    var name = (this.firstName||"");
    name += (name.length>0 ? " " : "") + (this.middleName||"");
    name += (name.length>0 ? " " : "") + (this.lastName||"");
      return name;
  }, 
  set: function(name) {
    var DELIM = " ";
    var names = (name||"").split(DELIM);
    this.firstName = names[0];
    this.lastName = names[1];
  }
});

function Employee(firstName, lastName, position) {
   Person.call(this, firstName, lastName);
   this.position = position||"";
}

Employee.prototype = Object.create(Person.prototype);
Employee.prototype.constructor = Employee;

var person = new Person();
person.fullName = "Ron Webb";
console.log(person.fullName);

var employee = new Employee("Ofelia", "Webb", "Manager");
console.log(employee.fullName);
console.log(employee.position);

Based on preceding code, the Employee is inheriting the Person via the following snippet:

Snippet 1 - Inhering the Person Class
Employee.prototype = Object.create(Person.prototype);

But, we should not stop there. We need to also specify the constructor to use the create the instance of Employee like the following snippet:

Snippet 2 - Specify the Constructor to Create the Employee Instance
Employee.prototype.constructor = Employee;

Lastly, in the constructor implementation of the Employee try to also call the constructor of the Person like the following snippet:

Snippet 3 - Call the Parent's Constructor
function Employee(firstName, lastName, position) {
   Person.call(this, firstName, lastName);
   this.position = position||"";
}

Using ES6 Syntactic Sugar for Inheritance

With ES6, this was greatly simplified because we don't need to deal with the prototype property of the constructor function like the following code:

Code 2 - ES6 implementation of Code 1
"use strict";

class Person {
  constructor(firstName, lastName) {
    this.firstName = firstName;
    this.lastName = lastName;
    this.middleName = "";
  }
  
  get fullName() {
    var name = (this.firstName||"");
    name += (name.length>0 ? " " : "") + (this.middleName||"");
    name += (name.length>0 ? " " : "") + (this.lastName||"");
      return name;  
  }
  
  set fullName(name) {
    const DELIM = " ";
    var names = (name||"").split(DELIM);
    this.firstName = names[0];
    this.lastName = names[1];  	
  }
}

class Employee extends Person {
  constructor(firstName, lastName, position) {
   super(firstName, lastName);
    this.position = position||"";
  }
}

var person = new Person();
person.fullName = "Ron Webb";
console.log(person.fullName);

var employee = new Employee("Ofelia", "Webb", "Manager");
console.log(employee.fullName);
console.log(employee.position);

In ES6, we can now use the class keyword to define a class and a constructor method for its initialization. Regarding the constructor, we can now use the super method to call the parent constructor.

Moreover, implementing getter (i.e. using the get keyword before the function name) and setter (i.e. using the set keyword before the function name) are now very simple. Also the const keyword is now available to define immutable identifier just like what we can see on the fullName setter as we can see in the following snippet:

Snippet 4 - Using the Const Keyword
set fullName(name) {
  const DELIM = " ";
  var names = (name||"").split(DELIM);
  this.firstName = names[0];
  this.lastName = names[1];  	
}

All these enhancements in ES6 related to inheritance, in the background it still using the prototypical way. Remember these are just syntactic sugar.

Changing React Component State

Introduction

React renders component based on any changes on its state (i.e. data).

Updating State with Form Components

In React, a form input component controlled by it is known as controlled component. These components maintains their own state and update itself based on user input as depicted by the following code:

Code 1 - Controlled Text Input
const MessageView = (props) => {
  return(
    <h1>{props.message}</h1>
  );
}

class App extends React.Component {
  state = {
    message : ""
  };

  handleMsgChange = (event) => {
    this.setState({
      message: event.target.value
    });
  }
  
  render() {
    return(
        <div>
          <input type="text" placeholder="Type Something" value={this.state.message} onChange={this.handleMsgChange}/>
          <MessageView message={this.state.message}/>
      </div>
    );
  }
}

ReactDOM.render(<App />, mountNode);

The App component above has a message attribute in state as seen in the following snippet:

Snippet 1 - App Component State
state = {
  message : ""
};

We can bound the message attribute to the text input via the value attribute and listen to the changes on itself using the onChange attribute which in turn updates the state of the message like the following snippet:

Snippet 2 - Controlled Text Input
<input type="text" placeholder="Type Something" value={this.state.message} onChange={this.handleMsgChange}/>

The handleMsgChange that is bound to text input onChange attribute must update the message state of the React App component and could have the following implementation. This makes the React state as the single source of truth.

Snippet 3 - handleMsgChange Implementation
handleMsgChange = (event) => {
  this.setState({
    message: event.target.value
  });
}

This particular implementation will also trigger the re-render of the MessageView component since it is also dependent to the state of the message as we can see in the following snippet:

Snippet 4 - MessageView listening to the State of the Message
<MessageView message={this.state.message}/>

Updating State that may be Asynchronous

React might batch multiple setState function calls into a single update for performance. Thus the correct way to change the React state is by using the setState function that accepts a function instead of object like the following snippet:

Snippet 5 - Updating the React State
this.setState((prevState, props) => {
   //Do the state update here.
})

Let us try to create a reusable ButtonClear component that will clear value of the message attribute with the following snippet.

Snippet 6 - ButtonClear Component
const ButtonClear = (props) => {
  if (props.visible) {
    return(
        <button onClick={props.handleClear}>Clear</button>
    );
  }
  return(null); //Returns nothing
}

Based on the preceding snippet the ButtonClean component will only display the clear button if we tell it to make it visible via its visible attribute. Moreover, it is also expecting to have a handleClear implementation that will be bound to button's onClick attribute.

The handleClear implementation will be provided by the App component like the following snippet with the recommended usage of the setState() function:

Snippet 7 - App Component handleClear Implementation
handleClear = () => {
  this.setState((prevState, props) => {
    return {message: ""}; //Clears the message
  });
}

Thus we can use the ButtonClear component in the App component like the following snippet:

Snippet 8 - Using the ButtonClear in the App Component
<ButtonClear handleClear={this.handleClear} visible={this.state.message!=""}/>

We also instructed it to make the clear button visible only if the message is not empty.

The updated complete code with ButtonClear component can be seen as follows:

Code 2 - The Complete Code
const MessageView = (props) => {
  return(
    <h1>{props.message}</h1>
  );
}

const ButtonClear = (props) => {
  if (props.visible) {
    return(
        <button onClick={props.handleClear}>Clear</button>
    );
  }
  return(null);
}

class App extends React.Component {
  state = {
    message : ""
  };

  handleMsgChange = (event) => {
    this.setState({
      message: event.target.value
    });
  }
  
  handleClear = () => {
    this.setState((prevState, props) => {
      return {message: ""}; //Clears the message.
    });
  }
  
  render() {
    return(
        <div>
          <input type="text" placeholder="Type Something" value={this.state.message} onChange={this.handleMsgChange}/>
          <ButtonClear handleClear={this.handleClear} visible={this.state.message!=""}/>
          <MessageView message={this.state.message}/>
      </div>
    );
  }
}

ReactDOM.render(<App />, mountNode);

Note: We can run both Code 1 and Code 2 using jsComplete Playground

Java Core Functional Interfaces

Description

Functional interface is an interface with a single abstract method (SAM).

List of Core Functional Interfaces

Interface NameArgumentsReturns
BiConsumer<T,U>(T, U)void
BiFunction<T, U, R>(T, U)R
BinaryOperator<T>(T, T)T
BiPredicate<T,U>(T, U)boolean
Consumer<T>Tvoid
Function<T, R>TR
Predicate<T>Tboolean
Supplier<T>T
UnaryOperator<T>TT

Using RxJava PublishSubject Concurrently

Introduction

Implementing hot observable can be achieved with RxJava's PublishSubject. Moreover, we will try to publish data to the subject concurrently safely (i.e. using the synchronized keyword). Thus the complete code at the bottom uses several threads for publishing data and several threads for subscription.

Creating an Instance of PublishSubject

A simple way to create an instance of the PublishSubject instance is by invoking the static method create like the following snippet (see the complete code at the bottom).

Snippet 1 - Invoking the Static Method Create
PublishSubject subject = PublishSubject.create();

Sending Data to PublishSubject

Sending data to a PublishSubject instance can be done by calling its onNext method. Place the actual call in a synchronized block if we are sending data concurrently like the following snippet (see the complete code at the bottom):

Snippet 2 - Sending Data to PublishSubject Instance
synchronized (subject) {
    subject.onNext(String.valueOf(strItem));
}

Subscribing to PublishSubject

To listen to any of the data sent to a PublishSubject instance we can use one of the subscribe method. In addition, we can also opt to use the computation Scheduler (i.e. normally the Scheduler is the one responsible to managing threads in a multi-threaded environment.) via the observeOn method (i.e. for some reason the subscribeOn method is not working with PublishSubject instance.) like the following  snippet (see the complete code at the bottom):

Snippet 3 - Subscribing to PublishSubject Instance
subject.observeOn(Schedulers.computation())
        .subscribe(___item -> {
    System.out.println(_name + " ThreadID:" + Thread.currentThread().getId() + ": " + ___item);
});

The Complete Code

package xyz.ronella.reactive;

import rx.schedulers.Schedulers;
import rx.subjects.PublishSubject;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.*;
import java.util.stream.IntStream;

public class PublishedObservable {

    public static void main(String ... args) {
        final PublishSubject subject = PublishSubject.create();

        List publishers = new ArrayList<>();
        List subscribers = new ArrayList<>();

        int defaultThreadCount = 3;

        ExecutorService executorPublisher = Executors.newFixedThreadPool(defaultThreadCount);
        ExecutorService executorSubscriber = Executors.newFixedThreadPool(defaultThreadCount);

        class LocalPublisher implements Runnable {

            private long _item;
            private String _name;
            private PublishSubject _subject;

            public LocalPublisher(String name, long itemStart, PublishSubject subject) {
                this._name = name;
                this._item = itemStart;
                this._subject = subject;
            }

            @Override
            public void run() {
                try {
                    while(true) {
                        Thread.sleep(1000);
                        String strItem = String.valueOf(++_item);
                        System.out.println("\n" + _name + " ThreadID:" + Thread.currentThread().getId() + ": " + strItem);
                        synchronized (_subject) {
                            _subject.onNext(String.valueOf(strItem));
                        }
                    }
                } catch (InterruptedException e) {
                    System.out.println(_name + " interrupted.");
                }
            }
        }

        class LocalSubscriber implements Runnable {

            private String _name;
            private PublishSubject _subject;

            public LocalSubscriber(String name, PublishSubject subject) {
                this._name = name;
                this._subject = subject;
            }

            @Override
            public void run() {
                _subject.observeOn(Schedulers.computation())
                        .subscribe(___item -> {
                    System.out.println(_name + " ThreadID:" + Thread.currentThread().getId() + ": " + ___item);
                });
            }
        }

        IntStream.rangeClosed(1, 3).forEach(___idx ->
                subscribers.add(executorSubscriber.submit(new LocalSubscriber("Subscriber " + ___idx, subject))));

        IntStream.rangeClosed(1, 6).forEach(___idx ->
                publishers.add(executorPublisher.submit(new LocalPublisher("Publisher " + ___idx,___idx * 100, subject))));

        subject.subscribe( ___item -> System.out.println("Main - ThreadID:" + Thread.currentThread().getId() + " " + ___item));

        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        executorPublisher.shutdownNow();
        executorSubscriber.shutdown();
    }
}

Producer and Consumer Implementations

Introduction

When trying to understand concurrency the producer and consumer implementation is normally the first concept to know. The producer will place in data to a shared resource only if the consumer has already emptied it. Where the producer and consumer are two different workers.

Using Wait and Notify

This is the classic way of implementing producer and consumer. We call the wait method if we want to block the execution and notify if we want to continue. Since theses methods are part of the Object class, every class in java has them. However, to use them the current thread must acquire the a monitor otherwise an IllegalMonitorStateException will be thrown. One of the way to acquire a monitor is to use the synchronized block. This is what we will be using in the following code.

Code 1 - Wait and Notify Implementation
package xyz.ronella.concurrency;

import java.util.Stack;
import java.util.stream.IntStream;

public class ProducerConsumerWaitNotify {

    public static void main (String ... args) {

        final Stack sharedData = new Stack<>();
        final Object monitor = new Object();

        //The data to process.
        final IntStream dataRange = IntStream.range(1, 10);

        Thread producer = new Thread(() -> {

            try {
                dataRange.forEach(___data -> {
                    synchronized (monitor) {
                        try {
                            while (sharedData.isEmpty()) {
                                sharedData.push(___data);
                                System.out.println("Producing " + ___data);

                                monitor.notify();
                            }

                            monitor.wait();
                        } catch (InterruptedException e) {
                            System.out.println("Producer Interrupted.");
                            throw new RuntimeException();
                        }
                    }
                });
            }
            catch (RuntimeException e) {
                System.out.println("Final producer exit.");
            }
        });

        Thread consumer = new Thread(() -> {
            try {
                while (true) {
                    synchronized (monitor) {
                        while (!sharedData.isEmpty()) {
                            System.out.println("Consuming " + sharedData.pop());
                            monitor.notify();
                        }

                        monitor.wait();
                    }
                }
            } catch (InterruptedException e) {
                System.out.println("Consumer Interrupted.");
            }
        });

        consumer.start();
        producer.start();

        Delayer.delay(1000);

        producer.interrupt();
        consumer.interrupt();

        System.out.println("Done");
    }

}

Notice the Delayer.delay(100) (see the Delayer code) near the end of the code. It is only there to delay the exit of the main thread.

Using Lock Conditions

An alternative to implementing producer and consumer is using the lock conditions. Some of the advantages of using Lock interface over synchronized is that it is not required to code it in a single block and can also cross scopes (e.g. you can acquire lock in a method and unlock it in different method.).

To use lock conditions must first create an instance of Lock like the following:

Snippet 1 - Instantiates a Lock
final Lock lock = new ReentrantLock();

Once created, we can now create an instance of Condition for both producer and consumer like the following:

Snippet 2 - Create Producer and Consumer Conditions
final Condition producerCond = lock.newCondition(); final Condition consumerCond = lock.newCondition();

We acquire the monitor using the lock method and release it using unlock method of the Lock instance like the following (see the actual usage on code 2):

Snippet 3 - Acquiring and Releasing a Monitor using Lock Interface
try{
	lock.lock();

	.
	.
	.

}
finally{
	lock.unlock();
}

Knowing all of these, we can now combine them to the following code.

Code 2 - Lock Condition Implementation
package xyz.ronella.concurrency;

import java.util.Stack;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.stream.IntStream;

public class ProducerConsumerLockCondition {

    public static void main (String ... args) {

        final Stack sharedData = new Stack<>();
        final Lock lock = new ReentrantLock();
        final Condition producerCond = lock.newCondition();
        final Condition consumerCond = lock.newCondition();

        //The data to process.
        final IntStream dataRange = IntStream.range(1, 10);

        Thread producer = new Thread(() -> {

            try {
                lock.lock();
                dataRange.forEach(___data -> {
                    try {
                        while (sharedData.isEmpty()) {
                            sharedData.push(___data);
                            System.out.println("Producing " + ___data);

                            consumerCond.signal();
                        }

                        producerCond.await();
                    } catch (InterruptedException e) {
                        System.out.println("Producer Interrupted.");
                        throw new RuntimeException();
                    }
                });
            }
            catch (RuntimeException e) {
                System.out.println("Final producer exit.");
            }
            finally {
                lock.unlock();
            }
        });

        Thread consumer = new Thread(() -> {
            try {
                lock.lock();
                while (true) {
                    if (!sharedData.isEmpty()) {
                        System.out.println("Consuming " + sharedData.pop());
                        producerCond.signal();
                    }

                    consumerCond.await();
                }
            } catch (InterruptedException e) {
                System.out.println("Consumer Interrupted.");
            }
            finally {
                lock.unlock();
            }
        });

        consumer.start();
        producer.start();

        Delayer.delay(1000);

        producer.interrupt();
        consumer.interrupt();

        System.out.println("Done");
    }
}

Using ArrayBlockingQueue

Another implementation is to just the use an instance of ArrayBlockingQueue and use put and take methods.  The put method blocks until a space (i.e. based on the capacity) is available and the take method blocks until there are some data to take. Thus, we don't need any explicit locking for this.

We define the capacity of an ArrayBlockingQueue on its constructor as we can see as part of the following code.

Code 3 - ArrayBlockingQueue Implementation
package xyz.ronella.concurrency;

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.stream.IntStream;

public class ProducerConsumerArrayBlockingQueue {

    public static void main (String ... args) {

        final long delay = 10;
        final BlockingQueue sharedData = new ArrayBlockingQueue<>(1);

        //The data to process.
        final IntStream dataRange = IntStream.range(1, 10);

        Thread producer = new Thread(() -> {

            try {
                dataRange.forEach(___data -> {
                    try {
                        sharedData.put(___data);
                        System.out.println("Producing " + ___data);
                    } catch (InterruptedException e) {
                        System.out.println("Producer Interrupted.");
                        throw new RuntimeException();
                    }
                });
            }
            catch (RuntimeException e) {
                System.out.println("Final producer exit.");
            }
        });

        Thread consumer = new Thread(() -> {
            try {
                while (true) {
                    Thread.sleep(delay);
                    //This will block if the sharedData is empty.
                    int data = sharedData.take();
                    System.out.println("Consuming " + data);
                }
            } catch (InterruptedException e) {
                System.out.println("Consumer Interrupted.");
            }
        });

        consumer.start();
        producer.start();

        Delayer.delay(1000);

        producer.interrupt();
        consumer.interrupt();

        System.out.println("Done");
    }
}

The Delayer Code

package xyz.ronella.concurrency;

public class Delayer {

    private Delayer() {}

    public static void delay(long millis) {
        final Object mainMonitor = new Object();
        Thread mainTimer = new Thread(() -> {
            try {
                Thread.sleep(millis);
                synchronized (mainMonitor) {
                    mainMonitor.notify();
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });
        mainTimer.start();

        synchronized (mainMonitor) {
            try {
                mainMonitor.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

}