There are a lot of libraries out there, but I haven't been
able to find one that solves some of the basic issues that
this one does. The majority of the code in this library has
been tested in real, live production applications in use on a
daily basis. I actively continue to use this code where ever
I can, so that if it breaks, it will affect me as soon
as (and hopefully before) it affects anyone else.
- Command Line Argument Parsing
-
I spend a lot of time writing CLI applications in a variety of
languages. One of the C libraries that I liked best was
libpopt
from RedHat. When I migrated
away from C and C++, I tried to find a version in Java that
did what I wanted. At the time, I found a couple of
libraries (including Java
Popt, Greg
White's DDJ article and JArgs), but none of
them really did what I wanted.
Also, as I wrote more and more of these things, I decided that
the traditional approach to handling the command line
arguments could be improved by applying OO techniques.
Eventually, I applied the Gang of
Four Command pattern and ended up
with something which I'm finally more-or-less happy with. I
think that if you have to write programs to handle
command-line arguments, you will soon appreciate the OO
approach over using more traditional mechanisms, including the
Jakarta
CLI library.
- Configuration Setting Handling
-
Another thing that I have needed with nearly every program
I've written is a way to provide overridable configuration
settings. This happens all the time because there are usually
at least 3 levels of configuration in any sizable application:
- Individual user settings
- System-wide default values normally set during
installation
- Built-in, hard-coded default values
In most programs, you want the settings available to the user
to be the union of items 1-3, but allow them to change or
override any settings contained in 2-3 which are appropriate.
Support for this functionality has been part of the library
from the beginning. As of the initial .NET work and
the 3.0.0-pre1 release, this functionality has been expanded
and generalized allowing more control over the configuration
settings and selective writes to either items 1 or 2.
- Filtering and Sorting
-
The library also includes classes that allow you to create
complex filters which may be applied to any of the standard
Java Collections framework classes. Supplied classes allow
for logical AND, OR and NOT operations to be created based on
either specific filter checks or on dynamic property value
checking (at the moment, the properties are limited to one
level, however by using the Jakarta
Commons Beanutils, this limitation can be easily
addressed).
The sorting aspects allow you to order the values in the
collection based on complex sort specifications. When used
with the filter classes, you can build complex queries and
order the results in a similar manner to the facilities
provided by SQL. Together, these facilities provide an
implementation of the Query Object pattern as defined by Martin Fowler in his
book Patterns of
Enterprise Application Architecture.
- Delimited Text File Processing
-
The library contains a set of classes to abstract the
boilerplate code used to process delimited text files to allow
the developer to concentrate on what to do with the data.
- Persistence Layer Utilities
-
The notable item in this package is the QueryHandler
class. This class provides a facility similar to the DB API
in Python allowing you to more easily execute SQL without
having to set up all of the boilerplate code.
For example, if you wanted to easily perform processing of the
SQL query:
select fname, lname from customer where customer_id = ?
you could do something similar to the following:
public void findCustomerById(long cust_id)
{
QueryHandler query = new QueryHandler(_myConnFactory);
query.addQueryListener(this);
// you would use an external source for the SQL
String sql = SQLProvider.get("find_customer_by_id");
query.execute(sql, new Object[] { new Long(cust_id) });
}
public void nextRow(QueryEvent event)
{
ResultSet rs = event.getResultSet();
String lname = rs.getString("lname");
String fname = rs.getSTring("fname");
System.out.println(lname + ", " + fname);
}
The management of the connection, the preparation of the SQL
and the processing of the result set are all handled by the
instance. Again, the idea here is to reduce the amount of
boiler-plate code you have to write as well as not having to
worry about mapping the query parameters to the specific
statement.
Parts of this package came from earlier work with the Jaxor persistence
framework. The library also has specific support for some
earlier Jaxor releases as extensions, so as to not directly
introduce a dependency if you don't need it (and I doubt it
works with the current version of Jaxor, anyway).
- Internationalization & Localization (I18N & L10N)
Tools
-
As part of the library implementation, an idiom is provided to
enable the efficient localization of any text messages in the
application. This facility goes beyond the standard Java
Properties class capabilities, allowing you to write code as
follows:
public void someMethod()
{
System.out.println(Strings.get("sTitle"));
System.out.println(Strings.format("fSomeMessage"
new Object[] { "arg1", "arg2" }));
}
This technique is based on something that I a saw in the Jakarta Tomcat
source code, but it has been expanded to use the same
inheritance principles used by the configuration system so
that you don't have to continually define the same strings
over and over again for different packages.
- General-purpose Swing Classes
-
The Swing capabilities consist of additional UI classes for a
status bar and an about box along with a way to easily
implement a background task which sends status information to
a monitor object. There is also a highly-extended Metal theme
mechanism which gives much more control over the themes and
allows the application to extend the values of the them in a
consistent manner.
Another aspect of this package is the ability to define the UI
in terms of resources. There is currently only one
implementation based on property files, but the mechanism is
general enough to be extended to XML or something else. The
existing implementation is based off the original Notepad
example shipped with the first releases of Swing.
- Project Version Information
-
One of the things I always try to do is include an awareness
of version information into a project's source. This is just
good practice and allows you to easily identify which version
of a product you are running.
Using the idioms in the project's build.xml
and
the Version.java.in
source file, you can easily
add version tracking information into your projects. For .NET
projects, I have created an NAnt task which
essentially does the same thing, but without having to jump
through quite as many hoops.
- Run-time Application Tracing
-
This facility was one of the first to be added and has been
with me for most of my career in one form or another. The
point of this part of the library is to allow enough
instrumentation to be useful for run-time problem diagnosis.
The main difference between TE-Trace and Jakarta's Log4j is
this library is focused solely on application tracing. To me,
application logging is a different beast altogether, and for
that I use Log4j. However, the key differences of TE-Trace is
the support for the different "maturity levels" of a
particular class. This means that what you get initially with
a trace level of 10 may not be what you will get after the
code is more mature and the developer increments the maturity
level.
Another way to look at it is imagine that you had the
DEBUG
level of Log4j divided up into as many
sub-levels as you needed. The biggest problem with using only
DEBUG
is that after a while, there is so much
information in the output that it becomes unusable. I know
you can filter the output with Log4j, but I don't think that
you should have to do that. The developers know which classes
are more mature, so they should be able to indicate this fact
during implementation. Once this has been done, the
user/administrator sets the output level they wish to see.
All of the "mature" code isn't cluttering up the trace log,
but if you want it, you can still get it by using a higher
trace level.