The Road to Joppa - FAQ
Table of Contents
·
What is Joppa?
·
What is the “Adaptation
Pattern”?
·
What is the application
design goal of “Substitutability”?
·
What are the high-level
Joppa “platform abstraction” characteristics?
·
Which technologies were initially
abstracted as a “Proof of Concept”?
·
When a Joppa plugin abstracts
a technology, is all functionality supported?
·
Hey, what’s the BO, LO, MO, RO
deal?
·
What is “Joppa CRUD”?
·
What Common layer patterned objects does Joppa promote?
·
What Presentation layer
patterned objects does Joppa promote?
·
What Service layer patterned
objects does Joppa promote?
·
What Integration layer
patterned objects does Joppa promote?
·
What Test patterned objects does Joppa promote?
·
Can an application use Joppa “al á carte”?
·
What about exception
handling?
·
What development tools are
used?
·
How are JARs organized?
·
How are the Joppa projects
organized?
·
What is the genesis of the
Joppa protocol?
Joppa is the open source "J2EE Objects: Pluggable
Platform Abstraction" enabling OO transactional application development
and automated method testing by abstracting existing best-in-class technologies.
Joppa delivers a consistent plugin module interface
and base
class extension (inheritance) style of API. Joppa pursues the illusive write-once and
run-with-a-choice-of-technologies goal.
As a platform abstraction - Joppa seeks to sustain loose coupling
(between underlying frameworks or technologies).
Joppa serves as a reference implementation for the “Adaptation Pattern”.
What is the
“Adaptation Pattern”?
Problem
You want to implement, sustain and
extend application functionality in a cost-effective manner while underlying
technologies are deprecated or mature-beyond-recognition over time.
Effective technologies are frequently transformed to
remain relevant; others have a formal termination date or informally fade from
usage and memory. The “Adaptation
Pattern” represents an alternative to hard-wiring an application by decoupling
technologies from their API. The
fundamental notion of authoring general-usage applications to independently
conceived APIs leads to several representative challenges:
·
Velocity
- A temptation to slow progress in order to make “good” technology/API choices
having long-term consequences.
·
Staffing
- The need to obtain/train/retrain resources fluent in selected technology
APIs.
·
Cost
- The need to “glue” disparate technology/API choices together into a
deliverable system.
·
Agility
– An inherent “brittle” relationship between application and dependent
technology.
·
Modularity
– A lack of “architectural cohesion” when business and technology functions
(inadvertently) merge.
Forces
·
You
want to author general purpose applications to a consistent technology
independent API.
·
You
want to establish an Object Oriented stratification for
organizing technology functions.
Solution
Use the “Adaptation Pattern” to segregate
general-purpose applications from their dependent technology functions.
The strategy encourages a minimum of three (3) levels of
contextual abstraction between a substitutable technology and application
code. Each abstraction level is
independently testable. Abstraction
level artifacts consist primarily of plugin module implementations and/or
extensions, base class implementations and/or extensions, and automated
method (unit) tests.

Consequences (side-effects)
·
Reduces
the unique lines of business function code required (to deliver a use case).
·
Reduces
the number of test objects and unique lines of test code required.
·
Enables
consistency of style and convention within the abstraction level.
o
No
need to learn/reference multiple APIs.
o
o
Multiple
entry points for (continuous) maturation of convenience methods.
·
Enables
establishment (and verification) of level performance criteria.
·
Encourages
design cohesion – particularly separation of technology and business functions.
·
Increases
the need for a shared understanding of design approach within a level
community.
Related Patterns
Adapter Pattern (or wrapper pattern): The adaptation pattern is to an application
what the adapter pattern is to a single object or class. In addition to this conceptual relationship,
the adapter pattern is a useful technique for translating methods from a
technology specific API to a multi-technology consistent API.
What is the
application design goal of “Substitutability”?
“Substitutability” as a design goal may be defined as:
The extent a technology change causes an expenditure of
effort.
Some technologies are more prone to a substitution then
others; “substitutability” is typically realized thru loose-coupling between an
application and its dependent technologies.
Joppa (fundamentally) concedes tight-coupling to the JDK – although not
necessarily all technologies contained therein.
In addition, Joppa promotes a balanced view of “substitutability”; a
preferred target might be 80% “substitutability” achieved with a 20%
expenditure of effort contrasted with 100% “substitutability” achieved with a
100% expenditure of effort. (See “Pareto
Principle” or “80-20 Rule” at http://en.wikipedia.org/wiki/Pareto_principle
.)
Inherit in the pursuit is the pretext that technology
substitutability is a characteristic of effective modular design. In somewhat the same manner that the pursuit
of “testability” can improve design as a side-effect; the intentional pursuit
of “substitutability” can lead one to additional rigor in design pattern usage.
In any case, effect “substitutability” contributes towards
reducing the unique lines of code required during the complete (multi-year)
life-cycle of the application.
What are the
high-level Joppa platform abstraction characteristics?
Joppa, as a platform abstraction, normally seeks to assist
teams deploying proven technology.
Since design is a compromise, Joppa typically compromises in favor of Object
Orientation (and testability). Joppa
stands opposed to design (and deployment) patterns explicitly or implicitly
encouraging separating attributes (variables) and their associated behavior
(methods).
At no time, in any way, should Joppa hinder application
development not using a Joppa-related plugin module or base
class. This is a significant
distinction between a framework and the Joppa notion of a “platform
abstraction”. Many frameworks attempt to
provide 100% functionality in a problem space.
Joppa seeks to support typical web application capabilities –
primarily those associated with request/response processing and reading/writing
(transparent persistence) with an object store (a.k.a. database). It has been said that Joppa (potentially) enables
80%
of transactional web applications for 80% of their functionality.
Another Joppa distinctive is the enthusiastic use of
plugins for abstracted programming logic.
Many base class methods are a façade for application invoked and call
back methods; they simply invoke the complementary plugin method. Typically plugins have defined Interfaces;
base classes do not (excepting when Java single-class inheritance
forces use of a “marker”). The strategy
enables base class maturation (without breaking existing applications) and use
of “protected” methods. The plugin strategy may be more readily apparent
in the following class diagrams for two substitutable unit testing technologies
– JUnit and TestNG.
JUnit Plugin

