Throwing and Catching Exceptions
The goal of this lab is to practice throwing and catching exceptions as well as creating your own exceptions rather than relying only on Java's built in exceptions. In general in an MVC designed application, the Model is the part that discovers the errors but does not know how to handle them because it does not know what context the View is providing (command-line, desktop, web, mobile, etc.). By throwing an exception when an error is discovered the Model can communicate with the View specifically what error has occurred and give the View a chance to catch the exception and handle it appropriately.
Getting Started
For this lab exercise, it is suggested that you work together with one other person in the class:
- One person should fork the original lab_exceptions repository into your own account to provide a place to edit, commit, and push changes
- On the resulting project web page, go to
Settings -> Membersto add your partner so both people in the group can access the same repository - Search for your partner's name and give them a
Maintainerrole in the project and chooseAdd to Project - Both students should clone the one forked repository to their individual machines so they can work on their own separate copies
- Import the project into IntelliJ (
New -> Project From Existing Sources)using the folder you just created (and just accept the defaults for all the options by selectingNext)
- Once the project window appears, you will need to do a few more steps to get it to run:
- Select
File -> Project Structureand then selectLibrarieswithin the dialog that appears - Right click on the correct library for your platform (i.e., one of
linux,mac, orwin) and selectAdd to Modules - Click
OKin the dialog that appears andOKagain to close the Project Settings dialog - Click to expand the folders
src -> browserto find the Java classMain - Right click on this class to run it by selecting "Run Main.main()"
- Select
- Run the program to verify that your Java installation is working.
If there are compilation errors or it does not run, then the project is not correctly configured.
Throw Exceptions Instead of Returning Null
In general, returning null is a sure way to cause an error somewhere else in the code! However, the BrowserModel currently returns null whenever an error occurs (e.g., it receives a bad URL, an attempt is made to go outsides the bounds of the history, or a bad favorite is asked for). Sometimes the BrowserView checks for null and sometimes not.
In each public method where the BrowserModel returns null, change the code to throw a newly created standard Java Exception instead, like new IllegalStateException() or new IllegalAccessException(). In the BrowserView class, change the code to surround each call to those Model methods with a try-catch block that catches the exact type of exception thrown (rather than the general class Exception). Handle the thrown exceptions by displaying an alert dialog to the user with a useful error message (i.e., call the method showError() in the catch block).
Test the code by running the Main class and entering a bad URL into the text field.
Once you think you have this part working, use GIT to add and commit the changes you have made.
Create Custom Exception
Instead of repurposing standard Java exceptions to fit your project, you can create your own kind of exception (sometimes it may be appropriate, but often there is not one that quite fits your real issue). In other cases, many Java libraries throw a single general exception type using a message that describes the error rather than the class name (typically branded after their project like SpringException).
Create a custom Exception class, called BrowserException, to be thrown instead of the standard Java exceptions you chose in the previous part. To do this, create a new class just like you would any other and make it extends RuntimeException. Then let IntelliJ help you by generating the standard constructors by highlighting the class name in the BrowserException file, right-clicking and selecting "Generate" and then "Constructor". From the list of possible constructors, select both the second and third ones (String and String, Throwable). That's it — you have now created a class that can be thrown and caught as any other standard Java Exception!
Then replace each throw in the BrowserModel class to throw new BrowserException() with an appropriate error message to explain the issue (take the message from those you displayed in the BrowserView class in the previous part). You will also need to change the catch blocks in the BrowserView to catch a BrowserException instead of the standard Java exception you chose. Then you will need to change the call to showError() to call the BrowserException's getMessage() method instead of using the hard coded string parameter as before.
Finally, it is good practice to declare the exceptions that could possibly be thrown by your method to help communicate your intent to users of your code. To each public method in BrowserModel that may throw a BrowserException, add the code "throws BrowserException" to the end of its signature (between the parameter's end parentheses and the definition's starting curly brace).
Test the code by running the Main class and entering a bad URL into the text field.
Once you think you have this part working, use GIT to add and commit the changes you have made.
Use Resource Files Instead of Hardcoded Strings
Just like button names, error messages should be read in from a Java resource properties file (in general, anything that might need to be translated or changed easily should be in a separate non-Java file). In this part, change any error messages you created in the previous step so that they come from a resource file instead of using hard coded strings.
Create a new properties file, named ErrorsEnglish.properties, that has a key=value format for each error message. In this case, the key will be what you refer to in your code and the value will be the message currently hardcoded in the BrowserModel. Then, in the constructor for the BrowserModel class (like in the constructor for the BrowserView class), create a ResourceBundle from the properties file to use to retrieve the message values. Note, you will need to add a parameter to the BrowserModel constructor that represents the error message's "language" that is passed in when it is constructed in the Main class (again like the BrowserView class).
Then you will need to replace each hardcoded string message used to construct the thrown BrowserException by calling the ResourceBundle's getString() method to get the value from the properties file. Note, when doing this, if you used a dynamic value from the code (like the URL given as a parameter to the go() method), you will need to change your message string to be a single value (not something concatenated together as you might usually write). You can use the String.format() method to accomplish this, substituting a "%s" in the one combined string stored in the properties file where you want the URL's value to appear, like this:
BadURL="Could not load URL %s"
Test the code by running the Main class and entering a bad URL into the text field. Further test this code by creating a second properties file, named ErrorsGibberish.properties, that has a different set of values for the error messages. Change the "language" used in the Main class and verify that the new message is displayed when a bad URL is entered.
Once you think you have this part working, use GIT to add and commit the changes you have made.
Submission
At the end of class, use Gitlab's Merge Request to submit your group's refactored code to the original organization repository. Make sure both people's NetIDs are in the title of your Merge Request.