Start improving code quality of existing applications now
Exception handling
With the introduction of exception handling in Uniface service pack 10.4.02, writing robust and maintainable code has become a lot easier. Exceptions are the same as $procerror; both are caused by a Uniface runtime error. However, what happens after the runtime error occurs differs greatly between classic error handling and exception handling, and has a big impact on the robustness of your code. In the classic situation, the runtime error sets $procerror after which code execution continues. It is the responsibility of the developer to inspect $procerror after every relevant ProcScript instruction to make sure no errors are ignored. Let's be honest, this does not happen... enough! As a consequence, most instructions are not followed by any error handling and that increases the chance of ignored runtime errors. Did you know that most security breaches are caused by a lack of error handling code?
Uniface exception handling in a nutshell
With exception-enabled code, the way Uniface reacts to runtime errors is turned upside down: all runtime errors cause an exception and exit the module! Wow, that is very drastic. Naturally, sometimes a runtime error is expected, and you may want to catch the error and continue code execution. This is of course possible.
Let’s have a quick look at the instructions related to exceptions as made available in 10.4.02:
- throws—declares a ProcScript module as exception enabled; all runtime errors stop code execution and exit the module with an exception (unless caught – see the other instructions in this list)
- try-endtry—defines a block of code as exception enabled; any runtime errors caused by an instruction within that try-endtry block stops code execution and jumps to the nearest catch block
- catch—defines a block of code that will be executed when a specific exception (runtime error) occurs
- throw—statement that allows you to throw a custom exception
- rethrow—statement that allows you to rethrow the caught exception from its catch block (added in 10.4.02.005)
- finally—defines a block of code that will always be executed, regardless of whether an exception occurred or not. Typically used to perform some clean-up actions (added in 10.4.02.003).
entry updateDescription
throws ; Exception enable the module
params
string pProductId: in
string pDescription: in
endparams
try
if (pProductId != $syntax("####"))
throw -10001, "Product Id not valid", "ParamNr=1;ProductId=%%(pProductId)"
endif
clear/e "PRODUCT"
PROD_ID.PRODUCT/init = pProductId
retrieve/e "PRODUCT"
DESCRIPTION.PRODUCT = pDescription
try
store/e "PRODUCT"
commit
catch
; Catch all and rollback before rethrow
rollback
rethrow
endtry
finally
; Always clean up when done
clear/e "PRODUCT"
endtry
end
Existing applications
This is all nice and well for new applications, but what about existing applications, can I just exception-enable all my code?
That is a good question and applicable to many applications. The answer is NO! The code of existing applications is not written in the exception-style (using try-catch) and simply adding throws to a module will most likely break it. Chances are big that code runs into a runtime error (consciously or not) making the module stop executing and exit with an exception, even if the next line would be error handling. So, making a module exception enabled probably requires some refactoring.
To improve the quality of a specific module, you can exception-enable it. You could start by adding throws to the top-level module in your call stack and work your way down until you reach the module you intend to improve, adding throws and any relevant try-catch blocks to catch expected exceptions. However, as of Uniface patch 10.4.02.006, Uniface has been enhanced to enable any module to bubble an exception up the call stack, regardless of whether it has a throws statement or not. This means you can start improving any module in your code to increase the quality with the introduction of exceptions. Any uncaught exceptions terminate the application, indicating an error that needs to be fixed. Either fix the issue, catch the exceptions in the module itself, or catch them in a module higher up in the call stack.
Conclusion
The purpose of exceptions is to write robust and maintainable code. If your code is already robust and of high quality, there is no reason to change or do anything; refactoring code always comes with a risk, and code with proven quality should not be touched. However, often there are weak spots in the code, probably the oldest modules where bugs just keep on popping up. That is a good opportunity to define a project and start improving the code by exception-enabling it.
With Uniface patch 10.4.02.006, modules can be exception-enabled one by one, without touching anything else, which limits the impact of your quality-improvement project. Make your code robust by fixing errors that were always there but never noticed and catching expected errors. Improve your code even further by adding custom checks, and throw your own exceptions that then tie into the Uniface exception mechanism.
Learn more about implementing exception handling with our elearning course on How to use Exceptions in ProcScript.
#uniface-runtime#uniface-version-10#uniface-development#uniface#tofp