In this seminar you will fix and remedy some broken or omitted thins

Task 0: Update your repository

For this seminar, you will work with the same forked repository as previously. We assume you already have the upstream remote set from Seminar 03 (if not, please follow task 0.1 from that seminar).

0.1: Update your main branch

First, you need to fetch new changes from the upstream repository and update your local main branch:

git switch main
git pull upstream main

Then you need to update the main branch in your forked repository:

git push origin main

0.2: Create a new branch for this seminar

Again, each pair creates a new branch on a single computer as they are working together as one person during the seminar.

Pair 1 creates a new branch seminar09-pair1:

git switch -c seminar09-pair1

Pair 2 creates a new branch seminar09-pair2:

git switch -c seminar09-pair2

0.3: Go through code changes

The structure and composition of the code base changed slightly since the last seminar. Familiarise yourself with these changes. They mainly consists of database layer (transactional management), logging, and exception reporting.

Task 1: You may have to go nuclear

Start the application, if you get an error – which you likely are if you’ve previusly started the app on this system. If that happen, do not forget to insert dev data into database again, similary as in the previous seminar Task 3.

Task 2: Transactional Import

New class appeared – TransactionExecutor. Discover what methods this class has. Use this class and its TransactionExecutor#executeInTransaction in a proper place.

Task 3: Logging

Application currently doesn’t provide much information when something goes wrong. The project is already comes preconfigured with tinylog library.

You can find two configuration files

tinylog.properties
tinylog-test.properties

Task 3.1: Add logging

In order to improve debugging, add logging to BatchCsvImporter. Think about what information should be logged and what severity level to use.

Task 3.2: Improve the logging format

As you can see the logging messages currently do not offer too much information. Reconfigure the logger so that the messages look similarly to this:

20:51:48 cz.muni.fi.pv168.employees.data.storage.db.DatabaseManager.createProductionInstance()
INFO: JDBC connection URI: jdbc:h2:~/pv168/db/employee-records;DB_CLOSE_DELAY=-1;DATABASE_TO_UPPER=false

Note: The above is an example of how the log message in DatabaseManager#createProductionInstance() should look once you change the logger configuration.

Hint: This task should make you think about what should be part of log entry. It’s not strictly required that you set your logger exactly like this.

Task 3.3: Add a writer

Logging to console is great, however in production it’s often impractical. Define another log writer which will write the log messages to file under ${HOME}/pv168/logs/ directory.

Task 3.4: Add logging into CRUD services

Use our logging functionality in CRUD services.

Task 4: Add validator

The customer requests to validate employees by their age. From now on, no employee below age 18 cannot be inserted in the application.

Task 4.1: Add test of that validator

To be sure that the validator works, add a test for it. Before you start, think about how it should be tested.

Time tests are quite tricky, so you might want to use some mocking. To do so, your validator must be able to accept a Clock instance.

Task 5: Error handling

Another area which is not covered by the current implementation of Employee Records is error handling. Every application needs to deal with the following types of errors

  • Expected runtime erros
  • Unexpected recoverable errors
  • Unexpected non-recoverable errors

Discuss examples of each type

Task 5.1: Display validation errors

Add employee validation to add/edit action dialog, displaying poissible validation errors. User should be able to address these erros without losing current input data.

Task 5.2: Handle all errors

The application currently contains an implementation of Thread.UncaughtExceptionHandler which globally handles any uncaught exception. However there are few remaining issues regarding exception handling

  1. Currently ApplicationErrorHandler handles all exceptions as fatal
  2. Currently ApplicationErrorHandler displayes only a generic error message
  3. In some cases FatalStorageException should be used instead of regular StorageException

Improve the implementation of ApplicationErrorHandler to address the abovementioned issues.

Hint: Arbitrary exceptions should be always considered unrecoverable (fatal). Instance of ApplicationException is unrecoverable if and only if it also implements FatalError interface.