Seminar 09 - Transactions, Logging, Error Handling
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
- Currently
ApplicationErrorHandler
handles all exceptions as fatal - Currently
ApplicationErrorHandler
displayes only a generic error message - In some cases
FatalStorageException
should be used instead of regularStorageException
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.