A Reminder App using JavaFX
April 15, 2017
1. Overview
This is an example reminder application built using JavaFX. This app has functions to add, update, delete reminders and notify when a reminder is due. The reminder data is stored in HSQL relational database. The app accesses the database using Spring JDBC (Java Database Connectivity).
Java SE 8, JavaFX 8, Spring 4.3.5 and HSQLDB 2.3.2 are used to build the app.
The finished app's screenshot is as follows:

The app's creation (a.k.a. code) is explained in following sections:
- The GUI: Main containers, components and their layout
- The App: Details of the components in main app
- Reminder Functions: Create, update, delete and notify
- Database: Download, install, create database and table
- Spring Configuration and Database Access: Application configuration and JDBC database access
- Application Starter: JavaFX application launcher program
The Java source code for the finished application can be downloaded from the Download section below.
2. The GUI
The app has a main window. This has a table with all the reminders and their details. New reminders can be created. A reminder can be selected from the table, edited or deleted. There are buttons to create, update or delete reminders. The reminder info is entered and edited in a reminder dialog.
The reminders are grouped and the predefined group names are listed in the app's main window; and on selecting a group the reminders related to that group are displayed in the table.
The app's GUI is built using the JavaFX 8; this is part of Java SE 8.
2.1. Main Window
The controls (or widgets) in the main window are grouped in two javafx.scene.layout.VBox
es (a layout where controls are placed vertically).
The first VBox
has the reminder group list and a calendar. The list is a javafx.scene.control.ListView
and the calendar a LocalDatePicker
(this is from JFXtras library, https://jfxtras.org/). The LocalDatePicker
allows the user to select a date on which a new reminder is to be created or just browse through the calendar.
The second VBox
contains a set of javafx.scene.control.Button
controls for new, update and delete reminder functions, the javafx.scene.control.TableView
in which the reminders are listed, and a javafx.scene.text.Text
control for status messages.
The two VBox
es are added to a javafx.scene.layout.HBox
(controls are placed horizontally). This completes the GUI layout for the app's main window.
2.2. Reminder Dialog
The reminder's info is entered or updated in a javafx.scene.control.Dialog
. This dialog is opened when a user clicks the new or update buttons in the main window. The following are the screenshots for the new and update reminder dialogs respectively:


In the dialog, the reminder name is entered in a javafx.scene.control.TextField
, the priority is checked in a javafx.scene.control.CheckBox
and the notes is entered in a javafx.scene.control.TextArea
. The javafx.scene.control.DatePicker
control allows the user to select a reminder date from a calendar popup. There is a JFXtras's (https://jfxtras.org/) LocalTimePicker
control to set the time by sliding the hour and minute knobs.
In case of updating an existing reminder, there is an additional completed CheckBox
in the dialog.
2.3. Reminder Notification
The app displays an alert dialog whenever a reminder is due. The alert dialog is a modal javafx.scene.control.Alert
control.

2.4. The App's GUI Class
The app's main GUI is built in the AppGui.java
class. This has code to build, configure and layout the controls, and populate the appropriate controls with the data from the database. The following sections has the detailed description.
This class defines the event handlers for the add/update/delete buttons, the reminder group list selection listener, reminders table configuration and the initiating the reminders task to find due reminders.
This class has reference to various classes which provide functionality like data access, build other GUI components like the reminder entry dialog and the reminders task function.
Link to view source code: AppGui.java
3. The App
3.1. The Reminder
A reminder has the following attributes: name with 5 to 25 characters, notes with up to 200 characters, date, time, priority flag and completed flag. The reminder is defined in the Reminder.java
class.
A reminder is unique for a given name and date. The newly created reminder date and time must be in future.
The reminder's date and time fields are captured in the app as java.time.LocalDate
and LocalTime
respectively. The LocalTime
has a precision of nanoseconds (10:23:45.27891161) by default. But, the app stores the time truncated to minutes (10:23). The following code snippet shows how it's done.
LocalTime truncatedToMinutes =
LocalTime.now().truncatedTo(java.time.temporal.ChronoUnit.MINUTES));
Link to view source code: Reminder.java
3.2. Reminder Group List
This is a read-only ListView
in the main window with reminder groups as items. The groups are Reminders, Today, Overdue, Completed and Priority. On selecting a group item in the list the filtered reminders are shown in the table. The default item is Reminders and its selection shows all reminders in the database.

