The jicarilla framework provides a compact COP/IoC/SoC/event-based API. It
is so compact, in fact, that COP nor IoC nor SoC require any kind of interface
representation!
Typical usage is basically defining a few services and hooking them up using
a few sinks, sources, and/or pipelines to each other and the world, then
throwing everything into a container which will hold your components for
you.
Besides provision for these patterns, the jicarilla framework also provides
some common low-level utilities and interfaces, like support for Assertions,
cascading exceptions, and more.
Terminology
Please refer to
http://wiki.codehaus.org/picocontainer/PicoTerminology
for definitions of basic terms used.
Event pipeline plumbing basics
A lot of information and tips about the use of the jicarilla framework
interfaces is kept with the interfaces themselves. Here's a brief overview of
the basis:
Event, message
The object which travels through the event-based system. With the
jicarilla framework, it really can be any object. In an application, this will
usually be a more specific type of event, though that's not a
requirement.
pipeline, plumbing
We draw an analogy between event-based programming and the work of a
plumber. Just like the plumber connects pipes, sinks, drains, valves, sources
and the like to control the flow of water through a plumbing system, we connect
their virtual counterparts to control the flow of messages through our event
system.
{@link org.jicarilla.lang.plumbing.Source source}
Continuing our analogy, a source in the physical world could be a spring, a
well, a tap, a reservoir, etc. Virtual equivalents can be sockets, event
listeners, state monitors, timers, and others. Sources are the starting point
of our plumbing.
{@link org.jicarilla.lang.plumbing.Sink sink}
Likewise, the drains, sinks and holes in the physical world have
counterparts in our event model. Possible sinks include sockets, event
handlers, resource recyclers, and others.
{@link org.jicarilla.lang.plumbing.Stage stages, channels}
Anything that is a source on one end and a sink on the other end is a
stage. In the real world, stages can be thought of as the pipes interconnecting
sources and sinks. In our model, everything that supports both the source and
the sink interface can be effectively used as a stage.
The implementation jar for jicarilla framework (or the combined
interface+implementation jar if you prefer it) contains basic implementations
of all of these interfaces, as well as several abstract base classes that make
creating custom interfaces easier.
These event mechanisms are loosely based on the lightweight executable
framework(s) described in Doug Lea's excellent book,
Concurrent
Programming in Java. Doug Lea's
util.concurrent
package is used in their implementation.
Utility classes
Many classes and interfaces don't really "fit" in a particular package or
framework, yet they are so commonly needed as to warrant inclusion in one of
the jars / core dependencies for any project. The jicarilla framework includes
a few of these. The criterion for inclusion of a class of interface is simple:
if its vital for use inside the jicarila framework itself or inside the
jicarilla container, inclusion is considered based on an evaluation of the
general applicability of the class or interface.
Current utility classes and interfaces include:
{@link org.jicarilla.lang.CascadingError},
{@link org.jicarilla.lang.CascadingException},
{@link org.jicarilla.lang.CascadingThrowable}
Prior to JDK 1.3, stack traces were inconsistently kept for various
exceptions. In order to create full stack traces, it was neccessary to
reference the offending exception whenever creating a new one. These classes
provided support for rich stack traces in a pre-jdk-1.3 environment by allowing
you to specify a the exception that "caused" the new exception, creating a
cascade. They are backwards compatible from jdk 1.0 and forwards compatible up
to at least jdk-1.4, but likely all the way up to all feature versions of
java.
All exceptions within jicarilla inherit from one of these base classes, and
there's nothing from stopping you doing the same in all your applications.
Alternatively, you may want to look at one of the several equivalents of these
classes that exist in:
- Avalon-Framework
- commons-lang
{@link org.jicarilla.lang.Active}
Despite all efforts to create COP frameworks without any sort of lifecycle
beyond a simple constructor, and every now and then a finalize() ,
we still need initialize() and dispose() methods if
we work in a multi-threaded environment (it's usually illegal to create threads
in a constructor). The Active interface merges the two.
Note that functional equivalents exist in:
- Avalon-Framework
- PicoContainer
- JContainer DNA
{@link org.jicarilla.lang.Assert}
Starting with JDK 1.4, java has assertions through the assert
keyword. However, that's a backwards-incompatible keyword, hence it is not
available in a lot of environments. Fortunaly, the functionality offered by
assert is easily emulated. The JUnit
testing framework has included an Assert class for ages, and its
proven to be tremendously useful. The jicarilla framework has one, too, and it
is used extensively throughout the jicarilla platform.
{@link org.jicarilla.lang.ExceptionListener}
Most java applications I know have extensive amounts of logging
statements throughout their code, usually done by using a library
like
- log4j
- commons-logging
- avalon-framework logging
That's a shame, because not only are the tons of logging statements quite
ugly, a lot of information is inevitably lost when an exception (which is what
is usually logged) is converted to a string. It just limits the
possibilities.
With the trivially simple ExceptionListener interface and the
supporting implementation code, the decision whether to log anything or not, at
what "log level", or any other actions to take is deferred to somewhere else.
This leads to cleaner, more flexible, and simpler code. (Which is a rare
combination!)
{@link org.jicarilla.lang.ReadonlyEnabled ReadonlyEnabled}
If possible We'd always have conceptually immutable components actually be
immutable from the moment they return from their constructor. That's not always
feasible. When its not, the ReadonlyEnabled interface can be used
as a common way to mark components immutable at a later point in their
lifetime.
{@link org.jicarilla.lang.Recyclable Recyclable}
Despite all the improvements in JVM technology, the new keyword is
often still one of the main resource costs in java programming. It is often
much cheaper to set a reference to null than it is to destroy an existing
instance, then create a new one. If this is the case, and the performance
matters, we can recycle our objects. The Recyclable interface
specifies a simple recycle() method that tells an object to do
just that to itself. Use it wherever instantiation/destruction becomes an
unnacceptable bottleneck in your system.
Combining utility interfaces
What happens if you combine the Active ,
AsyncEnabled and Recyclable interfaces? Naturally,
they can work together perfectly fine. Let's look at two common combinations
that deserve some particular attention.
Active with Recyclable
The Active interface defines part of the component lifecycle,
together with rather formal and exacting definitions of component state. The
Recyclable interface does no such thing: it assumes that its implementation is
a simple well-behaved component that is always in a consistent state. We should
give some thought to what happens when we mix the two. At your discretion, you
can modify the contract for Active to allow
{@link org.jicarilla.lang.Recyclable#recycle()} to be called
after dispose() . This would effectively allow for some
"restartable" components. Something like the follow would be valid:
// FRAGMENT
ActiveAndRecyclableComponent aar = new ActiveAndRecyclableComponent();
aar.initialize();
aar.doStuff();
aar.doMoreStuff();
aar.dispose();
aar.recycle();
aar.doStuff();
aar.doStuff();
aar.doStuff();
aar.doStuff();
aar.doStuff();
aar.dispose();
aar.initialize();
aar.dispose();
aar.recycle();
aar.initialize();
aar.dispose();
This is a very dumb example, of course. In some very specific cases, this
can be useful functionality, and the option is there (in fact, its sort-of
implemented by
{@link org.jicarilla.lang.AbstractActive#lazyInitialization()}.
But I recommend you try alternative solutions first, since this is the first
step to an overly complex and confusing component lifecycle.
Active with AsyncEnabled
This is a common combination, and a good one. Asynchronous components often
use a worked threads (or multiple workers, or a worker thread pool, or a
lightweight executable framework) to implement their asynchronous messaging.
It is a good idea to create these active materials as part of an
initialize() /dispose() sequence (remember: don't
create threads inside constructors!).
Some notes about thread safety
All the event-related interfaces and classes in jicarilla framework are safe
for concurrent usage. Why? Because event-based programming usually only makes
sense in a multi-threaded environment, hence we almost always need thread
safety. The interfaces and classes that are not directly event-related are not
synchronized, but for each of them, its highly unlikely you will ever run into
problems because of that.
Some notes about duplication
Several classes for which functional equivalents exist in other locations
have been duplicated inside the jicarilla framework. Why? Because it can be
highly annoying to introduce an external dependency on a jar not under your
control just because you want to use 2% of its functionality. As far as I know,
full credit is given to the original authors/projects in any and all files, as
well as to projects/authors that were a source of insipiration, and all
licensing requirements are satisfied. I hope everyone can see the rationale
behind the duplication and not be offended by it.
|