I have a new puzzle for you :-).
I was thinking on how should an application handle his own start up. Like : checking for required libraries, correct versions, database connectivity, database compatibility, etc. To be specific, here is the test case. I use SWT and Log4J, for obvious reasons. Now, the questions :
- Should the app check itself for the required dependencies? If yes, should the user be given specific details of what it’s missing? Or just a message, and details to the logs?
- What if the log4J library is unavailable?
- What is the best to do the test? Verifying the file existance (using file.exists(), at specified path), or loading a class, say Class.forName(“org.apache.log4j.Logger”)? What should be the proper order to do the checks? For instance, if i test for SWT, i have no idea if logger is available or not, and the error will occur when i try to access that. Backwards, if i test for the logger 1st : a) The lib could be unavailable – i cannot log the error; b) SWT could be unavailable – unable to display the user message.
- I’ve discovered apache.commons.lang framework today, and i find very useful the method
org.apache.commons.lang.SystemUtils.isJavaVersionAtLeast(Float value)
, and manny others, i am sure. However, importing too much libs to your project dont make it hard to maintain? Versions change, compatibilities are lost, eg. one cannot control a 3rd party developement style or direction.
Thank u for your answers.
I agree with your need. Checking for required runtime environment provides:
But please note that the runtime requirements could be checked in two situations:
This suggests creating an installer for your application.
Potentially, errors would not all be blocking for the installation. Some would rather accumulate as a list of tasks to be done after installation, maybe nicely formatted in a file with all reference information.
Here, we once again hit the notion of error level in validation (similar to what happens for Log4j) : some validation errors are at fatal level, others are errors, possibly also warnings …
In our projects, we have some sort of initialization and validation going on on startup. Based on our day-to-day experience, I would suggest the following:
When the application gets big, you don’t want to have all init centralized in one class, so we have a modular structure.
The interface (of course, abstract class is possible) that the modules implement typically have several initialization methods. They could be:
The kernel instanciates each of the module in turn. Then he calls one init method on all of them, then another init method and so on as needed. Each init method can:
The kernel takes care of module dependencies generically:
A nice feature of this kernel approach is that it is easy to aggregate the errors, at various levels (although fatal could stop it), and report all of them at the end, using whatever means is available (SWT or not, Log4J or not …). So instead of discovering the problems one after the other, and having to start again each time, you could deliver in one blow (nicely prioritized of course).
Concerning your precise questions:
Yes (see higher)
As said higher, when installing the user is more prepared to deal with this.
When starting, we use an easy message for the end-user, but give access to the full stack traces for the developper (we have a button that copies in the clipboard the application environment, the stack traces and so on).
Log without it (see higher).
I would load a class. But if it failed, I might check the file existence on disk to give a improved message, including “how to fix”.
As I said higher, I suggest these low-level errors get accumulated in a small area of code (kernel), where you could use anything that is available to display them. If nothing is available, you could simply log in the console without Log4J.