The reminder groups are defined as an enum ReminderGroup
. The enum, in addition to the constant definitions, has a list of reminder group string values (for example, PRIORITY constant has a string value of "Priority") which are used to populate the reminder group list.
The reminder group list is created and populated as follows in the AppGui
:
ListView<String> list = new ListView<>(ReminderGroup.getAsFormattedStrings());
The enum also maintains a Map
collection of the string key and enum constant values for lookup. There are corresponding getter methods to access the list and the map data respectively.
Link to view source code: ReminderGroup.java
3.3. Local Date Picker
The calendar in the app is built using the LocalDatePicker
from JFXtras.

The date selected from this control is retrieved and is used in reminder dialog for new reminder entry. In case of no selection (or unselected) the selectedDate
value will be null. In such case the selectedDate
is set to today's date in this app.
The following code snippet shows the date picker creation and getting the date from it:
LocalDatePicker picker = new LocalDatePicker();
LocalDate selectedDate = picker.getLocalDate();
3.4. Reminders Table
The reminders are listed in a table. The TableView
with the reminder info has all the columns for a reminder, except the notes text. The date and time columns are formatted as dd.MMM.yyyy and HH:mm respectively. The priority and completed information is rendered as CheckBox
es. The table is not editable.
The table columns can be re-positioned, resized or sorted; these are the default settings. A javafx.scene.control.Tooltip
shows the reminder notes (up to first 100 characters of it) when the mouse pointer is hovered over a row.

The table is built and populated with all the reminders at the start of the app in the AppGui
as shown in the code snippet:
TableView<Reminder> table = new TableView<>();
table.setItems(dataAccess.getAllReminders());
Whenever a reminder is added, updated or deleted the database is updated and the table is refreshed with updates. The later section Reminder Functions has details.
3.4.1. Date and Time Column Formatting
The reminder date is stored as a LocalDate
in the app. The date column is formatted and shown as, for example 24.Mar.2017.
The formatting is achieved by setting the javafx.scene.control.TableColumn
's cell factory to provide the custom formatting the date value. A cell factory is responsible for rendering the data contained within a javafx.scene.control.TableCell
for a table column. The cell factory is a javafx.util.CallBack
's call()
method which accepts the table column and returns a formatted cell. CallBack
is a functional interface.
The following code snippet shows the date column definition and the cell configuration:
TableColumn<Reminder, LocalDate> dateCol = new TableColumn<>("Date");
dateCol.setCellFactory(column -> cellFactories.getTableCellWithDateFormatting());
The getTableCellWithDateFormatting()
method of TableViewRowAndCellFactories.java
returns a customized TableCell
which formats the LocalDate
value of the reminder. The date is formatted using the java.time.format.DateTimeFormatter
:
localDate.format(DateTimeFormatter.ofPattern("dd.MMM.yyyy"))
The reminder time is defined as LocalTime
. The reminder time table column is formatted in a similar way by customizing the column's cell factory; the time value is displayed for example as 10:25.
Link to view source code: TableViewRowAndCellFactories.java
3.4.2. Rendering CheckBox in Table Column
The boolean value in the priority and completed columns are rendered as a CheckBox
. The boxes are checked or unchecked depending the values true or false respectively. This rendering is achieved by setting the TableColumn
's cell factory to provide the custom rendering.
completedCol.setCellFactory(column -> {
CheckBoxTableCell<Reminder, Boolean> cell = new CheckBoxTableCell<>();
cell.setAlignment(Pos.CENTER);
return cell;
});
The cell factory is a CallBack
's call
method which returns a javafx.scene.control.cell.CheckBoxTableCell
which is aligned at the center of the column. The CheckBoxTableCell
is a class containing a TableCell
implementation that draws a CheckBox
inside the cell.
3.4.3. Table Row Tooltip
A javafx.scene.control.Tooltip
shows the row's reminder note text. This is achieved by setting the row factory for the table with a customized javafx.scene.control.TableRow
.
table.setRowFactory(tableView -> cellFactories.getTooltipTableRow());
The row factory is a CallBack
's call()
method's return value, a customized TableRow
to show the tooltip with notes value. cellFactories
is an instance of TableViewRowAndCellFactories.java
.
Link to view source code: TableViewRowAndCellFactories.java
4. Reminder Functions
At the app's start all the reminders stored in the database are retrieved and are listed in the table.
Reminders are created, updated or deleted. There are three buttons: new, update and delete. The javafx.event.EventHandler
s handle the events of the three button actions respectively. Additionally, selecting a reminder row in the table and double-clicking it triggers the update function. The button click and the mouse double-click action event handlers are defined in the AppGui
class.