TestNG Plugin

The following abridged listing demonstrates invocation of
plugin logic from base class (JjBaseOutsideContainerJunit/JjBaseOutsideContainerTestng)
methods:
/**
* This method unloads the originally specified descriptor document and
* loads an alternate. It is typically used in the context of testing
* declarative plugin values. The corresponding "unload" method is invoked
* after completion of test logic.
*
* @param documentName
* Descriptor document name.
*/
protected void loadAlternateDescriptorDocumentBeforeTesting(String
documentName) {
JjUtilPluginFactory.returnTestOutsideContainerPlugin()
.loadAlternateDescriptorDocumentBeforeTest(documentName);
}
/**
* This method unloads the alternate and reloads the originally specified
* descriptor document.
*/
protected void unloadAlternateDescriptorDocumentAfterTesting()
{
JjUtilPluginFactory.returnTestOutsideContainerPlugin()
.unloadAlternateDescriptorDocumentAfterTest();
}
Somewhat uncharacteristically in the Java world, Joppa
artifacts are intentionally named to leverage code completion. For instance, all Joppa classes begin with “Jj” in these representative examples:
·
JjBase …
·
JjException …
·
JjFactory …
·
JjInterface …
·
JjTo …
·
JjUtil …
The Joppa Reference Implementations are integral for
validating and demonstrating the application development protocol. Not unlike Equinox, AppFuse and Honeycomb – Joppa presumes
application projects begin with a working implementation and then launch into a
test driven refactoring style of development.
Which technologies were initially abstracted
as a “Proof of Concept”?
The “real” Proof of Concept, in production at a Fortune
Five company, is not available to the open source community. That said, the pre-work completed to
communicate the strategic intent and tactical approach includes the following
technology substitutions.
·
Logging
o
J2SE
logging (packaged in joppa-core.jar).
o
Log4j
logging (packed in joppa-log4j.jar).
·
Outside
Container Testing
o
JUnit
(packaged in joppa-core.jar).
o
TestNG
(packed in joppa-testng.jar) for JDK v. 5.0
When a Joppa plugin
abstracts a technology, is all functionality supported?
It would normally be a violation of the intent of Joppa (and
the “Pareto Principle”) to support 100% of a technology’s capability. Using logging as an example; J2SE logging has
seven (7) predefined levels and Log4j has six (6). Joppa asserts (in principle) that 80% of the time
five (5) levels are enough. The mapping
between J2SE/Log4j with Joppa is represented in the following table:
J2SE / LOG4J VALUE
JOPPA METHOD
====
===== ===== ============
SEVERE/FATAL = 1000 = logThrowable()
WARNING/ERROR = 900 = Not explicitly supported
INFO/WARN = 800 = Not explicitly supported
CONFIG
= 700 = Not explicitly supported
[custom] = 600 = logLayer()
[refined by architectural layer]
FINE/INFO = 500 = logInformation()
FINER/DEBUG = 400 = logEntering()/logExiting()
FINEST/TRACE =
300 = logDebug()
However, an additional nuance is introduced beyond hierarchical
log levels. Level 600 can be further
designated by “Common”, “Presentation”, “Service” and “Integration”
architectural layer; so that one may log Integration layer transparent
persistence information without logging the same detail for the Presentation
layer. (See JjInterfaceLoggingHandlerPlugin Javadoc for details.)
Joppa asserts that 80% of applications can benefit from layer-sensitive
logging in addition to standard hierarchical logging. The good news is that if one disagrees with a
Joppa plugin implementation, there are several options:
·
Override
an existing plugin
·
Extend
an existing plugin
·
Author
a new (competing) plugin
And of course, there is no project velocity penalty to
simply ignore a particular Joppa capability and hard-wire a portion of an
application to a native API.
Hey, what’s the BO,
LO, MO and RO deal?
Business Object (BO): The J2EE Core Patterns 2nd
Edition identifies a Business Object as (minimally) encapsulating business data
and behavior. The BO instances collectively
represent the domain model or object graph.
A BO is typically coded with an immutable primary key and may also
contain a no-argument constructor.
Note: Care should be exercised
before adding unmapped attributes to a BO.
Attribute state may be lost when (shared) cached instances are
refreshed.
A BO is typically configured to be refreshed in shared
cache based on a change to the last updated date – the same attribute used for
optimistic locking for update operations.
When a BO is used in a read-only context it is referenced as a RO, when
a BO is being used in a modifiable context it is notated as a MO.
Lookup Object (LO): A LO is a specialized
BO. So, why have the distinction?
A LO is typically configured to not be refreshed in
shared cache. When a LO is used in a
read-only context it is referenced as an RO.
A LO would never be in a modifiable state and may be declaratively
configured as read only. The only
transactional operation performed on a LO is similar to:
registerCountryLoAsRelated()
A list of countries provides an excellent example of LO
usage. Historically, country names have
been reasonably static. An application
may have country names preloaded into a database table for subsequent use with
a drop-down list box on a web page. There
is no reason to continuously determine if a country name should be refreshed in
shared cache – the presumption is that once loaded, the country name is static
and will not change.
When global upheaval is endured and a country name change
required, the table must be updated outside of the application and the shared
cache refreshed – perhaps by restarting the application server.
Modifiable Object (MO): Unlike a BO or LO, the MO designation
is a naming convention to clarifying the instance is a private copy – not a
reference (to shared cache). It
indicates a BO has (already) been registered for either create or update. An example declaration might look as follows:
private PersonBO
personMO;
Read-only Object (RO): A RO is a wrapper object
protecting a BO instance in shared cache from a state change. Typically, only (accessor) “getters” are
exposed on an RO.
Wait – how about Transfer
Objects (TOs)?
Joppa redefines the Core J2EE Patterns problem (2nd ed. p.
415) as – “You want to transfer multiple
data elements and be able to refactor the elements without breaking method
signatures.” Simply stated, many
Joppa method signatures require (immutable) TOs as a
deliberate platform abstraction sustaining strategy. TOs are often the
messenger from an application to a Joppa method.
The “Joppa CRUD” constitutes the template method naming
conventions providing object graph management.
Eight (8) conventions support read-only operations, four (4) are
transactional and two (2) are used on an exception basis. The read-only conventions consist of two sets
beginning with the verbs “exists” and “read”.
The transactional conventions begin with the verb “register” and use the
overloaded acronym CRUD (for Create,
Relate, Update and Delete). The “Joppa CRUD” may be referenced
generically by their JjBaseDAO method names:
·
existsRoBy()
o
executes
a parameterized named query
o
determines
if a persisted (RO) instance exists
o
example
= existsCountryRoByCode()
·
existsRoWith()
o
executes
a hard-coded named query
o
determines
if a persisted (RO) instance exists
o
example
= existsCountryRoWithUS()
·
existsRoListBy()
o
executes
a parameterized named query
o
determines
if any persisted (RO) instances exist
o
example
= existsCountryRoListByContinent()
·
existsRoListWith()
o
executes
a hard-coded named query
o
determines
if any persisted (RO) instances exist
o
example
= existsCountryRoListWithNA()
·
readRoBy()
o
executes
a parameterized named query
o
retrieves
a reference to persisted (RO) instance
o
example
= readCountryRoByCode()
·
readRoWith()
o
executes
a hard-coded named query
o
retrieves
a reference to persisted (RO) instance
o
example
= readCountryRoWithUS()
·
readRoListBy()
o
executes
a parameterized named query
o
retrieves
a list of persisted (RO) instance references
o
example
= readCountryRoListByContinent()
·
readRoListWith()
o
executes
a hard-coded named query
o
retrieves
a list of persisted (RO) instance references
o
example
= readCountryRoListWithNA()
·
registerBoForCreate()
o
typically
utilizes an instantiated BO with immutable primary key
o
returns
a modifiable instance (MO) bound to a transaction
o
example
= registerPersonBoForCreate()
·
registerBoAsRelated()
o
typically
utilizes an instantiated BO with immutable primary key
o
returns
a modifiable instance (MO) bound to a transaction
o
example
= registerCountryLoAsRelated()
·
registerBoForUpdate()
o
typically
utilizes an instantiated BO with immutable primary key
o
returns
a modifiable instance (MO) bound to a transaction
<