Introduction
Krypton was inspired partially by the way assembly lines in
manufacturing work to decompose a production process into the
smallest possible parts, and then assemble the product from those
parts. This article discusses what aspects of Krypton’s paradigm
have been drawn from assembly line production. You don't need to
read this article to know how to use Krypton, but it might give you
a better understanding of the ideas that went into the tool.
This article will give you a flavour of how Krypton works. If you’d
like to find out more about Krypton, you could read the
How Krypton Works or
Trying Krypton
articles.
Assembly Lines
Assembly lines are based on the age-old principal of Divide et
Imperia (divide and conquer). The production of a complex product,
such as a car, is decomposed down to the level of individual parts,
such as bolts and hubcaps. Each of these parts needs to be
produced, or perhaps acquired from an external source, but the
process for producing a part such as a hubcap is much simpler than
the process for producing a car. The production processes for these
individual parts are relatively easy to define. As parts are
produced or acquired externally, they can begin to be assembled up
into components. Assembled components may in turn be combined to
create other components. Eventually when we have put together
enough parts and components, we have the finished product.
Dividing the production process into small sub-processes focused on
a specific part also makes it easier to automate them. It’s much
easier to create a robot that creates a table-leg than it is to
create a robot that creates a whole table in one go. Automation
results in faster production and a more consistent level of quality.
How Krypton Uses the Assembly Line Paradigm
Krypton applies the assembly line model we’ve just discussed to the
software build process. Krypton decomposes a build into the
production of individual parts, which are combined together in
ever-more complex arrangements until eventually the final product is
assembled.
Just like an assembly line building a car, a Krypton build process
takes in raw materials (source files), externally sourced artifacts,
and produces parts. Parts (we call them artifacts in Krypton) can
be combined to produce new more complex parts.
Krypton differs from tools including Ant and Maven in that it
focuses on the parts being produced and assembled while those other
tools focus on the process for producing the parts. You can see
this focus in the names given to Maven goals (for example "compile")
or the targets found in a typical Ant script (for example: "init",
"compile", "copy-resources"). In Krypton you will find build
targets (referred to as artifact's in Krypton) such as "corejar",
"website", "classtree". Note that it is possible to construct Ant
scripts that place a similar focus on artifacts. We discuss such an
approach in our article "Artifact-Oriented Ant".
A Brief Overview of Krypton
A Krypton build process is defined simply by declaring the artifacts
that are to come out of the build. This includes the final
product(s), and also the intermediate artifacts. Each artifact
declaration includes the name of the producer that will be
responsible for producing that artifact. Producers are the
industrial robots in the Krypton assembly line. Each producer has
certain inputs through which materials required for production can
be supplied. For example, the classtree producer has inputs for
java source code, JARs to be used during compilation and additional
pre-compile classes or files to import into the classtree.
These inputs are called dependencies in Krypton, because they can
define a relationship between two artifacts, and because in Krypton,
sources files are also treated as dependencies. In other words, to
use some body of source code in your build, you declare it as a
dependency of an artifact. Dependencies are typed so that when you
declare that something is a dependency of something else, you must
also specify how it a dependency.
The following diagram shows an example of a Krypton build
declaration. The trapezoidal shapes represent different types of
source code, the rectangular shapes show artifacts, and the arrows
show dependencies. Where a dependency enters an artifact, the blue
label shows the type of the dependency.
Starting with the j2ee_jar and log4j_jar artifacts: these are both
"built" by the SimpleMaven1RepoArtifact artifact producer. These
particular artifacts don’t have any dependencies, but in their
declarations settings are provided that enable the producer to work
out which JAR we want.
The Classtree artifact has three dependency types:
- java - Java source code
- importclasses - pre-built classes or other files to be included
in the classtree
- compilejars – The JAR files that the Java source code should be
compiled against.
For the java and importclasses dependencies, we are just linking in
some source code. For the compilejars dependency, we link in both
the j2ee_jar and log4j_jar artifacts. A artifact dependency can
include many sources, artifacts or a mixture of both.
The corejar artifact is built by the Jar artifact producer. This
producer has a single dependency which is the classtree that we want
to package up as a JAR. The Jar artifact producer has settings that
allow us to specify the filename of the JAR that is built.
The final artifact, coreapp, is produced by the App artifact
producer. That producer has dependencies for the elements that are
commonly found in a Java application:
- etc – Normal files that just get placed into the root directory
of the assembled application
- classes – Class files, or other files you want to have in the
classes directory such as log4j.properties
- lib – JAR files that are used by the application at
runtime.
We are providing sources as dependencies for the etc and classes
dependencies. We are also linking the log4j_jar in as a lib
dependency of our application.
The XML describing this Krypton build declaration looks like this:
<krypton name="Example Projekt">
<artifacts>
<artifact name="j2ee_jar"
type="SimpleMaven1RepoArtifact">
<setting name="artifactId" value="j2ee" />
<setting name="version" value="1.4" />
<setting name="type" value="jar" />
</artifact>
<artifact name="log4j_jar"
type="SimpleMaven1RepoArtifact">
<setting name="artifactId" value="log4j" />
<setting name="version" value="1.2.8" />
<setting name="type" value="jar" />
</artifact>
<artifact name="coreclasstree"
type="Classtree">
<dependency name="java">
<item type="source" name="java" />
</dependency>
<dependency name="compilejars">
<item type="artifact" name="j2ee_jar" />
<item type="artifact" name="log4j_jar" />
</dependency>
<dependency name="importclasses">
<item type="source" name="resources" />
</dependency>
</artifact>
<artifact name="corejar" type="Jar">
<setting name="jarFilename" value="myapp.jar" />
<dependency name="classtree">
<item type="artifact" name="coreclasstree" />
</dependency>
</artifact>
<artifact name="coreapp" type="App">
<dependency name="lib">
<item type="artifact" name="log4j_jar" />
<item type="artifact" name="corejar" />
</dependency>
<dependency name="classes">
<item type="artifact" name="config" />
</dependency>
<dependency name="etc">
<item type="source" name="etc" />
</dependency>
</artifact>
</artifacts>
</krypton>
The concepts of Artifacts and Dependencies are the core of the
Krypton model. The ancillary elements of Krypton’s model are:
- Meta Data - A mechanism for retrieving and deriving data about
the artifacts being produced, such as product names or version
numbers that might be applied to artifacts in the form of
filenames or injected into files such as manifests.
- Operations - Used to perform actions against produced artifacts,
such as "execute", "upload", or "publishtomaven". Operations
can also be performed against the source tree.
You can learn more about these other features in the
How Krypton Works article.
Benefit: Production Isolation
In an assembly line, it would be unacceptable for a configuration
change to the wheel-nut-tightening robot to have an effect on how
the spray-painting robot works. Each stage must be isolated from
the others, with the relationship between production stages being
the artifacts that are fed between them.
Krypton produces each artifact in an isolated environment containing
only the meta information properties, classes and build resources
that are relevant to the production of that artifact. Isolating
artifact production this way means that changing the way an artifact
is produced does not affect other stages in the build.
Contrast this to Ant, where properties are global and immutable,
and once created persist for the life of the build. Or Maven where
the build process is driven by a set of global properties. In these
types of arrangements, changing a global property for one part of
the build process can have an unexpected impact elsewhere in the
build.
Build isolation also means that different stages of the build
process are now able to use very different build environments.
This can be important when supporting legacy code while at the same
time building new features based on newer technologies. For
example, the following diagram shows a single build process where
different artifacts are built using different JDK and Ant versions.
Benefit: Build Process Rewiring
In process-oriented tools like Ant and Maven, changing the
relationships between artifacts means changing the build process
itself, which means lots of work. When you flip the paradigm around
to focus on artifacts, and make the process secondary, changing the
relationships between artifacts requires a simple "re-wiring" of the
dependencies between the artifacts.
Lets look again at our previous example:
In this build declaration, we are importing some hand-maintained
Java source code. Let's say now that we have decided to port our
middle-tier to be built as EJBs, and have decided to use XDoclet to
generate source. If our build was based on Ant, this would require
some heavy duty re-writing of our build script. If we were using
Maven, we'd have to find a plugin, figure out where it fits in the
build lifecycle, and debug it's integration. Using Krypton, all we
need to do is declare a new artifact for the code generated by
XDoclet, and wire that in to the flow with the right dependencies.
Benefit: Producer Re-use
Often when writing Ant build scripts, you find yourself putting the
same bunch of tasks together to do some particular job. Ant doesn't
provide very strong mechansims for re-use, so what tends to happen
is that these chunks of Ant code get copied and pasted across build
scripts.
As we've discussed, Krypton focuses on artifacts rather than
processes – what rather than how – and build processes are extracted
out and encapsulated as Producers. This encourages you to either
re-use an existing producer, or write a new re-usable producer.
Note Krypton still offers the flexibility to write a one-off piece
of Ant code for a projekt via the AntProjekt producer.
Benefit: All Artifacts Are Created Equal
Ivy, Maven and other efforts have developed very valuable resources
for the Java community in the form of repositories of JAR artifacts
and the mechanisms to retrieve those in very clever ways, including
resolving transitive dependencies. Build tools can retrieve
artifacts from these repositories based on meta information
describing the artifacts, and incorporate those into the build.
But these tools give special status to JAR files, and focus on
building repositories of those to the neglect of other types of
files.
Dependencies are key to the way Krypton works, and are not
restricted to JAR files. Under Krypton, all artifacts are created
equal: all can have dependencies, and all can be dependencies for
other artifacts.
This model allows for opens up some interesting possibilities and
opportunities to extend your build automation to include automated
integration of a variety of artifact types. For example:
- log4j.properties - If your dev shop always uses the same
log4j.properties file, that file could be published to a central
location.
Web Skins - Web front ends are often problematic to maintain and
version because they involve so many CSS, image and HTML files,
and because those files are complex anyway. By managing your
web skin as a separate projekt with it’s own version lifecycle,
and importing the published skin into your projekt, you begin to
manage the way skins are applied to your web applications. You
also remove the tempation for the web app developers to make
changes to the skin because they only have access to the
published artifact and not the original sources.
The Krypton standard library's
Site
producer retrieves it's skin from a Maven repositiory.
Conclusion
Krypton borrows ideas from manufacturing to create a software build
paradigm that is focused on artifacts rather than processes. This
leads to more controlled management of each stage of the build,
while at the same providing a model that is more flexible and
adaptable than older tools.
|