4.1. New Reminder
The new button click action triggers creating a new reminder.
newBtn.setOnAction(actionEvent -> newReminderRoutine());
The newReminderRoutine()
method has code to show a dialog to enter the new reminder details and then insert the reminder data in the database. The following code snippet is from the newReminderRoutine()
:
Dialog<Reminder> dialog = reminderDialog.create(reminderDate);
Optional<Reminder> result = dialog.showAndWait();
The reminderDate
is the date selected from the LocalDatePicker
control in the app. The new reminder dialog is displayed as follows:

The dialog is created and returned from ReminderDialog.java
class's create()
method. This class builds the dialog with the controls and their attributes like size, tooltips, initial values and validation criteria.
The dialog's showAndWait()
method displays the dialog and waits for user response (click Okay button or click 'x' to cancel). As the Okay button is pressed the dialog's data are validated and in case of not valid data a modal alert dialog displays an appropriate message (as shown below).

If the entered data is valid, a new Reminder
instance is built and is returned as a java.util.Optional<Reminder>
value. The Reminder
's info is added to the database and the app's table is refreshed to show the newly created reminder.
In the following code snippet, result
is the Optional<Reminder>
.
if (result.isPresent()) {
dataAccess.addReminder(result.get());
refreshTable();
...
}
Link to view source code: ReminderDialog.java
4.2. Update Reminder
The update action is triggered by either clicking the update button or double-clicking the reminder row in the table. The update dialog is similar to that of the new function, except that it has an additional control - the completed check box.

