SafeR - examples
SafeR is a lightweight framework to apply a Design by Contract (DbC) coding style. Additionally, SafeR provides operations for checks which return a boolean value beside other related operations. SafeR supports DbC in two ways: with safe reference objects and assertions with just static operations. While the latter is the same approach as seen typically in "Assert" classes with static operations, the former is the preferred approach wherever possible. Safe reference objects can be seen as (immutable) containers or wrappers which assert that the references they contain are in the specified / expected state. By using the type of a safe reference class as parameter type or return type of an operation, the caller of the operation is forced to comply with the contract at compile time - and the declaration of the contract (preconditions, postconditions and invariants) in the Javadoc can be omitted. While the assertions with just static operations can detect programmatic issues very early and very close to the location of the issue, with safe references the most of the relevant programmatic issues can be prevented by the compiler already and the rest of the programmatic issues should be detected directly at the location of the issue.
In case a programmatic issue is detected by SafeR, a runtime exception or error will be generated with a large exception message containing all the details which might be useful to fix the issue, including a filtered stacktrace. The exception message can be customized easily, translated to any language and split into parts in order to extract information for the user. Any given object or value will be encoded to a detailed string automatically or customized for the exception message. Please see also the sample log. Optionally, an analyzer tool like the SWDES Code Analyzer can be used to generate an analysis report or an advice about the root cause into the exception message. The SWDES Code Analyzer will try to perform a backtracking using static bytecode analysis to find the origin of the issue. This can make the manual backtracking of an issue obsolete, in applications where the assertion is not as close to the location of the issue (due to a less intensive use of assertions).
SafeR can be and shall be active in production environments as well, to detect programmatic issues which have not been detected previously and to prevent invalid states. In order to prevent any issue for the business or the users, due to thrown runtime exceptions, SafeR provides convenient possibilities to unblock an application quickly and just within the production environment, but just temporarily until the programmatic issue has been fixed. This can save your day or even your job! SafeR prevents that assertions can be switched off permanently and forgotten easily, as it can happen for instance with the Java assertions provided by the JDK.
SafeR is designed to reduce its impact on the performance of an application as much as possible, so that no impact should be noticeable even if a very large application uses SafeR very intensively.
Hint: even if SafeR is extremely easy to use already, for the everyday work it might be useful to create editor templates within your IDE for the code and/or Javadocs to quickly create an assertion with just two or three key strokes.
In SafeR each contract (in terms of DbC) is represented by one safe reference class. For instance the contract that a given object must not be null is represented by the class NotNull, the contract that a given text must not be null or empty is represented by the class NotEmptyText, the contract that a given collection must not contain any null values is represented by the class NoNulls, and so on. Most of these safe reference classes provide plenty of operations related to their contract, to create a safe reference from the given object, to assert the object meets the contract, to check whether or not the object meets the contract, and so on. This design makes it easy to extend SafeR with a new contract by just adding another safe reference class.
Let's look at some usage examples. For instance to ensure that an object is not null we can write...
NotNull<Foobar> foobar = NotNull.of(foobarInput);
...to create a safe reference. Here the foobar object is a safe reference that contains the foobar input. If the foobar input is null, then SafeR will throw a runtime exception before the safe rerefence is created actually. Now, we can pass foobar to any subsequent operation (with its type NotNull<Foobar> as parameter type) and that subsequent operation can trust safely that the foobar input inside foobar is not null. Hence, all subsequent operations which use the foobar input do not need to apply another null check. The other way around: a safe reference type as parameter type in a method signature will effectively force all callers of that method to meet the contract already at compile time!
Or instead of a safe reference we just use a static operation to ensure that an object is not null like so:
SafeR will throw a runtime exception if the foobar input is null, but the assertFor operation does not return a safe reference. Hence, we can just pass the original foobar input to any subsequent operation and all subsequent operations which use the foobar input will have to apply another null check. The the assertFor operations can be used as assertions of preconditions and postconditions, as well as assertions not related to DbC anywhere in the code.