The mouse double-click event is set for the table: table.setOnMousePressed()
method accepts a function to be called when a mouse button is pressed on this node.
table.setOnMousePressed((MouseEvent me) -> {
if ((me.isPrimaryButtonDown()) && (me.getClickCount() == 2)) {
updateReminderRoutine();
...
The updateReminderRoutine()
method is common for the update button press action event and mouse double-click event; the following is the code snippet from the method:
Reminder rem = table.getSelectionModel().getSelectedItem();
int ix = dataAccess.getAllReminders().indexOf(rem);
...
Dialog<Reminder> dialog = reminderDialog.create(rem);
Optional<Reminder> result = dialog.showAndWait();
if (result.isPresent()) {
dataAccess.updateReminder(ix, result.get());
refreshTable();
...
The updated reminder information is shown in the refreshed table in the app.
4.3. Delete Reminder
The delete button when clicked prompts a confirm alert to delete the selected reminder and on confirmation deletes the reminder from the app's table and the database.
The following code snippets show the delete button configuration and the deleteReminderRoutine()
method which is triggered from clicking the delete button respectively.
Button delBtn = new Button("Delete");
delBtn.setOnAction(actionEvent -> deleteReminderRoutine());
Reminder rem = table.getSelectionModel().getSelectedItem();
Alert confirmAlert = getConfirmAlertForDelete(rem);
Optional<ButtonType> result = confirmAlert.showAndWait();
if ((result.isPresent()) && (result.get() == ButtonType.OK)) {
dataAccess.deleteReminder(rem);
...
4.4. Reminder Notification Mechanism
The reminders when due are shown in an alert dialog.

The notification mechanism is implemented using the Timer
and TimerTask
API of java.util
package. Timer is a facility for threads to schedule tasks for future execution in a background thread. Tasks may be scheduled for one-time execution, or for repeated execution at regular intervals.
The task is defined by extending a TimerTask
. This is an abstract class which implements Runnable
; the run()
method has code for the action of this task.
There are two tasks defined for reminder notification. The first one is run once at the start of the app; this checks for all the overdue reminders and shows them in reminder alerts.
The next task is scheduled to run at a regular interval, every minute for the duration of the app. This checks for the reminder due at that minute and shows them in the alerts. It is possible to show multiple reminders at the same time.
The tasks are initiated at the start of the app in the AppGui
as shown in the following code snippet.
Timer timer = new Timer();
CheckRemindersTask tasks = new CheckRemindersTask();
timer.scheduleAtFixedRate(tasks, zeroDelay, periodOneMinute);
4.4.1. Reminders Task
The class CheckRemindersTask.java
extends the TimerTask
and overrides the run()
abstract method. The run()
method has code to check for due or overdue reminders and display the reminder alert.
In the following code snippet the getDueRems()
method retrieves all the due reminders based on the supplied predicate. Initially, the predicate value is set as: Predicate<Reminder> predicate = ReminderPredicates.OVER_DUE;
this notifies all the due reminders until the start of the app, all overdue reminders.
public void run() {
List<Reminder> dueRems = getDueRems();
showNotifications(dueRems);
predicate = ReminderPredicates.DUE_NOW;
}
private List<Reminder> getDueRems() {
ObservableList<Reminder> rems = dataClass.getAllReminders();
return rems.stream()
.filter(predicate)
.collect(Collectors.toList());
}
After the first run of the task, the predicate is reset to ReminderPredicates.DUE_NOW
and is run at a regular interval.
Link to view source code: CheckRemindersTask.java
4.5. Reminder Predicates
A java.util.function.Predicate<T>
is a functional interface and represents a predicate (a boolean-valued function) of one argument (T
is the input type).
This app uses predicates to filter reminders in two functions: (i) To display filtered reminders based on selecting a reminder group, and (ii) in the reminder notification task to get due reminders. These predicates are defined in a class ReminderPredicates.java
and are accessed as public static members. The following is a code snippet from the class:
Predicate<Reminder> COMPLETED = r -> (r.getCompleted() == true);
Predicate<Reminder> PRIORITY = r -> (r.getPriority() == true);
Predicate<Reminder> TODAYS = r -> r.getDate().isEqual(LocalDate.now());
Predicate<Reminder> TIME_NOW = r -> r.getTime()
.equals(LocalTime.now()
.truncatedTo(ChronoUnit.MINUTES));
Predicate<Reminder> DUE_NOW = TODAYS.and(TIME_NOW).and(COMPLETED.negate());
...
Link to view source code: ReminderPredicates.java
4.6. Data Access
The class DataAccess.java
provides access to the data in the app. There are methods to get reminders, add, update or delete reminders. These methods are used with the app functions.
This class acts as a data access layer and interacts with the database access code. This class also maintains all the reminders in a collection.
The following code snippet shows the reference to the database access class DatabaseJdbcAccess
, the local reminder store, the read and insert methods.
private DatabaseJdbcAccess dbAccess;
private ObservableList<Reminder> remData;
public ObservableList<Reminder> getAllReminders() {
return remData;
}
public void addReminder(Reminder rem) {
remData.add(rem);
dbAccess.addReminder(rem);
}
Link to view source code: DataAccess.java
4.6.1. Data for Reminder Group
In the app, when an item (group) is selected in the reminder group list, the corresponding group's reminders are shown in the app's table. A group's reminders are arrived at in the getTableDataForGroup()
method of DataAccess
class.
The following is the code snippet from the javafx.beans.value.ChangeListener
's changed()
method of reminder group ListView
.
String groupStr = list.getItems().get(ix); // ix = selected list item's index
ReminderGroup group = ReminderGroup.getGroup(groupStr);
table.setItems(DataAccess.getTableDataForGroup(group));
The following is the code snippet from the DataAccess
's getTableDataForGroup()
method:
public ObservableList<Reminder> getTableDataForGroup(ReminderGroup group) {
Predicate<Reminder> remPredicate = null;
switch (group) {
case PRIORITY:
remPredicate = ReminderPredicates.PRIORITY;
break;
case TODAY:
remPredicate = ReminderPredicates.TODAYS;
break;
...
default:
remPredicate = ReminderPredicates.ALL;
}
List<Reminder> result = remData.stream()
.filter(remPredicate)
.collect(Collectors.toList());
return FXCollections.observableList(result);
}
5. Database
The HSQL database is used for storing the reminders data. HSQLDB (HyperSQL DataBase) is a SQL relational database software written in Java and runs in a JVM. It is a small, fast, multithreaded and transactional database engine and supports embedded as well as server modes. It includes a JDBC driver.
5.1. Download and Install
Download the database software from the download link at the website http://hsqldb.org/. In this app, the HSQLDB version 2.3.2 is used. The downloaded file is a ZIP file. Extract the ZIP file into any directory of your choice. The ZIP file is extracted into a folder hsqldb-2.3.2\hsqldb
. This is the home (or install) directory.
This completes the installation. The installed database has user documentation, JDBC driver, database executables and utility programs. The install directory has /doc
and /lib
directories.
The doc
directory has the user guides. The lib
directory has the following JAR files used commonly:
- hsqldb.jar: This has the database engine, JDBC driver and a GUI database access tool.
- sqltool.jar: This has a SQL command line database access tool.
5.2. Create Reminders Database
The GUI database access tool is used to create and access the database. From the Windows DOS command prompt run this:
> java -cp hsqldb.jar org.hsqldb.util.DatabaseManagerSwing
This opens a Connect GUI dialog as shown below. Note that the hsqldb.jar
file is to be in the classpath.

- Recent Setting: <Do not select anything now.> (Next time to connect to the database select the setting created now.)
- Setting Name: <enter a name> reminders_db_setting
- Type: <select> HSQL Database Engine Standalone
- Driver: <select> org.hsqldb.jdbcDriver
- URL: <enter database file path> jdbc:hsqldb:file:<filepath> (see note below for details about how to specify the path)
- User: <leave blank>
- Password: <leave blank>
NOTE on URL's filepath: The filepath can be specified as a relative or an absolute path. The relative path is relative to the current directory; for example jdbc:hsqldb:file:db\remindersDB
in the URL creates a directory called db
and the remindersDB
database in it. An example with absolute path is jdbc:hsqldb:file:D:\jqp\examples\db\remindersDB
.
Click Ok. This creates a database named remindersDB
in the specified directory. This also opens the HSQL Database Manager window. The window has areas showing the database structure, SQL entry and result details. The window is shown below in the following section.
IMPORTANT: The Driver, URL, User and Password information entered above in Connect dialog must be used to configure the DataSource
object in the app (details follow in later section - Database Access using Spring JDBC).
5.3. Create Reminders Table
In the HSQL Database Manager window enter the following SQL DDL script and execute it.
CREATE TABLE REMINDERS_TABLE (
name VARCHAR(50) NOT NULL,
notes VARCHAR(500) NOT NULL,
date DATE NOT NULL,
time TIME NOT NULL,
priority BOOLEAN NOT NULL,
completed BOOLEAN NOT NULL,
PRIMARY KEY (name, date))
);
This creates the reminders data table. The newly created table can be viewed in the database structure area as seen in the picture below.

IMPORTANT: Note that this step need to be completed before running the app. The app's code assumes that the reminders database and the table are created.
6. Spring Configuration
The Spring Framework (https://spring.io/) is an open source application framework for Java. The framework's core (core-module) features are used with this application. Spring's JDBC data access module is used to work with the database access.
The software can be download from this link: http://repo.spring.io/release/org/springframework/spring/. Find the release you need and download the 'dist' ZIP file; this has the JAR files and the apidocs.
The application uses the 4.3.5.RELEASE version of Spring.
6.1. Spring Java Configuration
The Spring container takes the responsibility of creating the beans in the application and co-ordinating the relationships between those objects via dependency injection. In this app the bean configuration is specified using Java-based configuration.
The annotations used are @Configuration and @Bean of org.springframework.context.annotation
package. Annotating a class with @Configuration indicates that its primary purpose is a source of bean definitions. The @Bean annotation is used to indicate that a method instantiates and configures a new object to be managed by the Spring container.
The app defines two configuration classes: AppConfig.java
and DatabaseJdbcConfig.java
. The AppConfig
is the primary configuration and imports database related bean definitions from the DatabaseJdbcConfig
. The configurations use constructor-based dependency injection.
The following is a code snippet from the AppConfig
configuration class:
@Configuration
@Import(DatabaseJdbcConfig.class)
public class AppConfig {
@Bean public AppGui appGui(DataAccess dataAccess,
CheckRemindersTask checkRemindersTask, ReminderDialog reminderDialog) {
return new AppGui(dataAccess, checkRemindersTask, reminderDialog);
}
@Bean public ReminderDialog reminderDialog(DataAccess dataAccess) {
return new ReminderDialog(dataAccess);
}
...
Link to view source code: AppConfig.java
6.2. Database Access using Spring JDBC
The app uses Spring JDBC to connect and access the reminders database. The API used in this app are org.springframework.jdbc.core.JdbcTemplate
and RowMapper
. These are used for the create, update, delete and read database operations.
The classes related to the database access and their dependencies are configured in a Spring's Java-configuration class DatabaseJdbcConfig
. The configuration defines and produces the beans including java.sql.DataSource
, JdbcTemplate
and the DatabaseJdbcAccess
.
The following code snippet shows the configuration of DataSource
in the DatabaseJdbcConfig
:
@Configuration public class DatabaseJdbcConfig {
private static final String JDBC_DRIVER = "org.hsqldb.jdbc.JDBCDriver";
/* ifexists=true connection property disallows creating a new database.*/
private static final String CONNECTION_URL =
"jdbc:hsqldb:file:db/remindersDB;ifexists=true;";
@Bean
public DataSource dataSource() {
SingleConnectionDataSource ds = new SingleConnectionDataSource();
ds.setDriverClassName(JDBC_DRIVER);
ds.setUrl(CONNECTION_URL);
ds.setUsername(""); ds.setPassword("");
return ds;
} ...
Note that in the above code the driver, URL, user and password are same as the ones used in creating the reminders database, in Database section.
In the above code snippet the DataSource
is an implementation of org.springframework.jdbc.datasource.SingleConnectionDataSource
. This wraps a single JDBC java.sql.Connection
which is not closed after use. In this app, the database shutdown routine in DatabaseJdbcAccess
uses the open connection to close the database.
Link to view source code: DatabaseJdbcConfig.java
6.2.1. Database Jdbc Access
The DatabaseJdbcAccess.java
class has methods for querying and updating the reminders data in the database using the Spring JDBC. The following code snippet shows the functions to query all and insert the reminders respectively:
public List<Reminder> getAllReminders() {
return jdbcTemplate.query("SELECT * FROM REMINDERS_TABLE",
new RowMapper<Reminder>() {
@Override public Reminder mapRow(ResultSet rs, int rowNum)
throws SQLException {
Reminder r = new Reminder();
r.setName(rs.getString("name"));
r.setNotes(rs.getString("notes"));
r.setDate(rs.getDate("date").toLocalDate());
...
return r;
}
...
public void addReminder(Reminder r) {
jdbcTemplate.update("INSERT INTO REMINDERS_TABLE VALUES (?, ?, ?, ?, ?, ?)",
r.getName(),
r.getNotes(),
...
r.getCompleted());
}
Link to view source code: DatabaseJdbcAccess.java
6.2.2. Date and Time Data Conversion
The reminder's date and time are stored in the reminders database table of HSQLDB are of type Date
and Time
respectively. These are compatible with the java.sql.Date
and Time
respectively.
The app captures the reminder date and time as java.time.LocalDate
and LocalTime
respectively. The LocalDate
to database's Date
and vice-versa conversion is done using the following java.sql.Date
class's methods, for example:
LocalDate input = LocalDate.now();
Date date = Date.valueOf(input);
LocalDate localDate = date.toLocalDate();
The java.sql.Time
class has methods with similar function: valueOf()
and toLocalTime()
.
6.3. Database Exception Handling
The app handles the database exceptions. Spring's org.springframework.dao.DataAccessException
, an unchecked exception, is thrown by the database access JDBC API.
The app's database access is for getting all reminders, adding, updating and deleting reminders. The DataAccessException
is handled in the app's GUI layer, in the AppGui
class. Whenever there is a database related exception an error alert is displayed with a message and then the app is closed.
7. Application Starter
The AppStarter.java
launches the reminders app.
The class extends javafx.application.Application
and overrides its start()
abstract method, which is the main entry point for the JavaFX application. The start method has code to load the Spring's application context and show the GUI.
@Override public void start(Stage primaryStage) {
context = new AnnotationConfigApplicationContext(AppConfig.class);
AppGui appGui = context.getBean(AppGui.class);
Parent mainView = appGui.getView();
primaryStage.setScene(new Scene(mainView, 925, 450));
primaryStage.show();
}
This class also has code that runs at the application close, to destroy any open resources. The Application
class's overridden stop()
method has code to close the Spring's application context, shutdown the database and cancel the reminders task timer.
@Override public void stop() {
context.getBean(DatabaseJdbcAccess.class).shutdownDatabase();
context.getBean(AppGui.class).getTimer().cancel();
context.close();
}
Link to view source code: AppStarter.java
A screenshot of the reminder app:

8. Download
Download the Java source code for the example: reminder-app.zip
9. Useful Links
- Java SE 8 API: https://docs.oracle.com/javase/8/docs/api
- JavaFX 8 API: https://docs.oracle.com/javase/8/javafx/api/toc.htm
- JavaFX: http://docs.oracle.com/javase/8/javase-clienttechnologies.htm
- JFXtras: https://jfxtras.org/
- HSQL Database: http://hsqldb.org/
- Spring Framework: https://spring.io/