Author: rhauch
Date: 2009-06-08 10:57:17 -0400 (Mon, 08 Jun 2009)
New Revision: 995
Added:
trunk/docs/reference/src/main/docbook/en-US/content/connectors/
trunk/docs/reference/src/main/docbook/en-US/content/connectors/in_memory.xml
trunk/docs/reference/src/main/docbook/en-US/content/core/
trunk/docs/reference/src/main/docbook/en-US/content/core/classloaders.xml
trunk/docs/reference/src/main/docbook/en-US/content/core/configuration.xml
trunk/docs/reference/src/main/docbook/en-US/content/core/environment.xml
trunk/docs/reference/src/main/docbook/en-US/content/core/mimetypes.xml
trunk/docs/reference/src/main/docbook/en-US/content/core/repositories.xml
trunk/docs/reference/src/main/docbook/en-US/content/core/sequencing.xml
trunk/docs/reference/src/main/docbook/en-US/content/developers/
trunk/docs/reference/src/main/docbook/en-US/content/developers/testing.xml
trunk/docs/reference/src/main/docbook/en-US/content/developers/tools.xml
trunk/docs/reference/src/main/docbook/en-US/content/jcr/
trunk/docs/reference/src/main/docbook/en-US/content/jcr/jcr.xml
trunk/docs/reference/src/main/docbook/en-US/content/jcr/rest_service.xml
trunk/docs/reference/src/main/docbook/en-US/content/sequencers/
trunk/docs/reference/src/main/docbook/en-US/content/sequencers/zip.xml
Removed:
trunk/docs/reference/src/main/docbook/en-US/content/classloaders.xml
trunk/docs/reference/src/main/docbook/en-US/content/configuration.xml
trunk/docs/reference/src/main/docbook/en-US/content/development_tools.xml
trunk/docs/reference/src/main/docbook/en-US/content/environment.xml
trunk/docs/reference/src/main/docbook/en-US/content/jcr.xml
trunk/docs/reference/src/main/docbook/en-US/content/mimetypes.xml
trunk/docs/reference/src/main/docbook/en-US/content/repositories.xml
trunk/docs/reference/src/main/docbook/en-US/content/sequencing.xml
trunk/docs/reference/src/main/docbook/en-US/content/testing.xml
Modified:
trunk/docs/reference/src/main/docbook/en-US/master.xml
Log:
Reorganized the Reference Guide into parts.
Deleted: trunk/docs/reference/src/main/docbook/en-US/content/classloaders.xml
===================================================================
--- trunk/docs/reference/src/main/docbook/en-US/content/classloaders.xml 2009-06-08
00:52:09 UTC (rev 994)
+++ trunk/docs/reference/src/main/docbook/en-US/content/classloaders.xml 2009-06-08
14:57:17 UTC (rev 995)
@@ -1,121 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ JBoss DNA (
http://www.jboss.org/dna)
- ~
- ~ See the COPYRIGHT.txt file distributed with this work for information
- ~ regarding copyright ownership. Some portions may be licensed
- ~ to Red Hat, Inc. under one or more contributor license agreements.
- ~ See the AUTHORS.txt file in the distribution for a full listing of
- ~ individual contributors.
- ~
- ~ JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
- ~ is licensed to you under the terms of the GNU Lesser General Public License as
- ~ published by the Free Software Foundation; either version 2.1 of
- ~ the License, or (at your option) any later version.
- ~
- ~ JBoss DNA is distributed in the hope that it will be useful,
- ~ but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- ~ or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
- ~ for more details.
- ~
- ~ You should have received a copy of the GNU Lesser General Public License
- ~ along with this distribution; if not, write to:
- ~ Free Software Foundation, Inc.
- ~ 51 Franklin Street, Fifth Floor
- ~ Boston, MA 02110-1301 USA
- -->
-<!DOCTYPE preface PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
"http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd" [
-<!ENTITY % CustomDTD SYSTEM "../custom.dtd">
-%CustomDTD;
-]>
-<chapter id="classloaders">
- <title>Class loaders</title>
- <para>
- JBoss DNA is designed around extensions: sequencers, connectors, MIME type detectors,
and class loader factories.
- The core part of JBoss DNA is relatively small and has few dependencies, while all of
the "interesting" components
- are extensions that plug into and are used by different parts of the core. The core
doesn't really care what
- the extensions do or what external libraries they require, as long as the extension
fulfills its end of the
- extension contract.
- </para>
- <para>
- This means that you only need the core modules of JBoss DNA on the application
classpath, while the extensions
- do not have to be on the application classpath. And because the core modules of JBoss
DNA have few dependencies,
- the risk of JBoss DNA libraries conflicting with the application's are lower.
Extensions, on the other hand,
- will likely have a lot of unique dependencies. By separating the core of JBoss DNA from
the class loaders used
- to load the extensions, your application is isolated from the extensions and their
dependencies. Of course,
- you can put all the JARs on the application classpath, too. (This is what the examples
in the &GettingStarted; document do.)
- </para>
- <para>
- This design also allows you to select only those extensions that are interesting and
useful for your application.
- Not every application needs all of the JBoss DNA functionality.
- Some applications may only need JBoss DNA sequencing, and specifically just a few types
of sequencers.
- Other applications may not need sequencing but do want to use JBoss DNA federation
capabilities.
- </para>
- <para>
- Finally, the use of these formal extensions also makes it easier for you to write your
own customized extensions.
- You may have proprietary file formats that you want to sequence. Or, you may have a
non-JCR repository system that you
- want to access via JCR and maybe even federate with information from other sources.
Since extensions do
- only one thing (e.g., be a sequencer, or a connector, etc.), its easier to develop
those customizations.
- </para>
- <sect1 id="dna-classloader-factory">
- <title>Class loader factory</title>
- <para>
- JBoss DNA loads all of the extension classes using class loaders returned by a
<emphasis>class loader factory</emphasis>.
- Each time JBoss DNA wants to load a class, it needs the name of the class and an
optional "class loader name".
- The meaning of the names is dependent upon the implementation of the class loader
factory. For example, the
- <link linkend="dna-maven-classloader">Maven class loader
factory</link> expects the names to be
- <ulink
url="http://maven.apache.org/pom.html#Maven_Coordinates">Maven
coordinates</ulink>. Either way,
- the class loader factory implementation uses the name to create and return a
&ClassLoader;
- instance that can be used to load the class. Of course, if no name is provided, then
a JBoss DNA service
- just uses its class loader to load the class. (This is why putting all the extension
jars on the classpath works.)
- </para>
- <para>
- The class loader factory interface is pretty simple:
- </para>
- <programlisting>
-public interface &ClassLoaderFactory; {
-
- /**
- * Get a class loader given the supplied classpath. The meaning of the classpath is
implementation-dependent.
- * @param classpath the classpath to use
- * @return the class loader; may not be null
- */
- &ClassLoader; getClassLoader( String... classpath );
-}
-</programlisting>
- <para>In the <link linkend="environment">next
chapter</link> we'll describe an &ExecutionContext; interface that is
- supplied to each of the JBoss DNA core services. This context interface actually
extends the &ClassLoaderFactory;
- interface, so setting up an &ExecutionContext; implicitly sets up the class
loader factory.</para>
- </sect1>
- <sect1 id="dna-standard-classloader">
- <title>Standard class loader factory</title>
- <para>JBoss DNA includes and uses as a default a standard class loader factory
that just loads the classes using the Thread's current context
- class loader (if there is one), or a delegate class loader that defaults to the
class loader that loaded
- the &StandardClassLoaderFactory; class. The class ignores any class loader
names that are supplied.
- </para>
- </sect1>
- <sect1 id="dna-maven-classloader">
- <title>Maven Repository class loader factory</title>
- <para>
- The <code>dna-classloader-maven</code> project has a class loader
factory implementation that parses the names into
- <ulink
url="http://maven.apache.org/pom.html#Maven_Coordinates">Maven
coordinates</ulink>, then uses those coordinates
- to look up artifacts in a Maven 2 repository. The artifact's POM file is used to
determine the dependencies,
- which is done transitively to obtain the complete dependency graph. The resulting
class loader has access
- to these artifacts in dependency order.
- </para>
- <para>
- This class loader is also able to use a JCR repository that contains the equivalent
contents of a Maven repository.
- However, JBoss DNA doesn't currently have any tooling to help populate that
repository, so this component may be
- of limited use right now.
- </para>
- </sect1>
- <sect1>
- <title>Summary</title>
- <para>
- In this chapter, we described the framework used by JBoss DNA to load extension
classes, like implementations
- of repositories, sequencers, MIME type detectors, and other components.
- <link linkend="environment">Next</link>, we cover how JBoss
security works and how the various components
- of JBoss DNA can access this security information as well as information about the
environment in which the component is running.
- </para>
- </sect1>
-</chapter>
Deleted: trunk/docs/reference/src/main/docbook/en-US/content/configuration.xml
===================================================================
--- trunk/docs/reference/src/main/docbook/en-US/content/configuration.xml 2009-06-08
00:52:09 UTC (rev 994)
+++ trunk/docs/reference/src/main/docbook/en-US/content/configuration.xml 2009-06-08
14:57:17 UTC (rev 995)
@@ -1,371 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ JBoss DNA (
http://www.jboss.org/dna)
- ~
- ~ See the COPYRIGHT.txt file distributed with this work for information
- ~ regarding copyright ownership. Some portions may be licensed
- ~ to Red Hat, Inc. under one or more contributor license agreements.
- ~ See the AUTHORS.txt file in the distribution for a full listing of
- ~ individual contributors.
- ~
- ~ JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
- ~ is licensed to you under the terms of the GNU Lesser General Public License as
- ~ published by the Free Software Foundation; either version 2.1 of
- ~ the License, or (at your option) any later version.
- ~
- ~ JBoss DNA is distributed in the hope that it will be useful,
- ~ but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- ~ or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
- ~ for more details.
- ~
- ~ You should have received a copy of the GNU Lesser General Public License
- ~ along with this distribution; if not, write to:
- ~ Free Software Foundation, Inc.
- ~ 51 Franklin Street, Fifth Floor
- ~ Boston, MA 02110-1301 USA
- -->
-<!DOCTYPE preface PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
"http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd" [
-<!ENTITY % CustomDTD SYSTEM "../custom.dtd">
-%CustomDTD;
-]>
-<chapter id="configuration">
- <title>Configuring and Using JBoss DNA</title>
- <para>Using JBoss DNA within your application is actually quite straightforward.
As you'll see in this chapter,
- the first step is setting up JBoss DNA and starting the
<code>JcrEngine</code>. After that, you obtain the
- <code>javax.jcr.Repository</code> instance for a named repository and just
use the standard JCR API throughout your
- application.
- </para>
- <sect1 id="jcr_engine">
- <title>JBoss DNA's JcrEngine</title>
- <para>
- JBoss DNA encapsulates everything necessary to run one or more JCR repositories into a
single &JcrEngine; instance.
- This includes all underlying repository sources, the pools of connections to the
sources, the sequencers,
- the MIME type detector(s), and the &Repository; implementations.
- </para>
- <para>
- Obtaining a &JcrEngine; instance is very easy - assuming that you have a valid
&JcrConfiguration; instance. We'll see
- how to get one of those in a little bit, but if you have one then all you have to do
is build and start the engine:
- </para>
- <programlisting role="JAVA"><![CDATA[
-JcrConfiguration config = ...
-JcrEngine engine = config.build();
-engine.start();
- ]]></programlisting>
- <para>
- Obtaining a JCR &Repository; instance is a matter of simply asking the engine for
it by the name defined in the configuration:
- </para>
- <programlisting role="JAVA"><![CDATA[
-javax.jcr.Repository repository = engine.getRepository("Name of repository");
- ]]></programlisting>
- <para>
- At this point, your application can proceed by working with the JCR API.
- </para>
- <para>
- And, once you're finished with the &JcrEngine;, you should shut it down:
- </para>
- <programlisting role="JAVA"><![CDATA[
-engine.shutdown();
-engine.awaitTermination(3,TimeUnit.SECONDS); // optional
- ]]></programlisting>
- <para>
- When the <code>shutdown()</code> method is called, the &Repository;
instances managed by the engine are marked as being shut down,
- and they will not be able to create new &Session;s. However, any existing
&Session;s or ongoing operations (e.g., event notifications)
- present at the time of the <code>shutdown()</code> call will be allowed to
finish.
- In essence, <code>shutdown()</code> is a
<emphasis>graceful</emphasis> request, and since it may take some time to
complete,
- you can wait until the shutdown has completed by simply calling
<code>awaitTermination(...)</code> as shown above.
- This method will block until the engine has indeed shutdown or until the supplied time
duration has passed (whichever comes first).
- And, yes, you can call the <code>awaitTermination(...)</code> method
repeatedly if needed.
- </para>
- </sect1>
- <sect1 id="jcr_configuration">
- <title>JcrConfiguration</title>
- <para>
- The previous section assumed the existence of a &JcrConfiguration;. It's not
really that creating an instance is all that difficult.
- In fact, there's only one no-argument constructor, so actually creating the
instance is a piece of cake. What can be a little more challenging,
- though, is setting up the &JcrConfiguration; instance, which must define the
following components:
- <itemizedlist>
- <listitem>
- <para><emphasis role="strong"><code>Repository
sources</code></emphasis> are the POJO objects that each describe a
particular
- location where content is stored. Each repository source object is an instance of a
JBoss DNA connector, and is configured
- with the properties that particular source. JBoss DNA's &RepositorySource;
classes are analogous to JDBC's &DataSource; classes -
- they are implemented by specific connectors (aka, "drivers") for specific
kinds of repository sources (aka, "databases").
- Similarly, a &RepositorySource; instance is analogous to a &DataSource;
instance, with bean properties for each configurable
- parameter. Therefore, each repository source definition must supply the name of the
&RepositorySource; class, any
- bean properties, and, optionally, the classpath that should be used to load the
class. </para>
- </listitem>
- <listitem>
- <para><emphasis
role="strong"><code>Repositories</code></emphasis> define
the JCR repositories that are available. Each
- repository has a unique name that is used to obtain the &Repository; instance
from the &JcrEngine;'s <code>getRepository(String)</code>
- method, but each repository definition also can include the predefined namespaces
(other than those automatically defined by
- JBoss DNA), various options, and the node types that are to be available in the
repository without explicit registration
- through the JCR API.</para>
- </listitem>
- <listitem>
- <para><emphasis
role="strong"><code>Sequencers</code></emphasis> define the
particular sequencers that are available for use.
- Each sequencer definition provides the path expressions governing which nodes in the
repository should be sequenced when those nodes change,
- and where the resulting output generated by the sequencer should be placed. The
definition also must state the name of
- the sequencer class, any bean properties and, optionally, the classpath that should
be used to load the class.</para>
- </listitem>
- <listitem>
- <para><emphasis role="strong"><code>MIME type
detectors</code></emphasis> define the particular MIME type detector(s) that
should
- be made available. A MIME type detector does exactly what the name implies: it
attempts to determine the MIME type given a
- "filename" and contents. JBoss DNA automatically uses a detector that
uses the file extension to identify the MIME type,
- but also provides an implementation that uses an external library to identify the
MIME type based upon the contents.
- The definition must state the name of the detector class, any bean properties and,
optionally, the classpath that should
- be used to load the class.</para>
- </listitem>
- </itemizedlist>
- </para>
- <para>
- There really are three options:
- <itemizedlist>
- <listitem>
- <para><emphasis role="strong"><code>Load from a
file</code></emphasis> is conceptually the easiest and requires the least
amount
- of Java code, but it now requires a configuration file.</para>
- </listitem>
- <listitem>
- <para><emphasis role="strong"><code>Load from a
configuration repository</code></emphasis> is not much more complicated than
loading
- from a file, but it does allow multiple &JcrEngine; instances (usually in
different processes perhaps on different machines)
- to easily access their (shared) configuration. And technically, loading the
configuration from a file really just creates an
- &InMemoryRepositorySource;, imports the configuration file into that source, and
then proceeds with this approach.</para>
- </listitem>
- <listitem>
- <para><emphasis role="strong"><code>Programmatic
configuration</code></emphasis> is always possible, even if the configuration
is loaded
- from a file or repository. Using the &JcrConfiguration;'s API, you can
define (or update or remove) all of the definitions that make
- up a configuration.</para>
- </listitem>
- </itemizedlist>
- </para>
- <para>
- Each of these approaches has their obvious advantages, so the choice of which one to
use is entirely up to you.
- </para>
- <sect2 id="loading_from_file">
- <title>Loading from a configuration file</title>
- <para>
- Loading the JBoss DNA configuration from a file is actually very simple:
- </para>
- <programlisting role="JAVA"><![CDATA[
-JcrConfiguration config = new JcrConfiguration();
-configuration.loadFrom(file);
- ]]></programlisting>
- <para>
- where the <code>file</code> parameter can actually be a &File;
instance, a &URL; to the file, an &InputStream;
- containing the contents of the file, or even a &String; containing the contents
of the file.
- </para>
- <note>
- <para>The <code>loadFrom(...)</code> method can be called any
number of times, but each time it is called it completely wipes
- out any current notion of the configuration and replaces it with the configuration
found in the file.
- </para>
- </note>
- <para>
- There is an optional second parameter that defines the &Path; within the
configuration file identifying the parent node of the various
- configuration nodes. If not specified, it assumes "/". This makes it
possible for the configuration content to be
- located at a different location in the hierarchical structure. (This is not often
required, but when it is required
- this second parameter is very useful.)
- </para>
- <para>
- Here is the configuration file that is used in the repository example, though it has
been simplified a bit and most comments
- have been removed for clarity):
- </para>
- <programlisting role="JAVA"><![CDATA[
-<?xml version="1.0" encoding="UTF-8"?>
-<configuration
xmlns="http://www.jboss.org/dna/1.0"
xmlns:jcr="http://www.jcp.org/jcr/1.0">
- <!--
- Define the JCR repositories
- -->
- <dna:repositories>
- <!--
- Define a JCR repository that accesses the 'Cars' source directly.
- This of course is optional, since we could access the same content through
'vehicles'.
- -->
- <dna:repository jcr:name="car repository"
dna:source="Cars">
- <options jcr:primaryType="dna:options"/>
- <jaasLoginConfigName jcr:primaryType="dna:option"
dna:value="dna-jcr"/>
- </options>
- </dna:repository>
- </dna:repositories>
- <!--
- Define the sources for the content. These sources are directly accessible using the
DNA-specific Graph API.
- -->
- <dna:sources jcr:primaryType="nt:unstructured">
- <dna:source jcr:name="Cars"
dna:classname="org.jboss.dna.graph.connector.inmemory.InMemoryRepositorySource"
dna:retryLimit="3" dna:defaultWorkspaceName="workspace1"/>
- <dna:source jcr:name="Aircraft"
dna:classname="org.jboss.dna.graph.connector.inmemory.InMemoryRepositorySource">
- <!-- Define the name of the workspace used by default. Optional, but
convenient. -->
- <defaultWorkspaceName>workspace2</defaultWorkspaceName>
- </dna:source>
- </dna:sources>
- <!--
- Define the sequencers. This is an optional section. For this example, we're not
using any sequencers.
- -->
- <dna:sequencers>
- <!--dna:sequencer jcr:name="Image Sequencer"
dna:classname="org.jboss.dna.sequencer.image.ImageMetadataSequencer">
- <dna:description>Image metadata sequencer</dna:description>
- <dna:pathExpression>/foo/source =>
/foo/target</dna:pathExpression>
- <dna:pathExpression>/bar/source =>
/bar/target</dna:pathExpression>
- </dna:sequencer-->
- </dna:sequencers>
- <dna:mimeTypeDetectors>
- <dna:mimeTypeDetector jcr:name="Detector"
dna:description="Standard extension-based MIME type detector"/>
- </dna:mimeTypeDetectors>
-</configuration>
- ]]></programlisting>
- </sect2>
- <sect2 id="loading_from_repository">
- <title>Loading from a configuration repository</title>
- <para>
- Loading the JBoss DNA configuration from an existing repository is also pretty
straightforward. Simply create and configure the
- &RepositorySource; instance to point to the desired repository, and then call the
<code>loadFrom(&RepositorySource; source)</code>
- method:
- </para>
- <programlisting role="JAVA"><![CDATA[
-RepositorySource configSource = ...
-JcrConfiguration config = new JcrConfiguration();
-configuration.loadFrom(configSource);
- ]]></programlisting>
- <para>
- This really is a more advanced way to define your configuration, so we won't go
into how you configure a &RepositorySource;.
- For more information, consult the &ReferenceGuide;.
- </para>
- <note>
- <para>The <code>loadFrom(...)</code> method can be called any
number of times, but each time it is called it completely wipes
- out any current notion of the configuration and replaces it with the configuration
found in the file.
- </para>
- </note>
- <para>
- There is an optional second parameter that defines the name of the workspace in the
supplied source where the configuration content
- can be found. It is not needed if the workspace is the source's default
workspace.
- There is an optional third parameter that defines the &Path; within the
configuration repository identifying the parent node of the various
- configuration nodes. If not specified, it assumes "/". This makes it
possible for the configuration content to be
- located at a different location in the hierarchical structure. (This is not often
required, but when it is required
- this second parameter is very useful.)
- </para>
- </sect2>
- <sect2 id="programmatic_configuration">
- <title>Programmatic configuration</title>
- <para>
- Defining the configuration programmatically is not terribly complicated, and it for
obvious reasons results in more verbose Java code.
- But this approach is very useful and often the easiest approach when the
configuration must change or is a reflection of other
- dynamic information.
- </para>
- <para>
- The &JcrConfiguration; class was designed to have an easy-to-use API that makes
it easy to configure each of the different kinds of
- components, especially when using an IDE with code completion. Here are several
examples:
- </para>
- <sect3 id="programmatically_configuring_sources">
- <title>Repository sources</title>
- <para>Each repository source definition must include the name of the
&RepositorySource; class as well as each bean property
- that should be set on the object:
- </para>
- <programlisting role="JAVA"><![CDATA[
-JcrConfiguration config = ...
-config.repositorySource("source A")
- .usingClass(InMemoryRepositorySource.class)
- .setDescription("The repository for our content")
- .setProperty("defaultWorkspaceName", workspaceName);
- ]]></programlisting>
- <para>
- This example defines an in-memory source with the name "source A", a
description, and a single "defaultWorkspaceName" bean property.
- Different &RepositorySource; implementations will the bean properties that are
required and optional.
- Of course, the class can be specified as Class reference or a string (followed by
whether the class should be loaded from
- the classpath or from a specific classpath).
- </para>
- <note>
- <para>Each time <code>repositorySource(String)</code> is called,
it will either load the existing definition with the supplied
- name or will create a new definition if one does not already exist. To remove a
definition, simply call <code>remove()</code>
- on the result of <code>repositorySource(String)</code>.
- The set of existing definitions can be accessed with the
<code>repositorySources()</code> method.
- </para>
- </note>
- </sect3>
- <sect3 id="programmatically_configuring_repositories">
- <title>Repositories</title>
- <para>Each repository must be defined to use a named repository source, but all
other aspects (e.g., namespaces, node types, options)
- are optional.</para>
- <programlisting role="JAVA"><![CDATA[
-JcrConfiguration config = ...
-config.repository("repository A")
- .addNodeTypes("myCustomNodeTypes.cnd")
- .setSource("source 1")
- .registerNamespace("acme","http://www.example.com/acme")
- .setOption(JcrRepository.Option.JAAS_LOGIN_CONFIG_NAME, "dna-jcr");
- ]]></programlisting>
- <para>
- This example defines a repository that uses the "source 1" repository
source (which could be a federated source, an in-memory source,
- a database store, or any other source). Additionally, this example adds the node
types in the "myCustomNodeTypes.cnd" file as those
- that will be made available when the repository is accessed. It also defines the
"http://www.example.com/acme" namespace,
- and finally sets the "JAAS_LOGIN_CONFIG_NAME" option to define the name of
the JAAS login configuration that should be used by
- the JBoss DNA repository.
- </para>
- <note>
- <para>Each time <code>repository(String)</code> is called, it will
either load the existing definition with the supplied
- name or will create a new definition if one does not already exist. To remove a
definition, simply call <code>remove()</code>
- on the result of <code>repository(String)</code>.
- The set of existing definitions can be accessed with the
<code>repositories()</code> method.
- </para>
- </note>
- </sect3>
- <sect3 id="programmatically_configuring_sequencers">
- <title>Sequencers</title>
- <para>Each defined sequencer must specify the name of the &StreamSequencer;
implementation class as well as the path expressions
- defining which nodes should be sequenced and the output paths defining where the
sequencer output should be placed (often as a function
- of the input path expression).</para>
- <programlisting role="JAVA"><![CDATA[
-JcrConfiguration config = ...
-config.sequencer("Image Sequencer")
- .usingClass("org.jboss.dna.sequencer.image.ImageMetadataSequencer")
- .loadedFromClasspath()
- .setDescription("Sequences image files to extract the characteristics of the
image")
-
.sequencingFrom("//(*.(jpg|jpeg|gif|bmp|pcx|png|iff|ras|pbm|pgm|ppm|psd)[*])/jcr:content[@jcr:data]")
- .andOutputtingTo("/images/$1");
- ]]></programlisting>
- <para>
- This shows an example of a sequencer definition named "Image Sequencer"
that uses the &ImageMetadataSequencer; class
- (loaded from the classpath), that is to sequence the "jcr:data" property
on any new or changed nodes that are named
- "jcr:content" below a parent node with a name ending in ".jpg",
".jpeg", ".gif", ".bmp", ".pcx", ".iff",
".ras",
- ".pbm", ".pgm", ".ppm" or ".psd". The
output of the sequencing operation should be placed at the "/images/$1" node,
- where the "$1" value is captured as the name of the parent node. (The
capture groups work the same was as regular expressions;
- see the &ReferenceGuide; for more details.)
- Of course, the class can be specified as Class reference or a string (followed by
whether the class should be loaded from
- the classpath or from a specific classpath).
- </para>
- <note>
- <para>Each time <code>sequencer(String)</code> is called, it will
either load the existing definition with the supplied
- name or will create a new definition if one does not already exist. To remove a
definition, simply call <code>remove()</code>
- on the result of <code>sequencer(String)</code>.
- The set of existing definitions can be accessed with the
<code>sequencers()</code> method.
- </para>
- </note>
- </sect3>
- <sect3 id="programmatically_configuring_mime_type_detectors">
- <title>MIME type detectors</title>
- <para>Each defined MIME type detector must specify the name of the
&MimeTypeDetector; implementation class as well as any
- other bean properties required by the implementation.</para>
- <programlisting role="JAVA"><![CDATA[
-JcrConfiguration config = ...
-config.mimeTypeDetector("Extension Detector")
- .usingClass(org.jboss.dna.graph.mimetype.ExtensionBasedMimeTypeDetector.class);
- ]]></programlisting>
- <para>
- Of course, the class can be specified as Class reference or a string (followed by
whether the class should be loaded from
- the classpath or from a specific classpath).
- </para>
- <note>
- <para>Each time <code>mimeTypeDetector(String)</code> is called,
it will either load the existing definition with the supplied
- name or will create a new definition if one does not already exist. To remove a
definition, simply call <code>remove()</code>
- on the result of <code>mimeTypeDetector(String)</code>.
- The set of existing definitions can be accessed with the
<code>mimeTypeDetectors()</code> method.
- </para>
- </note>
- </sect3>
- </sect2>
- </sect1>
- <sect1 id="using_dna_whats_next">
- <title>What's next</title>
- <para>
- This chapter outline how you configure JBoss DNA, how you then access a
<code>javax.jcr.Repository</code> instance,
- and use the standard JCR API to interact with the repository. The
- <link linkend="downloading_and_running">next chapter </link>
walks you through downloading
- and running the JBoss DNA examples.
- </para>
- </sect1>
-</chapter>
Added: trunk/docs/reference/src/main/docbook/en-US/content/connectors/in_memory.xml
===================================================================
--- trunk/docs/reference/src/main/docbook/en-US/content/connectors/in_memory.xml
(rev 0)
+++
trunk/docs/reference/src/main/docbook/en-US/content/connectors/in_memory.xml 2009-06-08
14:57:17 UTC (rev 995)
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ JBoss DNA (
http://www.jboss.org/dna)
+ ~
+ ~ See the COPYRIGHT.txt file distributed with this work for information
+ ~ regarding copyright ownership. Some portions may be licensed
+ ~ to Red Hat, Inc. under one or more contributor license agreements.
+ ~ See the AUTHORS.txt file in the distribution for a full listing of
+ ~ individual contributors.
+ ~
+ ~ JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
+ ~ is licensed to you under the terms of the GNU Lesser General Public License as
+ ~ published by the Free Software Foundation; either version 2.1 of
+ ~ the License, or (at your option) any later version.
+ ~
+ ~ JBoss DNA is distributed in the hope that it will be useful,
+ ~ but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ ~ or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ ~ for more details.
+ ~
+ ~ You should have received a copy of the GNU Lesser General Public License
+ ~ along with this distribution; if not, write to:
+ ~ Free Software Foundation, Inc.
+ ~ 51 Franklin Street, Fifth Floor
+ ~ Boston, MA 02110-1301 USA
+ -->
+<!DOCTYPE preface PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
"http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd" [
+<!ENTITY % CustomDTD SYSTEM "../../custom.dtd">
+%CustomDTD;
+]>
+<chapter id="in-memory-connector">
+ <title>In-Memory Connector</title>
+ <para>
+ blah blah
+ </para>
+</chapter>
+
Property changes on:
trunk/docs/reference/src/main/docbook/en-US/content/connectors/in_memory.xml
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Copied: trunk/docs/reference/src/main/docbook/en-US/content/core/classloaders.xml (from
rev 994, trunk/docs/reference/src/main/docbook/en-US/content/classloaders.xml)
===================================================================
--- trunk/docs/reference/src/main/docbook/en-US/content/core/classloaders.xml
(rev 0)
+++ trunk/docs/reference/src/main/docbook/en-US/content/core/classloaders.xml 2009-06-08
14:57:17 UTC (rev 995)
@@ -0,0 +1,121 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ JBoss DNA (
http://www.jboss.org/dna)
+ ~
+ ~ See the COPYRIGHT.txt file distributed with this work for information
+ ~ regarding copyright ownership. Some portions may be licensed
+ ~ to Red Hat, Inc. under one or more contributor license agreements.
+ ~ See the AUTHORS.txt file in the distribution for a full listing of
+ ~ individual contributors.
+ ~
+ ~ JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
+ ~ is licensed to you under the terms of the GNU Lesser General Public License as
+ ~ published by the Free Software Foundation; either version 2.1 of
+ ~ the License, or (at your option) any later version.
+ ~
+ ~ JBoss DNA is distributed in the hope that it will be useful,
+ ~ but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ ~ or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ ~ for more details.
+ ~
+ ~ You should have received a copy of the GNU Lesser General Public License
+ ~ along with this distribution; if not, write to:
+ ~ Free Software Foundation, Inc.
+ ~ 51 Franklin Street, Fifth Floor
+ ~ Boston, MA 02110-1301 USA
+ -->
+<!DOCTYPE preface PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
"http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd" [
+<!ENTITY % CustomDTD SYSTEM "../../custom.dtd">
+%CustomDTD;
+]>
+<chapter id="classloaders">
+ <title>Class loaders</title>
+ <para>
+ JBoss DNA is designed around extensions: sequencers, connectors, MIME type detectors,
and class loader factories.
+ The core part of JBoss DNA is relatively small and has few dependencies, while all of
the "interesting" components
+ are extensions that plug into and are used by different parts of the core. The core
doesn't really care what
+ the extensions do or what external libraries they require, as long as the extension
fulfills its end of the
+ extension contract.
+ </para>
+ <para>
+ This means that you only need the core modules of JBoss DNA on the application
classpath, while the extensions
+ do not have to be on the application classpath. And because the core modules of JBoss
DNA have few dependencies,
+ the risk of JBoss DNA libraries conflicting with the application's are lower.
Extensions, on the other hand,
+ will likely have a lot of unique dependencies. By separating the core of JBoss DNA from
the class loaders used
+ to load the extensions, your application is isolated from the extensions and their
dependencies. Of course,
+ you can put all the JARs on the application classpath, too. (This is what the examples
in the &GettingStarted; document do.)
+ </para>
+ <para>
+ This design also allows you to select only those extensions that are interesting and
useful for your application.
+ Not every application needs all of the JBoss DNA functionality.
+ Some applications may only need JBoss DNA sequencing, and specifically just a few types
of sequencers.
+ Other applications may not need sequencing but do want to use JBoss DNA federation
capabilities.
+ </para>
+ <para>
+ Finally, the use of these formal extensions also makes it easier for you to write your
own customized extensions.
+ You may have proprietary file formats that you want to sequence. Or, you may have a
non-JCR repository system that you
+ want to access via JCR and maybe even federate with information from other sources.
Since extensions do
+ only one thing (e.g., be a sequencer, or a connector, etc.), its easier to develop
those customizations.
+ </para>
+ <sect1 id="dna-classloader-factory">
+ <title>Class loader factory</title>
+ <para>
+ JBoss DNA loads all of the extension classes using class loaders returned by a
<emphasis>class loader factory</emphasis>.
+ Each time JBoss DNA wants to load a class, it needs the name of the class and an
optional "class loader name".
+ The meaning of the names is dependent upon the implementation of the class loader
factory. For example, the
+ <link linkend="dna-maven-classloader">Maven class loader
factory</link> expects the names to be
+ <ulink
url="http://maven.apache.org/pom.html#Maven_Coordinates">Maven
coordinates</ulink>. Either way,
+ the class loader factory implementation uses the name to create and return a
&ClassLoader;
+ instance that can be used to load the class. Of course, if no name is provided, then
a JBoss DNA service
+ just uses its class loader to load the class. (This is why putting all the extension
jars on the classpath works.)
+ </para>
+ <para>
+ The class loader factory interface is pretty simple:
+ </para>
+ <programlisting>
+public interface &ClassLoaderFactory; {
+ /**
+ * Get a class loader given the supplied classpath. The meaning of the classpath
+ * is implementation-dependent.
+ * @param classpath the classpath to use
+ * @return the class loader; may not be null
+ */
+ &ClassLoader; getClassLoader( &String;... classpath );
+}
+</programlisting>
+ <para>In the <link linkend="environment">next
chapter</link> we'll describe an &ExecutionContext; interface that is
+ supplied to each of the JBoss DNA core services. This context interface actually
extends the &ClassLoaderFactory;
+ interface, so setting up an &ExecutionContext; implicitly sets up the class
loader factory.</para>
+ </sect1>
+ <sect1 id="dna-standard-classloader">
+ <title>Standard class loader factory</title>
+ <para>JBoss DNA includes and uses as a default a standard class loader factory
that just loads the classes using the Thread's current context
+ class loader (if there is one), or a delegate class loader that defaults to the
class loader that loaded
+ the &StandardClassLoaderFactory; class. The class ignores any class loader
names that are supplied.
+ </para>
+ </sect1>
+ <sect1 id="dna-maven-classloader">
+ <title>Maven Repository class loader factory</title>
+ <para>
+ The <code>dna-classloader-maven</code> project has a class loader
factory implementation that parses the names into
+ <ulink
url="http://maven.apache.org/pom.html#Maven_Coordinates">Maven
coordinates</ulink>, then uses those coordinates
+ to look up artifacts in a Maven 2 repository. The artifact's POM file is used to
determine the dependencies,
+ which is done transitively to obtain the complete dependency graph. The resulting
class loader has access
+ to these artifacts in dependency order.
+ </para>
+ <para>
+ This class loader is also able to use a JCR repository that contains the equivalent
contents of a Maven repository.
+ However, JBoss DNA doesn't currently have any tooling to help populate that
repository, so this component may be
+ of limited use right now.
+ </para>
+ </sect1>
+ <sect1>
+ <title>Summary</title>
+ <para>
+ In this chapter, we described the framework used by JBoss DNA to load extension
classes, like implementations
+ of repositories, sequencers, MIME type detectors, and other components.
+ <link linkend="environment">Next</link>, we cover how JBoss
security works and how the various components
+ of JBoss DNA can access this security information as well as information about the
environment in which the component is running.
+ </para>
+ </sect1>
+</chapter>
Property changes on:
trunk/docs/reference/src/main/docbook/en-US/content/core/classloaders.xml
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Copied: trunk/docs/reference/src/main/docbook/en-US/content/core/configuration.xml (from
rev 994, trunk/docs/reference/src/main/docbook/en-US/content/configuration.xml)
===================================================================
--- trunk/docs/reference/src/main/docbook/en-US/content/core/configuration.xml
(rev 0)
+++ trunk/docs/reference/src/main/docbook/en-US/content/core/configuration.xml 2009-06-08
14:57:17 UTC (rev 995)
@@ -0,0 +1,371 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ JBoss DNA (
http://www.jboss.org/dna)
+ ~
+ ~ See the COPYRIGHT.txt file distributed with this work for information
+ ~ regarding copyright ownership. Some portions may be licensed
+ ~ to Red Hat, Inc. under one or more contributor license agreements.
+ ~ See the AUTHORS.txt file in the distribution for a full listing of
+ ~ individual contributors.
+ ~
+ ~ JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
+ ~ is licensed to you under the terms of the GNU Lesser General Public License as
+ ~ published by the Free Software Foundation; either version 2.1 of
+ ~ the License, or (at your option) any later version.
+ ~
+ ~ JBoss DNA is distributed in the hope that it will be useful,
+ ~ but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ ~ or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ ~ for more details.
+ ~
+ ~ You should have received a copy of the GNU Lesser General Public License
+ ~ along with this distribution; if not, write to:
+ ~ Free Software Foundation, Inc.
+ ~ 51 Franklin Street, Fifth Floor
+ ~ Boston, MA 02110-1301 USA
+ -->
+<!DOCTYPE preface PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
"http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd" [
+<!ENTITY % CustomDTD SYSTEM "../../custom.dtd">
+%CustomDTD;
+]>
+<chapter id="configuration">
+ <title>Configuring and Using JBoss DNA</title>
+ <para>Using JBoss DNA within your application is actually quite straightforward.
As you'll see in this chapter,
+ the first step is setting up JBoss DNA and starting the
<code>JcrEngine</code>. After that, you obtain the
+ <code>javax.jcr.Repository</code> instance for a named repository and just
use the standard JCR API throughout your
+ application.
+ </para>
+ <sect1 id="jcr_engine">
+ <title>JBoss DNA's JcrEngine</title>
+ <para>
+ JBoss DNA encapsulates everything necessary to run one or more JCR repositories into a
single &JcrEngine; instance.
+ This includes all underlying repository sources, the pools of connections to the
sources, the sequencers,
+ the MIME type detector(s), and the &Repository; implementations.
+ </para>
+ <para>
+ Obtaining a &JcrEngine; instance is very easy - assuming that you have a valid
&JcrConfiguration; instance. We'll see
+ how to get one of those in a little bit, but if you have one then all you have to do
is build and start the engine:
+ </para>
+ <programlisting role="JAVA"><![CDATA[
+JcrConfiguration config = ...
+JcrEngine engine = config.build();
+engine.start();
+ ]]></programlisting>
+ <para>
+ Obtaining a JCR &Repository; instance is a matter of simply asking the engine for
it by the name defined in the configuration:
+ </para>
+ <programlisting role="JAVA"><![CDATA[
+javax.jcr.Repository repository = engine.getRepository("Name of repository");
+ ]]></programlisting>
+ <para>
+ At this point, your application can proceed by working with the JCR API.
+ </para>
+ <para>
+ And, once you're finished with the &JcrEngine;, you should shut it down:
+ </para>
+ <programlisting role="JAVA"><![CDATA[
+engine.shutdown();
+engine.awaitTermination(3,TimeUnit.SECONDS); // optional
+ ]]></programlisting>
+ <para>
+ When the <code>shutdown()</code> method is called, the &Repository;
instances managed by the engine are marked as being shut down,
+ and they will not be able to create new &Session;s. However, any existing
&Session;s or ongoing operations (e.g., event notifications)
+ present at the time of the <code>shutdown()</code> call will be allowed to
finish.
+ In essence, <code>shutdown()</code> is a
<emphasis>graceful</emphasis> request, and since it may take some time to
complete,
+ you can wait until the shutdown has completed by simply calling
<code>awaitTermination(...)</code> as shown above.
+ This method will block until the engine has indeed shutdown or until the supplied time
duration has passed (whichever comes first).
+ And, yes, you can call the <code>awaitTermination(...)</code> method
repeatedly if needed.
+ </para>
+ </sect1>
+ <sect1 id="jcr_configuration">
+ <title>JcrConfiguration</title>
+ <para>
+ The previous section assumed the existence of a &JcrConfiguration;. It's not
really that creating an instance is all that difficult.
+ In fact, there's only one no-argument constructor, so actually creating the
instance is a piece of cake. What can be a little more challenging,
+ though, is setting up the &JcrConfiguration; instance, which must define the
following components:
+ <itemizedlist>
+ <listitem>
+ <para><emphasis role="strong"><code>Repository
sources</code></emphasis> are the POJO objects that each describe a
particular
+ location where content is stored. Each repository source object is an instance of a
JBoss DNA connector, and is configured
+ with the properties that particular source. JBoss DNA's &RepositorySource;
classes are analogous to JDBC's &DataSource; classes -
+ they are implemented by specific connectors (aka, "drivers") for specific
kinds of repository sources (aka, "databases").
+ Similarly, a &RepositorySource; instance is analogous to a &DataSource;
instance, with bean properties for each configurable
+ parameter. Therefore, each repository source definition must supply the name of the
&RepositorySource; class, any
+ bean properties, and, optionally, the classpath that should be used to load the
class. </para>
+ </listitem>
+ <listitem>
+ <para><emphasis
role="strong"><code>Repositories</code></emphasis> define
the JCR repositories that are available. Each
+ repository has a unique name that is used to obtain the &Repository; instance
from the &JcrEngine;'s <code>getRepository(String)</code>
+ method, but each repository definition also can include the predefined namespaces
(other than those automatically defined by
+ JBoss DNA), various options, and the node types that are to be available in the
repository without explicit registration
+ through the JCR API.</para>
+ </listitem>
+ <listitem>
+ <para><emphasis
role="strong"><code>Sequencers</code></emphasis> define the
particular sequencers that are available for use.
+ Each sequencer definition provides the path expressions governing which nodes in the
repository should be sequenced when those nodes change,
+ and where the resulting output generated by the sequencer should be placed. The
definition also must state the name of
+ the sequencer class, any bean properties and, optionally, the classpath that should
be used to load the class.</para>
+ </listitem>
+ <listitem>
+ <para><emphasis role="strong"><code>MIME type
detectors</code></emphasis> define the particular MIME type detector(s) that
should
+ be made available. A MIME type detector does exactly what the name implies: it
attempts to determine the MIME type given a
+ "filename" and contents. JBoss DNA automatically uses a detector that
uses the file extension to identify the MIME type,
+ but also provides an implementation that uses an external library to identify the
MIME type based upon the contents.
+ The definition must state the name of the detector class, any bean properties and,
optionally, the classpath that should
+ be used to load the class.</para>
+ </listitem>
+ </itemizedlist>
+ </para>
+ <para>
+ There really are three options:
+ <itemizedlist>
+ <listitem>
+ <para><emphasis role="strong"><code>Load from a
file</code></emphasis> is conceptually the easiest and requires the least
amount
+ of Java code, but it now requires a configuration file.</para>
+ </listitem>
+ <listitem>
+ <para><emphasis role="strong"><code>Load from a
configuration repository</code></emphasis> is not much more complicated than
loading
+ from a file, but it does allow multiple &JcrEngine; instances (usually in
different processes perhaps on different machines)
+ to easily access their (shared) configuration. And technically, loading the
configuration from a file really just creates an
+ &InMemoryRepositorySource;, imports the configuration file into that source, and
then proceeds with this approach.</para>
+ </listitem>
+ <listitem>
+ <para><emphasis role="strong"><code>Programmatic
configuration</code></emphasis> is always possible, even if the configuration
is loaded
+ from a file or repository. Using the &JcrConfiguration;'s API, you can
define (or update or remove) all of the definitions that make
+ up a configuration.</para>
+ </listitem>
+ </itemizedlist>
+ </para>
+ <para>
+ Each of these approaches has their obvious advantages, so the choice of which one to
use is entirely up to you.
+ </para>
+ <sect2 id="loading_from_file">
+ <title>Loading from a configuration file</title>
+ <para>
+ Loading the JBoss DNA configuration from a file is actually very simple:
+ </para>
+ <programlisting role="JAVA"><![CDATA[
+JcrConfiguration config = new JcrConfiguration();
+configuration.loadFrom(file);
+ ]]></programlisting>
+ <para>
+ where the <code>file</code> parameter can actually be a &File;
instance, a &URL; to the file, an &InputStream;
+ containing the contents of the file, or even a &String; containing the contents
of the file.
+ </para>
+ <note>
+ <para>The <code>loadFrom(...)</code> method can be called any
number of times, but each time it is called it completely wipes
+ out any current notion of the configuration and replaces it with the configuration
found in the file.
+ </para>
+ </note>
+ <para>
+ There is an optional second parameter that defines the &Path; within the
configuration file identifying the parent node of the various
+ configuration nodes. If not specified, it assumes "/". This makes it
possible for the configuration content to be
+ located at a different location in the hierarchical structure. (This is not often
required, but when it is required
+ this second parameter is very useful.)
+ </para>
+ <para>
+ Here is the configuration file that is used in the repository example, though it has
been simplified a bit and most comments
+ have been removed for clarity):
+ </para>
+ <programlisting role="JAVA"><![CDATA[
+<?xml version="1.0" encoding="UTF-8"?>
+<configuration
xmlns="http://www.jboss.org/dna/1.0"
xmlns:jcr="http://www.jcp.org/jcr/1.0">
+ <!--
+ Define the JCR repositories
+ -->
+ <dna:repositories>
+ <!--
+ Define a JCR repository that accesses the 'Cars' source directly.
+ This of course is optional, since we could access the same content through
'vehicles'.
+ -->
+ <dna:repository jcr:name="car repository"
dna:source="Cars">
+ <options jcr:primaryType="dna:options"/>
+ <jaasLoginConfigName jcr:primaryType="dna:option"
dna:value="dna-jcr"/>
+ </options>
+ </dna:repository>
+ </dna:repositories>
+ <!--
+ Define the sources for the content. These sources are directly accessible using the
DNA-specific Graph API.
+ -->
+ <dna:sources jcr:primaryType="nt:unstructured">
+ <dna:source jcr:name="Cars"
dna:classname="org.jboss.dna.graph.connector.inmemory.InMemoryRepositorySource"
dna:retryLimit="3" dna:defaultWorkspaceName="workspace1"/>
+ <dna:source jcr:name="Aircraft"
dna:classname="org.jboss.dna.graph.connector.inmemory.InMemoryRepositorySource">
+ <!-- Define the name of the workspace used by default. Optional, but
convenient. -->
+ <defaultWorkspaceName>workspace2</defaultWorkspaceName>
+ </dna:source>
+ </dna:sources>
+ <!--
+ Define the sequencers. This is an optional section. For this example, we're not
using any sequencers.
+ -->
+ <dna:sequencers>
+ <!--dna:sequencer jcr:name="Image Sequencer"
dna:classname="org.jboss.dna.sequencer.image.ImageMetadataSequencer">
+ <dna:description>Image metadata sequencer</dna:description>
+ <dna:pathExpression>/foo/source =>
/foo/target</dna:pathExpression>
+ <dna:pathExpression>/bar/source =>
/bar/target</dna:pathExpression>
+ </dna:sequencer-->
+ </dna:sequencers>
+ <dna:mimeTypeDetectors>
+ <dna:mimeTypeDetector jcr:name="Detector"
dna:description="Standard extension-based MIME type detector"/>
+ </dna:mimeTypeDetectors>
+</configuration>
+ ]]></programlisting>
+ </sect2>
+ <sect2 id="loading_from_repository">
+ <title>Loading from a configuration repository</title>
+ <para>
+ Loading the JBoss DNA configuration from an existing repository is also pretty
straightforward. Simply create and configure the
+ &RepositorySource; instance to point to the desired repository, and then call the
<code>loadFrom(&RepositorySource; source)</code>
+ method:
+ </para>
+ <programlisting role="JAVA"><![CDATA[
+RepositorySource configSource = ...
+JcrConfiguration config = new JcrConfiguration();
+configuration.loadFrom(configSource);
+ ]]></programlisting>
+ <para>
+ This really is a more advanced way to define your configuration, so we won't go
into how you configure a &RepositorySource;.
+ For more information, consult the &ReferenceGuide;.
+ </para>
+ <note>
+ <para>The <code>loadFrom(...)</code> method can be called any
number of times, but each time it is called it completely wipes
+ out any current notion of the configuration and replaces it with the configuration
found in the file.
+ </para>
+ </note>
+ <para>
+ There is an optional second parameter that defines the name of the workspace in the
supplied source where the configuration content
+ can be found. It is not needed if the workspace is the source's default
workspace.
+ There is an optional third parameter that defines the &Path; within the
configuration repository identifying the parent node of the various
+ configuration nodes. If not specified, it assumes "/". This makes it
possible for the configuration content to be
+ located at a different location in the hierarchical structure. (This is not often
required, but when it is required
+ this second parameter is very useful.)
+ </para>
+ </sect2>
+ <sect2 id="programmatic_configuration">
+ <title>Programmatic configuration</title>
+ <para>
+ Defining the configuration programmatically is not terribly complicated, and it for
obvious reasons results in more verbose Java code.
+ But this approach is very useful and often the easiest approach when the
configuration must change or is a reflection of other
+ dynamic information.
+ </para>
+ <para>
+ The &JcrConfiguration; class was designed to have an easy-to-use API that makes
it easy to configure each of the different kinds of
+ components, especially when using an IDE with code completion. Here are several
examples:
+ </para>
+ <sect3 id="programmatically_configuring_sources">
+ <title>Repository sources</title>
+ <para>Each repository source definition must include the name of the
&RepositorySource; class as well as each bean property
+ that should be set on the object:
+ </para>
+ <programlisting role="JAVA"><![CDATA[
+JcrConfiguration config = ...
+config.repositorySource("source A")
+ .usingClass(InMemoryRepositorySource.class)
+ .setDescription("The repository for our content")
+ .setProperty("defaultWorkspaceName", workspaceName);
+ ]]></programlisting>
+ <para>
+ This example defines an in-memory source with the name "source A", a
description, and a single "defaultWorkspaceName" bean property.
+ Different &RepositorySource; implementations will the bean properties that are
required and optional.
+ Of course, the class can be specified as Class reference or a string (followed by
whether the class should be loaded from
+ the classpath or from a specific classpath).
+ </para>
+ <note>
+ <para>Each time <code>repositorySource(String)</code> is called,
it will either load the existing definition with the supplied
+ name or will create a new definition if one does not already exist. To remove a
definition, simply call <code>remove()</code>
+ on the result of <code>repositorySource(String)</code>.
+ The set of existing definitions can be accessed with the
<code>repositorySources()</code> method.
+ </para>
+ </note>
+ </sect3>
+ <sect3 id="programmatically_configuring_repositories">
+ <title>Repositories</title>
+ <para>Each repository must be defined to use a named repository source, but all
other aspects (e.g., namespaces, node types, options)
+ are optional.</para>
+ <programlisting role="JAVA"><![CDATA[
+JcrConfiguration config = ...
+config.repository("repository A")
+ .addNodeTypes("myCustomNodeTypes.cnd")
+ .setSource("source 1")
+ .registerNamespace("acme","http://www.example.com/acme")
+ .setOption(JcrRepository.Option.JAAS_LOGIN_CONFIG_NAME, "dna-jcr");
+ ]]></programlisting>
+ <para>
+ This example defines a repository that uses the "source 1" repository
source (which could be a federated source, an in-memory source,
+ a database store, or any other source). Additionally, this example adds the node
types in the "myCustomNodeTypes.cnd" file as those
+ that will be made available when the repository is accessed. It also defines the
"http://www.example.com/acme" namespace,
+ and finally sets the "JAAS_LOGIN_CONFIG_NAME" option to define the name of
the JAAS login configuration that should be used by
+ the JBoss DNA repository.
+ </para>
+ <note>
+ <para>Each time <code>repository(String)</code> is called, it will
either load the existing definition with the supplied
+ name or will create a new definition if one does not already exist. To remove a
definition, simply call <code>remove()</code>
+ on the result of <code>repository(String)</code>.
+ The set of existing definitions can be accessed with the
<code>repositories()</code> method.
+ </para>
+ </note>
+ </sect3>
+ <sect3 id="programmatically_configuring_sequencers">
+ <title>Sequencers</title>
+ <para>Each defined sequencer must specify the name of the &StreamSequencer;
implementation class as well as the path expressions
+ defining which nodes should be sequenced and the output paths defining where the
sequencer output should be placed (often as a function
+ of the input path expression).</para>
+ <programlisting role="JAVA"><![CDATA[
+JcrConfiguration config = ...
+config.sequencer("Image Sequencer")
+ .usingClass("org.jboss.dna.sequencer.image.ImageMetadataSequencer")
+ .loadedFromClasspath()
+ .setDescription("Sequences image files to extract the characteristics of the
image")
+
.sequencingFrom("//(*.(jpg|jpeg|gif|bmp|pcx|png|iff|ras|pbm|pgm|ppm|psd)[*])/jcr:content[@jcr:data]")
+ .andOutputtingTo("/images/$1");
+ ]]></programlisting>
+ <para>
+ This shows an example of a sequencer definition named "Image Sequencer"
that uses the &ImageMetadataSequencer; class
+ (loaded from the classpath), that is to sequence the "jcr:data" property
on any new or changed nodes that are named
+ "jcr:content" below a parent node with a name ending in ".jpg",
".jpeg", ".gif", ".bmp", ".pcx", ".iff",
".ras",
+ ".pbm", ".pgm", ".ppm" or ".psd". The
output of the sequencing operation should be placed at the "/images/$1" node,
+ where the "$1" value is captured as the name of the parent node. (The
capture groups work the same was as regular expressions;
+ see the &ReferenceGuide; for more details.)
+ Of course, the class can be specified as Class reference or a string (followed by
whether the class should be loaded from
+ the classpath or from a specific classpath).
+ </para>
+ <note>
+ <para>Each time <code>sequencer(String)</code> is called, it will
either load the existing definition with the supplied
+ name or will create a new definition if one does not already exist. To remove a
definition, simply call <code>remove()</code>
+ on the result of <code>sequencer(String)</code>.
+ The set of existing definitions can be accessed with the
<code>sequencers()</code> method.
+ </para>
+ </note>
+ </sect3>
+ <sect3 id="programmatically_configuring_mime_type_detectors">
+ <title>MIME type detectors</title>
+ <para>Each defined MIME type detector must specify the name of the
&MimeTypeDetector; implementation class as well as any
+ other bean properties required by the implementation.</para>
+ <programlisting role="JAVA"><![CDATA[
+JcrConfiguration config = ...
+config.mimeTypeDetector("Extension Detector")
+ .usingClass(org.jboss.dna.graph.mimetype.ExtensionBasedMimeTypeDetector.class);
+ ]]></programlisting>
+ <para>
+ Of course, the class can be specified as Class reference or a string (followed by
whether the class should be loaded from
+ the classpath or from a specific classpath).
+ </para>
+ <note>
+ <para>Each time <code>mimeTypeDetector(String)</code> is called,
it will either load the existing definition with the supplied
+ name or will create a new definition if one does not already exist. To remove a
definition, simply call <code>remove()</code>
+ on the result of <code>mimeTypeDetector(String)</code>.
+ The set of existing definitions can be accessed with the
<code>mimeTypeDetectors()</code> method.
+ </para>
+ </note>
+ </sect3>
+ </sect2>
+ </sect1>
+ <sect1 id="using_dna_whats_next">
+ <title>What's next</title>
+ <para>
+ This chapter outline how you configure JBoss DNA, how you then access a
<code>javax.jcr.Repository</code> instance,
+ and use the standard JCR API to interact with the repository. The
+ <link linkend="downloading_and_running">next chapter </link>
walks you through downloading
+ and running the JBoss DNA examples.
+ </para>
+ </sect1>
+</chapter>
Property changes on:
trunk/docs/reference/src/main/docbook/en-US/content/core/configuration.xml
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Copied: trunk/docs/reference/src/main/docbook/en-US/content/core/environment.xml (from rev
994, trunk/docs/reference/src/main/docbook/en-US/content/environment.xml)
===================================================================
--- trunk/docs/reference/src/main/docbook/en-US/content/core/environment.xml
(rev 0)
+++ trunk/docs/reference/src/main/docbook/en-US/content/core/environment.xml 2009-06-08
14:57:17 UTC (rev 995)
@@ -0,0 +1,310 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ JBoss DNA (
http://www.jboss.org/dna)
+ ~
+ ~ See the COPYRIGHT.txt file distributed with this work for information
+ ~ regarding copyright ownership. Some portions may be licensed
+ ~ to Red Hat, Inc. under one or more contributor license agreements.
+ ~ See the AUTHORS.txt file in the distribution for a full listing of
+ ~ individual contributors.
+ ~
+ ~ JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
+ ~ is licensed to you under the terms of the GNU Lesser General Public License as
+ ~ published by the Free Software Foundation; either version 2.1 of
+ ~ the License, or (at your option) any later version.
+ ~
+ ~ JBoss DNA is distributed in the hope that it will be useful,
+ ~ but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ ~ or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ ~ for more details.
+ ~
+ ~ You should have received a copy of the GNU Lesser General Public License
+ ~ along with this distribution; if not, write to:
+ ~ Free Software Foundation, Inc.
+ ~ 51 Franklin Street, Fifth Floor
+ ~ Boston, MA 02110-1301 USA
+ -->
+<!DOCTYPE preface PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
"http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd" [
+<!ENTITY % CustomDTD SYSTEM "../../custom.dtd">
+%CustomDTD;
+]>
+<chapter id="environment">
+ <title>Environment</title>
+ <para>
+ The various components of JBoss DNA are designed as plain old Java objects, or POJOs.
And rather than making assumptions
+ about their environment, each component instead requires that any external dependencies
necessary for it to operate
+ must be supplied to it. This pattern is known as Dependency Injection, and it allows
the components to be simpler
+ and allows for a great deal of flexibility and customization in how the components are
configured.
+ And, JBoss DNA will soon provide a higher-level component that leverages the
+ <ulink
url="http://www.jboss.org/jbossmc">JBoss
Microcontainer</ulink> to automatically assemble and wire together
+ all the lower-level components.
+ </para>
+ <sect1 id="execution-context">
+ <title>Execution contexts</title>
+ <para>
+ One of the objects that must be supplied to many JBoss DNA components is an
&ExecutionContext;. Some components
+ require this context to be passed into individual methods, allowing the context to
vary with each method invocation.
+ Other components require the context to be provided before it's used, and will use
that context for all its operations
+ (until it is given a different one).
+ </para>
+ <para>
+ What does an &ExecutionContext; represent? Quite simply, it's the set of
objects that define the environment
+ or context in which the method or component is currently operating. It includes a way
for recording and reporting
+ errors and problems. It includes the ability to <link
linkend="classloaders">create class loaders</link>
+ given a classpath of class loader names. It also includes information about the
current <link linkend="security">user</link>.
+ It includes access to factories that can be used to create and convert property
values. And it includes factories
+ for working with namespaces and fully-qualified names. In fact, as JBoss DNA evolves,
more things may need to be
+ added. Here is what the &ExecutionContext; interface looks like:
+ </para>
+ <programlisting>
+public class &ExecutionContext; implements &ClassLoaderFactory; {
+
+ /**
+ * Get the factories that should be used to create values for {@link Property
properties}.
+ * @return the property value factory; never null
+ */
+ public &ValueFactories; getValueFactories() {...}
+
+ /**
+ * Get the namespace registry for this context.
+ * @return the namespace registry; never null
+ */
+ public &NamespaceRegistry; getNamespaceRegistry() {...}
+
+ /**
+ * Get the factory for creating {@link Property} objects.
+ * @return the property factory; never null
+ */
+ public &PropertyFactory; getPropertyFactory() {...}
+
+ /**
+ * Get the security context for this environment.
+ * @return the security context; never <code>null</code>
+ */
+ public &SecurityContext; getSecurityContext() {...}
+
+ /**
+ * Return a logger associated with this context. This logger records only those
activities within the
+ * context and provide a way to capture the context-specific activities. All log
messages are also
+ * sent to the system logger, so classes that log via this mechanism should
<i>not</i> also
+ * {@link Logger#getLogger(Class) obtain a system logger}.
+ * @param clazz the class that is doing the logging
+ * @return the logger, named after <code>clazz</code>; never null
+ */
+ public &Logger; getLogger( Class<?> clazz ) {...}
+
+ /**
+ * Return a logger associated with this context. This logger records only those
activities within the
+ * context and provide a way to capture the context-specific activities. All log
messages are also
+ * sent to the system logger, so classes that log via this mechanism should
<i>not</i> also
+ * {@link Logger#getLogger(Class) obtain a system logger}.
+ * @param name the name for the logger
+ * @return the logger, named after <code>clazz</code>; never null
+ */
+ public &Logger; getLogger( String name ) {...}
+
+ ...
+}
+</programlisting>
+ <para>
+ Notice that &ExecutionContext; implements the &ClassLoaderFactory; interface
described in the
+ <link linkend="classloaders">previous chapter</link>, meaning it
can be used to create other contexts. These other methods are not shown,
+ but can be used to create create subcontexts with different <link
linkend="security">security contexts</link>,
+ with different namespace registry, or with different combinations of components.
+ </para>
+ <para>
+ The fact that so many of the JBoss DNA components take &ExecutionContext;
instances gives us some interesting possibilities.
+ For example, one execution context instance can be used as the highest-level (or
"application-level") context for all of the services
+ (e.g., &RepositoryService;, &SequencingService;, etc.).
+ Then, an execution context could be created for each user that will be performing
operations, and that user's context can
+ be passed around to not only provide security information about the user but also to
allow the activities being performed
+ to be recorded for user feedback, monitoring and/or auditing purposes.
+ </para>
+ <para>
+ The following code fragment shows how easy it is to create various execution
contexts:
+ </para>
+ <programlisting>
+&ExecutionContext; context1 = new &ExecutionContext;();
+String jaasRealm = ...;
+
+// Create a context for a user, authenticating using a JAAS LoginContext...
+char[] password = "password".toCharArray();
+&SecurityContext; securityContext = new JaasSecurityContext(jaasRealm,
"username", password);
+&ExecutionContext; context2 = context1.with(securityContext);
+
+// Create a context for the same user, authenticating using JAAS, and using a different
callback handler ...
+&CallbackHandler; callbackHandler = ...
+&ExecutionContext; context3 = context1.with(new JaasSecurityContext(jaasRealm,
callbackHandler);
+
+// Create a context that uses a provided &SecurityContext; (see the <link
linkend="security">next section</link>)...
+&SecurityContext; mySecurityContext = ...
+&ExecutionContext; context4 = context1.with(mySecurityContext);
+</programlisting>
+ <para>
+ These contexts can then be passed to the various components as needed.
+ </para>
+ </sect1>
+ <sect1 id="security">
+ <title>Security</title>
+ <para>
+ JBoss DNA uses a simple abstraction layer to isolate it from the security
infrastructure used within an application.
+ The &SecurityContext; interface is defined as follows:
+ </para>
+ <programlisting>
+public interface &SecurityContext; {
+
+ /**
+ * Get the name of the authenticated user.
+ * @return the authenticated user's name
+ */
+ &String; getUserName();
+
+ /**
+ * Determine whether the authenticated user has the given role.
+ * @param roleName the name of the role to check
+ * @return true if the user has the role and is logged in; false otherwise
+ */
+ boolean hasRole( String roleName );
+
+ /**
+ * Logs the user out of the authentication mechanism.
+ * For some authentication mechanisms, this will be implemented as a no-op.
+ */
+ void logout();
+}
+</programlisting>
+ <para>
+ As noted below, this security context is made available through the
&ExecutionContext; described above.
+ </para>
+ <sect2 id="jaas_security">
+ <title>JAAS</title>
+ <para>
+ One such implementation is the &JaasSecurityContext;, which delegates any
authentication or authorization requests to a
+ <ulink
url="http://java.sun.com/javase/technologies/security/">Java
Authentication and Authorization Service (JAAS)</ulink>
+ provider. This is the standard approach for authenticating and authorizing in
Java, and is the default mechanism
+ used by the &JcrEngine;.
+ </para>
+ <para>
+ There are quite a few JAAS providers available, but one of the best and most powerful
providers is
+ <ulink
url="http://www.jboss.org/jbosssecurity/">JBoss
Security</ulink>, the open source
+ security framework used by JBoss. JBoss Security offers a number of JAAS login
modules, including:
+ <itemizedlist>
+ <listitem>
+ <para>
+ <emphasis role="strong">User-Roles Login
Module</emphasis>
+ is a simple
+ <code>javax.security.auth.login.LoginContext</code>
+ implementation that uses usernames and passwords stored in a properties
file.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <emphasis role="strong">Client Login Module</emphasis>
+ prompts the user for their username and password.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <emphasis role="strong">Database Server Login
Module</emphasis>
+ uses a JDBC database to authenticate principals and associate them with
roles.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <emphasis role="strong">LDAP Login Module</emphasis>
+ uses an LDAP directory to authenticate principals. Two implementations are
available.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <emphasis role="strong">Certificate Login
Module</emphasis>
+ authenticates using X509 certificates, obtaining roles from either property
files or a JDBC database.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <emphasis role="strong">Operating System Login
Module</emphasis>
+ authenticates using the operating system's mechanism.
+ </para>
+ </listitem>
+ </itemizedlist>
+ and many others. Plus, JBoss Security also provides other capabilities, such as
using XACML policies or using federated single sign-on.
+ For more detail, see the <ulink
url="http://www.jboss.org/jbosssecurity/">JBoss Security</ulink>
project.
+ </para>
+ </sect2>
+ <sect2 id="web_security">
+ <title>Web application security</title>
+ <para>
+ If JBoss DNA is being used within a web application, then it is probably desirable to
reuse the security infrastructure
+ of the application server. This can be accomplished by implementing the
&SecurityContext; interface with an implementation
+ that delegates to the <interface>HttpServletRequest</interface>. Then,
for each request, create a &SecurityContextCredentials;
+ instance around your &SecurityContext;, and use that credentials to obtain a JCR
&Session;.
+ </para>
+ <para>
+ Here is an example of the &SecurityContext; implementation that uses the servlet
request:
+ </para>
+ <programlisting>
+@Immutable
+public class ServletSecurityContext implements &SecurityContext; {
+
+ private final String userName;
+ private final HttpServletRequest request;
+
+ /**
+ * Create a {@link ServletSecurityContext} with the supplied
+ * {@link HttpServletRequest servlet information}.
+ *
+ * @param request the servlet request; may not be null
+ */
+ public ServletSecurityContext( HttpServletRequest request ) {
+ this.request = request;
+ this.userName = request.getUserPrincipal() != null ?
request.getUserPrincipal().getName() : null;
+ }
+
+ /**
+ * Get the name of the authenticated user.
+ * @return the authenticated user's name
+ */
+ public &String; getUserName() {
+ return userName;
+ }
+
+ /**
+ * Determine whether the authenticated user has the given role.
+ * @param roleName the name of the role to check
+ * @return true if the user has the role and is logged in; false otherwise
+ */
+ boolean hasRole( String roleName ) {
+ request.isUserInRole(roleName);
+ }
+
+ /**
+ * Logs the user out of the authentication mechanism.
+ * For some authentication mechanisms, this will be implemented as a no-op.
+ */
+ public void logout() {
+ }
+}</programlisting>
+ <para>
+ Then use this to create a &Session;:
+ </para>
+ <programlisting>
+HttpServletRequest request = ...
+&Repository; repository = engine.getRepository("my repository");
+&SecurityContext; securityContext = new ServletSecurityContext(httpServletRequest);
+SecurityContextCredentials credentials = new
SecurityContextCredentials(securityContext);
+&Session; session = repository.login(credentials, workspaceName);
+</programlisting>
+ </sect2>
+ </sect1>
+ <sect1>
+ <title>Summary</title>
+ <para>
+ In this chapter, we covered security and environment topics as used throughout JBoss
DNA.
+ The <link linkend="repositories">next chapter</link> will cover
JBoss DNA repositories, including the connector framework,
+ how DNA's JCR implementation works with connectors, what connectors are available
(and how to use them),
+ and how to write your own connector.
+ </para>
+ </sect1>
+</chapter>
+
Property changes on:
trunk/docs/reference/src/main/docbook/en-US/content/core/environment.xml
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Copied: trunk/docs/reference/src/main/docbook/en-US/content/core/mimetypes.xml (from rev
994, trunk/docs/reference/src/main/docbook/en-US/content/mimetypes.xml)
===================================================================
--- trunk/docs/reference/src/main/docbook/en-US/content/core/mimetypes.xml
(rev 0)
+++ trunk/docs/reference/src/main/docbook/en-US/content/core/mimetypes.xml 2009-06-08
14:57:17 UTC (rev 995)
@@ -0,0 +1,204 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ JBoss DNA (
http://www.jboss.org/dna)
+ ~
+ ~ See the COPYRIGHT.txt file distributed with this work for information
+ ~ regarding copyright ownership. Some portions may be licensed
+ ~ to Red Hat, Inc. under one or more contributor license agreements.
+ ~ See the AUTHORS.txt file in the distribution for a full listing of
+ ~ individual contributors.
+ ~
+ ~ JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
+ ~ is licensed to you under the terms of the GNU Lesser General Public License as
+ ~ published by the Free Software Foundation; either version 2.1 of
+ ~ the License, or (at your option) any later version.
+ ~
+ ~ JBoss DNA is distributed in the hope that it will be useful,
+ ~ but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ ~ or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ ~ for more details.
+ ~
+ ~ You should have received a copy of the GNU Lesser General Public License
+ ~ along with this distribution; if not, write to:
+ ~ Free Software Foundation, Inc.
+ ~ 51 Franklin Street, Fifth Floor
+ ~ Boston, MA 02110-1301 USA
+ -->
+<!DOCTYPE preface PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
"http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd" [
+<!ENTITY % CustomDTD SYSTEM "../../custom.dtd">
+%CustomDTD;
+]>
+<chapter id="mimetypes">
+ <title>MIME types</title>
+ <para>
+ JBoss DNA often needs the ability to determine the MIME type for some binary content.
When uploading content into
+ a repository, we may want to add the MIME type as metadata. Or, we may want to make
some processing decisions
+ based upon the MIME type. So, JBoss DNA created a small pluggable framework for
determining the MIME type by using
+ the name of the file (e.g., extensions) and/or by reading the actual content.
Technically, the framework
+ delegates this to one or more extensions. And we've provided one extension that
does a very good job at
+ determining the MIME type from a large variety of file types. But if that isn't
sufficient, you can always
+ incorporate your own detector into JBoss DNA.
+ </para>
+ <para>
+ To use this system, you simply invoke a static method and supply the name of the
content (e.g., the name of the file, with the extension)
+ and the &InputStream; to the actual binary content:
+ </para>
+ <programlisting>&MimeType;.of(name,content)</programlisting>
+ <para>
+ The result is a &String; containing the <ulink
url="http://www.iana.org/assignments/media-types/">MIME type</ulink>
+ (e.g., "text/plain") or null if the MIME type cannot be determined. Note
that the name or &InputStream; may be
+ null, making this a very versatile utility.
+ </para>
+ <sect1 id="detectors">
+ <title>JBoss DNA MIME type detectors</title>
+ <para>
+ The principle component in this framework is the concept of a <emphasis
role="strong">detector</emphasis>.
+ A detector attempts to determine the MIME type using the name of the content (e.g.,
the file name)
+ and the actual content itself. If the detector is able to determine the MIME type, it
simply returns
+ it as a string. If not, it merely returns null. Note, however, that a detector must
be thread-safe.
+ </para>
+ <para>
+ Here is the interface:
+ </para>
+ <programlisting>
+package org.jboss.dna.graph.mimetype;
+@ThreadSafe
+public interface &MimeTypeDetector; {
+
+ /**
+ * Returns the MIME-type of a data source, using its supplied content and/or
+ * its supplied name, depending upon the implementation. If the MIME-type
+ * cannot be determined, either a "default" MIME-type or
<code>null</code> may
+ * be returned, where the former will prevent earlier registered MIME-type
+ * detectors from being consulted.
+ *
+ * @param name The name of the data source; may be <code>null</code>.
+ * @param content The content of the data source; may be
<code>null</code>.
+ * @return The MIME-type of the data source, or optionally
<code>null</code>
+ * if the MIME-type could not be determined.
+ * @throws &IOException; If an error occurs reading the supplied content.
+ */
+ &String; mimeTypeOf( &String; name,
+ &InputStream; content ) throws &IOException;;
+}</programlisting>
+ <para>
+ Detectors can be added to the &MimeType; class using the
<code>addDetector(&MimeTypeDetectorConfig; config)</code>
+ method, where the &MimeTypeDetectorConfig; defines the name of the detector class,
the name of the
+ <link linkend="classloaders">class loader</link>, a name, and a
description. It is also possible
+ to set the &ClassLoaderFactory; that the &MimeType; singleton will use.
+ </para>
+ <para>
+ We'll next look at the MIME type detectors that are provided out by JBoss DNA out
of the box, and how to write your own.
+ </para>
+ <sect2 id="dna-mimetype-detector-aperture">
+ <title>Aperture MIME type detector</title>
+ <para>
+ The &ApertureMimeTypeDetector; class is an implementation of
&MimeTypeDetector; that uses the
+ <ulink
url="http://aperture.sourceforge.net/">Aperture</ulink>
open-source library, which
+ is a very capable utility for determining the MIME type for a wide range of file
types,
+ using both the file name and the actual content.
+ </para>
+ </sect2>
+ </sect1>
+ <sect1 id="custom-detectors">
+ <title>Writing custom detectors</title>
+ <para>
+ Creating a custom detector involves the following steps:
+ <itemizedlist>
+ <listitem>
+ <para>Create a Maven 2 project for your detector;</para>
+ </listitem>
+ <listitem>
+ <para>Implement the &MimeTypeDetector; interface with your own
implementation, and create unit tests to verify
+ the functionality and expected behavior;</para>
+ </listitem>
+ <listitem>
+ <para>Add a &MimeTypeDetectorConfig; to the &MimeType; class in your
application
+ as described <link linkend="detectors">earlier</link>;
and</para>
+ </listitem>
+ <listitem>
+ <para>Deploy the JAR file with your implementation (as well as any
dependencies), and make them available to JBoss DNA
+ in your application.</para>
+ </listitem>
+ </itemizedlist>
+ It's that simple.
+ </para>
+ <sect2 id="custom_detector_project">
+ <title>Creating the Maven 2 project</title>
+ <para>The first step is to create the Maven 2 project that you can use to
compile your code and build the JARs.
+ Maven 2 automates a lot of the work, and since you're already <link
linkend="maven">set up to use Maven</link>,
+ using Maven for your project will save you a lot of time and effort. Of course, you
don't have to use Maven 2, but then you'll
+ have to get the required libraries and manage the compiling and building process
yourself.</para>
+ <note>
+ <para>JBoss DNA may provide in the future a Maven archetype for creating
detector projects. If you'd find this useful
+ and would like to help create it, please <link
linkend="preface">join the community</link>.</para>
+ </note>
+ <note>
+ <para>
+ The <emphasis
role="strong">dna-mimetype-detector-aperture</emphasis> project is a
small, self-contained detector implementation that
+ that you can use to help you get going. Starting with this project's source
and modifying it to suit your needs
+ may be the easiest way to get started.
+ See the subversion repository:
+ <ulink
url="&Subversion;trunk/extensions/dna-mimetype-detector-aperture/">&Subversion;trunk/sequencers/dna-mimetype-detector-aperture/</ulink>
+ </para>
+ </note>
+ <para>You can create your Maven project any way you'd like. For examples,
see the <ulink
url="http://maven.apache.org/guides/getting-started/index.html#How_d...
2 documentation</ulink>.
+ Once you've done that, just add the dependencies in your project's
<code>pom.xml</code> dependencies section:</para>
+ <programlisting role="XML"><![CDATA[<dependency>
+ <groupId>org.jboss.dna</groupId>
+ <artifactId>dna-common</artifactId>
+ <version>0.1</version>
+</dependency>
+<dependency>
+ <groupId>org.jboss.dna</groupId>
+ <artifactId>dna-graph</artifactId>
+ <version>0.1</version>
+</dependency>
+<dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-api</artifactId>
+</dependency>
+]]></programlisting>
+ <para>These are minimum dependencies required for compiling a detector. Of
course, you'll have to add
+ other dependencies that your sequencer needs.</para>
+ <para>As for testing, you probably will want to add more dependencies, such as
those listed here:</para>
+ <programlisting role="XML"><![CDATA[<dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <version>4.4</version>
+ <scope>test</scope>
+</dependency>
+<dependency>
+ <groupId>org.hamcrest</groupId>
+ <artifactId>hamcrest-library</artifactId>
+ <version>1.1</version>
+ <scope>test</scope>
+</dependency>
+<!-- Logging with Log4J -->
+<dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-log4j12</artifactId>
+ <version>1.4.3</version>
+ <scope>test</scope>
+</dependency>
+<dependency>
+ <groupId>log4j</groupId>
+ <artifactId>log4j</artifactId>
+ <version>1.2.14</version>
+ <scope>test</scope>
+</dependency>
+]]></programlisting>
+ <para>
+ After you've created the project, simply implement the &MimeTypeDetector;
interface. And testing should be
+ quite straightforward, MIME type detectors don't require any other components.
In your tests,
+ simply instantiate your &MimeTypeDetector; implementation, supply various
combinations of names and/or &InputStream;s,
+ and verify the output is what you expect.
+ </para>
+ <para>
+ To use in your application, create a &MimeTypeDetectorConfig; object with the
name, description, and class information
+ for your detector, and add to the &MimeType; class using the
<code>addDetector(&MimeTypeDetectorConfig; config)</code> method.
+ Then, just use the &MimeType; class.
+ </para>
+ </sect2>
+ </sect1>
+</chapter>
Property changes on:
trunk/docs/reference/src/main/docbook/en-US/content/core/mimetypes.xml
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Copied: trunk/docs/reference/src/main/docbook/en-US/content/core/repositories.xml (from
rev 994, trunk/docs/reference/src/main/docbook/en-US/content/repositories.xml)
===================================================================
--- trunk/docs/reference/src/main/docbook/en-US/content/core/repositories.xml
(rev 0)
+++ trunk/docs/reference/src/main/docbook/en-US/content/core/repositories.xml 2009-06-08
14:57:17 UTC (rev 995)
@@ -0,0 +1,1468 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ JBoss DNA (
http://www.jboss.org/dna)
+ ~
+ ~ See the COPYRIGHT.txt file distributed with this work for information
+ ~ regarding copyright ownership. Some portions may be licensed
+ ~ to Red Hat, Inc. under one or more contributor license agreements.
+ ~ See the AUTHORS.txt file in the distribution for a full listing of
+ ~ individual contributors.
+ ~
+ ~ JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
+ ~ is licensed to you under the terms of the GNU Lesser General Public License as
+ ~ published by the Free Software Foundation; either version 2.1 of
+ ~ the License, or (at your option) any later version.
+ ~
+ ~ JBoss DNA is distributed in the hope that it will be useful,
+ ~ but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ ~ or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ ~ for more details.
+ ~
+ ~ You should have received a copy of the GNU Lesser General Public License
+ ~ along with this distribution; if not, write to:
+ ~ Free Software Foundation, Inc.
+ ~ 51 Franklin Street, Fifth Floor
+ ~ Boston, MA 02110-1301 USA
+ -->
+<!DOCTYPE preface PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
"http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd" [
+<!ENTITY % CustomDTD SYSTEM "../../custom.dtd">
+%CustomDTD;
+]>
+<chapter id="repositories">
+ <title>Repositories</title>
+ <para></para>
+ <para>There is a lot of information stored in many of different places:
databases, repositories, SCM systems,
+ registries, file systems, services, etc. The purpose of the federation engine is to
allow applications to use the JCR API
+ to access that information as if it were all stored in a single JCR repository, but
to really leave the information where
+ it is.</para>
+ <para>Why not just copy or move the information into a JCR repository? Moving it
is probably pretty difficult, since most
+ likely there are existing applications that rely upon that information being where it
is. All of those applications
+ would break or have to change. And copying the information means that we'd have to
continually synchronize the changes.
+ This not only is a lot of work, but it often makes it difficult to know whether
information is accurate and "the master" data.
+ </para>
+ <para>JBoss DNA lets us leave information where it, yet access it through the JCR
API as if it were in one big repository.
+ One major benefit is that existing applications that use the information in the
original locations don't break, since they
+ can keep using the information. But now our JCR clients can also access all the
information, too. And if our federating JBoss DNA repository is
+ configured to allow updates, JCR client applications can change the information in
the repository and JBoss DNA will propagate
+ those changes down to the original source, making those changes visible to all the
other applications.
+ </para>
+ <para>
+ In short, all clients see the correct information, even when it changes in the
underlying systems. But the JCR clients can get to all of the information
+ in one spot, using one powerful standard API.
+ </para>
+ <sect1 id="connectors">
+ <title>Repository connectors</title>
+ <para>
+ With JBoss DNA, your applications use the <ulink
url="&JSR170;">JCR API</ulink> to work with the repository,
+ but the DNA repository transparently fetches the information from different kinds of
repositories and storage systems,
+ not just a single purpose-built store. This is fundamentally what makes JBoss DNA
different.
+ </para>
+ <para>How does JBoss DNA do this? At the heart of JBoss DNA and it's JCR
implementation is a simple graph-based
+ <emphasis>repository connector</emphasis> system. Essentially, JBoss
DNA's JCR implementation uses a single
+ repository connector to access all content:
+ <figure id="dnajcr-and-connector">
+ <title>JBoss DNA's JCR implementation delegates to a repository
connector</title>
+ <graphic align="center" scale="100"
fileref="dnajcr-and-connector.png"/>
+ </figure>
+ That single repository connector could use an in-memory repository, a JBoss Cache
instance (including those that are clustered and replicated),
+ or a federated repository where content from multiple sources is unified.
+ <figure id="dna-connectors-0.2">
+ <title>JBoss DNA can put JCR on top of multiple kinds of systems</title>
+ <graphic align="center" scale="100"
fileref="dna-connectors-0.2.png"/>
+ </figure>
+ Really, the federated connector gives us all kinds of possibilities, since we can use
that connector on top of lots of connectors
+ to other individual sources. This simple connector architecture is fundamentally what
makes JBoss DNA so powerful and flexible.
+ Along with a good library of connectors, which is what we're planning to create.
+ </para>
+ <para>
+ For instance, we want to build a connector to <ulink
url="&JIRA-39;">other JCR repositories</ulink>, and another that
accesses
+ the <ulink url="&JIRA-34;">local file system</ulink>.
We've already started on a <ulink url="&JIRA-36;">Subversion
connector</ulink>,
+ which will allow JCR to access the files in a SVN repository (and perhaps push changes
into SVN through a commit).
+ And of course we want to create a connector that accesses <ulink
url="&JIRA-199;">data</ulink>
+ and <ulink url="&JIRA-37;">metadata</ulink> from relational
databases. For more information, check out our
+ <ulink
url="&JIRA;?report=com.atlassian.jira.plugin.system.project:roadmap-panel">roadmap</ulink>.
+ Of course, if we don't have a connector to suit your needs, you can <link
linkend="custom-connectors">write your own</link>.
+ <figure id="dna-connectors-future">
+ <title>Future JBoss DNA connectors</title>
+ <graphic align="center" scale="100"
fileref="dna-connectors-future.png"/>
+ </figure>
+ </para>
+ <para>
+ </para>
+ <para>
+ It's even possible to put a different API layer on top of the connectors. For
example, the new <ulink url="&JSR203;">New I/O
(JSR-203)</ulink>
+ API offers the opportunity to build new file system providers. This would be very
straightforward to put on top of a JCR implementation,
+ but it could be made even simpler by putting it on top of a DNA connector. In both
cases, it'd be a trivial mapping from nodes that represent
+ files and folders into JSR-203 files and directories, and events on those nodes could
easily be translated into JSR-203 watch events.
+ Then, simply choose a DNA connector and configure it to use the source you want to
use.
+ <figure id="dna-connectors-vfs">
+ <title>Virtual File System with JBoss DNA</title>
+ <graphic align="center" scale="100"
fileref="vfs-and-connector.png"/>
+ </figure>
+ </para>
+ <para>Before we go further, let's define some terminology regarding
connectors.</para>
+ <itemizedlist>
+ <listitem>
+ <para>
+ A <emphasis role="strong">connector</emphasis> is the runnable
code packaged in one or more JAR files that
+ contains implementations of several interfaces (described below). A Java developer
<emphasis>writes</emphasis>
+ a connector to a type of source, such as a particular database management system,
LDAP directory, source code
+ management system, etc. It is then packaged into one or more JAR files (including
dependent JARs) and deployed
+ for use in applications that use JBoss DNA repositories.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ The description of a particular source system (e.g., the "Customer"
database, or the company LDAP system)
+ is called a <emphasis role="strong">repository
source</emphasis>. JBoss DNA defines a &RepositorySource; interface
+ that defines methods describing the behavior and supported features and a method for
establishing connections.
+ A connector will have a class that implements this interface and that has JavaBean
properties for
+ all of the connector-specific properties required to fully describe an instance of
the system. Use of JavaBean
+ properties is not required, but it is highly recommended, as it enables reflective
configuration and administration.
+ Applications that use JBoss DNA create an instance of the connector's
&RepositorySource; implementation and set
+ the properties for the external source that the application wants to access with
that connector.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ A repository source instance is then used to establish <emphasis
role="strong">connections</emphasis> to
+ that source. A connector provides an implementation of the
&RepositoryConnection; interface, which
+ defines methods for interacting with the external system. In particular, the
<code>execute(...)</code> method
+ takes an &ExecutionContext; instance and a &Request; object. The
&ExectuionContext; object defines the
+ environment in which the processing is occurring, including information about the
JAAS &Subject; and &LoginContext;.
+ The &Request; object describes the requested operations on the content, with
different concrete subclasses
+ representing each type of activity. Examples of commands include (but not limited
to) getting a node, moving a node, creating a node,
+ changing a node, and deleting a node. And, if the repository source is able to
participate in JTA/JTS distributed transactions, then the
+ &RepositoryConnection; must implement the
<code>getXaResource()</code> method by returning
+ a valid <code>javax.transaction.xa.XAResource</code> object that can be
used by the transaction monitor.
+ </para>
+ </listitem>
+ </itemizedlist>
+ <para>As an example, consider that we want JBoss DNA to give us access through
JCR to the schema information contained in a
+ relational databases. We first have to develop a connector that allows us to interact
with relational databases using JDBC.
+ That connector would contain a <code>JdbcRepositorySource</code> Java
class that implements &RepositorySource;,
+ and that has all of the various JavaBean properties for setting the name of the driver
class, URL, username, password,
+ and other properties. (Or we might have a JavaBean property that defines the JNDI
name where we can find a JDBC
+ <code>DataSource</code> instance pointing to our JDBC database.)
+ </para>
+ <para>
+ Our new connector would also have a <code>JdbcRepositoryConnection</code>
Java class that implements the
+ &RepositoryConnection; interface. This class would probably wrap a JDBC database
connection,
+ and would implement the <code>execute(...)</code> method such that the
nodes exposed by the connector
+ describe the database schema of the database. For example, the connector might
represent each database table
+ as a node with the table's name, with properties that describe the table (e.g.,
the description, whether it's a
+ temporary table), and with child nodes that represent each of the columns, keys and
constraints.
+ </para>
+ <para>
+ To use our connector in an application that uses JBoss DNA, we need to create an
instance of the
+ <classname>JdbcRepositorySource</classname> for each database instance
that we want to access. If we have 3 MySQL databases,
+ 9 Oracle databases, and 4 PostgreSQL databases, then we'd need to create a total
of 16 <classname>JdbcRepositorySource</classname>
+ instances, each with the properties describing a single database instance. Those
sources are then available for use by
+ JBoss DNA components, including the <link
linkend="jcr">JCR</link> implementation.
+ </para>
+ <para>
+ So, we've so far learned what a repository connector is and how they're used
to establish connections to the underlying sources
+ and access the content in those sources. In the <link
linkend="repository-service">next section</link>, we'll show how
these
+ source instances can be configured, managed, and their connections pooled. After
that, we'll look review JBoss DNA's
+ <link linkend="connector-library">existing connectors</link> and
show how to <link linkend="custom-connectors">create your own
connectors</link>.
+ </para>
+ </sect1>
+ <sect1 id="repository-workspaces">
+ <title>Workspaces</title>
+ <para>The previous section talked about how connector expose their information
through the graph language of JBoss DNA.
+ This is true, except that we didn't dive into too much of the detail. JBoss DNA
graphs have the notion of <emphasis>workspaces</emphasis>
+ in which the content appears, and its very easy for clients using the graph to switch
between workspaces. In fact,
+ workspaces differ from each other in that they provide different views of the same
information.
+ </para>
+ <para>Consider a source control system, like SVN or CVS. These systems provide
different views of the source code:
+ a mainline development branch as well as other branches (or tags) commonly used for
releases. So, just like one source
+ file might appear in the mainline branch as well as the previous two release branches,
a node in a repository source
+ might appear in multiple workspaces.
+ </para>
+ <para>
+ However, each connector can kind of decide how (or whether) it uses workspaces. For
example, there may be no overlap
+ in the content between workspaces. Or a connector might only expose a single
workspace (in other words, there's only one
+ "default" workspace).
+ </para>
+ </sect1>
+ <sect1 id="repository-service">
+ <title>Repository Service</title>
+ <para>The JBoss DNA &RepositoryService; is the component that manages the
<emphasis>repository sources</emphasis>
+ and the connections to them. &RepositorySource; instances can be
programmatically added to the service, but
+ the service can actually read its configuration from a configuration repository
(which, by the way, is represented by a
+ just another &RepositorySource; instance that's usually added programmatically
to the service). The service connects to
+ the configuration repository, reads the content in a particular area, and
automatically sets up the &RepositorySource; instances
+ per the information found in the configuration repository.
+ </para>
+ <para>
+ The &RepositoryService; also transparently maintains for each source a pool of
reusable connections. The pooling properties
+ can be controlled via the configuration repository, or adjusted programmatically.
+ </para>
+ <para>
+ Using a repository, then, involves simply asking the &RepositoryService; for a
&RepositoryConnection;
+ to the repository given the repository's name. If a source exists with that name,
the service checks out a connection from
+ the source's pool. The resulting connection is actually a wrapper around the
underlying pooled connection, so the
+ component that requested the connection can simply close it, and under the covers the
actual connection is simply returned
+ to the pool.
+ </para>
+ <para>To instantiate the &RepositoryService;, we need to first have a few
other objects:
+ <itemizedlist>
+ <listitem>
+ <para>
+ A &ExecutionContext; instance, as discussed <link
linkend="execution-context">earlier</link>.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ A &RepositoryLibrary; instance that manages the list of &RepositorySource;
instances,
+ properly injects the execution contexts into each repository source, and provides a
configurable pool of connections
+ for each source.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ A <emphasis>configuration repository</emphasis> that contains
descriptions of all of the repository sources
+ as well as any information those sources need. Because this is a regular
repository, this could be a simple
+ repository with content loaded from an XML file, or it could be a shared
+ central repository with information about all of the JBoss DNA repositories used
across your organization.
+ </para>
+ </listitem>
+ </itemizedlist>
+ With these components in place, we can then instantiate the &RepositoryService;
and start it (using its
+ &ServiceAdministrator;). During startup, the service reads the configuration
repository and loads any
+ defined &RepositorySource; instances into the repository library, using the class
loader factory
+ (available in the &ExecutionContext;) to obtain.
+ </para>
+ <para>
+ Here's sample code that shows how to set up and start the repository service. You
can see something similar
+ in the example application in the <code>startRepositories()</code> method
of the
+ <code>org.jboss.example.dna.repository.RepositoryClient</code> class.
+ </para>
+ <programlisting>
+ // Create the top-level execution context with all standard components ...
+ &ExecutionContext; context = new ExecutionContext();
+
+ // Create the library for the RepositorySource instances ...
+ &RepositoryLibrary; sources = new &RepositoryLibrary;(context);
+
+ // Load into the source manager the repository source for the configuration repository
...
+ &InMemoryRepositorySource; configSource = new &InMemoryRepositorySource;();
+ configSource.setName("Configuration");
+ sources.addSource(configSource);
+
+ // Now instantiate the Repository Service ...
+ &RepositoryService; service = new &RepositoryService;(sources,
configSource.getName(), context);
+ service.getAdministrator().start();
+ </programlisting>
+
+ <para>After startup completes, the repositories are ready to be used. The client
application obtains the list of repositories
+ and presents them to the user. When the user selects one, the client application
starts navigating that repository
+ starting at its root node (e.g., the "/" path). As you type a command to
list the contents of the current node or to
+ "change directories" to a different node, the client application obtains the
information for the node using a simple
+ procedure:
+ <orderedlist>
+ <listitem>
+ <para>Get a connection to the repository.</para>
+ </listitem>
+ <listitem>
+ <para>Using the connection, find the current node and read its properties and
children, putting the information
+ into a simple Java plain old Java object (POJO).</para>
+ </listitem>
+ <listitem>
+ <para>Close the connection to the repository (in a finally block to ensure it
always happens).</para>
+ </listitem>
+ </orderedlist>
+ </para>
+ </sect1>
+ <sect1 id="connector-library">
+ <title>Out-of-the-box repository connectors</title>
+ <para>
+ A number of repository connectors are already available in JBoss DNA, and are
outlined in the following sections.
+ Note that we do want to build <ulink
url="https://jira.jboss.org/jira/secure/IssueNavigator.jspa?reset=tr...
connectors</ulink>
+ in the upcoming releases.
+ </para>
+ <sect2 id="dna-connector-inmemory">
+ <title>In-memory connector</title>
+ <para>
+ The in-memory repository connector is a simple connector that creates a transient,
in-memory repository.
+ This repository is used as a very simple in-memory cache or as a standalone transient
repository.
+ </para>
+ <para>
+ The &InMemoryRepositorySource; class provides a number of JavaBean properties
that control its behavior:
+ </para>
+ <table frame='all'>
+ <title>&InMemoryRepositorySource; properties</title>
+ <tgroup cols='2' align='left' colsep='1'
rowsep='1'>
+ <colspec colname='c1' colwidth="1*"/>
+ <colspec colname='c2' colwidth="1*"/>
+ <thead>
+ <row>
+ <entry>Property</entry>
+ <entry>Description</entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry>name</entry>
+ <entry>The name of the repository source, which is used by the
&RepositoryService; when obtaining a &RepositoryConnection; by
name.</entry>
+ </row>
+ <row>
+ <entry>jndiName</entry>
+ <entry>Optional property that, if used, specifies the name in JNDI where an
&InMemoryRepository; instance can be found.
+ This is an advanced property that is infrequently used.</entry>
+ </row>
+ <row>
+ <entry>rootNodeUuid</entry>
+ <entry>Optional property that, if used, defines the UUID of the root node in
the in-memory repository. If not used,
+ then a new UUID is generated.</entry>
+ </row>
+ <row>
+ <entry>retryLimit</entry>
+ <entry>Optional property that, if used, defines the number of times that any
single operation on a &RepositoryConnection; to this source should be retried
+ following a communication failure. The default value is
'0'.</entry>
+ </row>
+ <row>
+ <entry>defaultCachePolicy</entry>
+ <entry>Optional property that, if used, defines the default for how long
this information provided by this source may to be
+ cached by other, higher-level components. The default value of null implies that
this source does not define a specific
+ duration for caching information provided by this repository
source.</entry>
+ </row>
+ <row>
+ <entry>defaultWorkspaceName</entry>
+ <entry>Optional property that is initialized to an empty string and which
defines the name for the workspace that will be used by default
+ if none is specified.</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+ </sect2>
+ <sect2 id="dna-connector-jbosscache">
+ <title>JBoss Cache connector</title>
+ <para>
+ The JBoss Cache repository connector allows a <ulink
url="http://www.jboss.org/jbosscache/">JBoss Cache</ulink> instance to
be
+ used as a JBoss DNA (and thus JCR) repository. This provides a repository that is an
effective, scalable, and distributed cache,
+ and is often paired with other repository sources to provide a local or <link
linkend="dna-connector-federation">federated</link>
+ repository.
+ </para>
+ <para>
+ The &JBossCacheSource; class provides a number of JavaBean properties that
control its behavior:
+ </para>
+ <table frame='all'>
+ <title>&JBossCacheSource; properties</title>
+ <tgroup cols='2' align='left' colsep='1'
rowsep='1'>
+ <colspec colname='c1' colwidth="1*"/>
+ <colspec colname='c2' colwidth="1*"/>
+ <thead>
+ <row>
+ <entry>Property</entry>
+ <entry>Description</entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry>name</entry>
+ <entry>The name of the repository source, which is used by the
&RepositoryService; when obtaining a &RepositoryConnection; by
name.</entry>
+ </row>
+ <row>
+ <entry>cacheFactoryJndiName</entry>
+ <entry>Optional property that, if used, specifies the name in JNDI where an
existing JBoss Cache Factory instance can be found.
+ That factory would then be used if needed to create a JBoss Cache instance. If
no value is provided, then the
+ JBoss Cache <code>DefaultCacheFactory</code> class is
used.</entry>
+ </row>
+ <row>
+ <entry>cacheConfigurationName</entry>
+ <entry>Optional property that, if used, specifies the name of the
configuration that is supplied to the cache factory
+ when creating a new JBoss Cache instance.</entry>
+ </row>
+ <row>
+ <entry>cacheJndiName</entry>
+ <entry>Optional property that, if used, specifies the name in JNDI where an
existing JBoss Cache instance can be found.
+ This should be used if your application already has a cache that is used, or if
you need to configure the cache in
+ a special way.</entry>
+ </row>
+ <row>
+ <entry>uuidPropertyName</entry>
+ <entry>Optional property that, if used, defines the property that should be
used to find the UUID value for each node
+ in the cache. "<code>dna:uuid</code>" is the
default.</entry>
+ </row>
+ <row>
+ <entry>retryLimit</entry>
+ <entry>Optional property that, if used, defines the number of times that any
single operation on a &RepositoryConnection; to this source should be retried
+ following a communication failure. The default value is
'0'.</entry>
+ </row>
+ <row>
+ <entry>defaultCachePolicy</entry>
+ <entry>Optional property that, if used, defines the default for how long
this information provided by this source may to be
+ cached by other, higher-level components. The default value of null implies that
this source does not define a specific
+ duration for caching information provided by this repository
source.</entry>
+ </row>
+ <row>
+ <entry>nameOfDefaultWorkspace</entry>
+ <entry>Optional property that is initialized to an empty string and which
defines the name for the workspace that will be used by default
+ if none is specified.</entry>
+ </row>
+ <row>
+ <entry>predefinedWorkspaceNames</entry>
+ <entry>Optional property that defines the names of the workspaces that exist
and that are available for use without having to create them.</entry>
+ </row>
+ <row>
+ <entry>creatingWorkspacesAllowed</entry>
+ <entry>Optional property that is by default 'true' that defines
whether clients can create new workspaces.</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+ </sect2>
+ <sect2 id="dna-connector-federation">
+ <title>Federating connector</title>
+ <para>
+ The federated repository source provides a unified repository consisting of
information that is dynamically federated from multiple other
+ &RepositorySource; instances. This is a very powerful repository source that
appears to be a single repository, when in
+ fact the content is stored and managed in multiple other systems. Each
&FederatedRepositorySource; is typically configured
+ with the name of another &RepositorySource; that should be used as the local,
unified cache of the federated content.
+ The configuration also contains the names of the other &RepositorySource;
instances that are to be federated along with
+ the &Projection; definition describing where in the unified repository the
content is to appear.
+ </para>
+ <figure id="dna-connector-federation-image">
+ <title>Federating multiple sources using the Federated Repository
Connector</title>
+ <graphic align="center" scale="100"
fileref="dna-connector-federation.png"/>
+ </figure>
+ <para> The federation connector works by effectively building up a single
graph by querying each source and merging or
+ unifying the responses. This information is cached, which improves performance,
reduces the number of (potentially
+ expensive) remote calls, reduces the load on the sources, and helps mitigate
problems with source availability. As
+ clients interact with the repository, this cache is consulted first. When the
requested portion of the graph (or
+ "subgraph") is contained completely in the cache, it is retuned
immediately. However, if any part of the requested
+ subgraph is not in the cache, each source is consulted for their contributions to
that subgraph, and any results are
+ cached.</para>
+ <para> This basic flow makes it possible for the federated repository to
build up a local cache of the integrated graph
+ (or at least the portions that are used by clients). In fact, the federated
repository caches information in a manner
+ that is similar to that of the Domain Name System (DNS). As sources are consulted
for their contributions, the source
+ also specifies whether it is the authoritative source for this information (some
sources that are themselves federated
+ may not be the information's authority), whether the information may be
modified, the time-to-live (TTL) value (the time
+ after which the cached information should be refreshed), and the expiration time
(the time after which the cached
+ information is no longer valid). In effect, the source has complete control over
how the information it contributes is
+ cached and used.</para>
+ <para>
+ The federated repository also needs to incorporate <emphasis>negative
caching</emphasis>, which is storage of the knowledge
+ that something does <emphasis>not</emphasis> exist. Sources can be
configured to contribute information
+ only below certain paths (e.g., <code>/A/B/C</code>), and the
federation engine can take advantage of this by never
+ consulting that source for contributions to information on other paths. However,
below that path, any negative responses
+ must also be cached (with appropriate TTL and expiry parameters) to prevent the
exclusion of that source (in case the source
+ has information to contribute at a later time) or the frequent checking with the
source.
+ </para>
+ <para>
+ The federated repository uses other &RepositorySource;s that are to be federated
and a &RepositorySource; that is to be used as the
+ cache of the unified contents. These are configured in another
&RepositorySource; that is treated as a configuration repository.
+ The &FederatedRepositorySource; class uses JavaBean properties to define the name
of the configuration repository and
+ the path to the "<code>dna:federation</code>" node in that
configuration repository containing the information about the
+ cache and federated sources. This graph structure that is expected at this location
is as follows:
+ </para>
+ <programlisting><![CDATA[<!-- Define the federation configuration. -->
+<dna:federatedRepository
xmlns:dna="http://www.jboss.org/dna"
+
xmlns:jcr="http://www.jcp.org/jcr/1.0"
+ dna:timeToCache="100000" >
+ <dna:workspaces>
+ <dna:workspace jcr:name="default">
+ <!-- Define how the content in the 'Cache' source is to map to the
federated cache -->
+ <dna:cache dna:sourceName="Cache"
dna:workspaceName="default" dna:projectionRules="/a => /" />
+
+ <!-- Define how the content in the two sources maps to the federated/unified
repository.
+ This example puts the 'Cars' and 'Aircraft' content underneath
'/vehicles', but the
+ 'Configuration' content (which is defined by this file) will appear
under '/'. -->
+ <dna:projections>
+ <dna:projection jcr:name="Cars" dna:projectionRules="/Vehicles
=> /" />
+ <dna:projection jcr:name="Aircraft"
dna:projectionRules="/Vehicles => /" />
+ <dna:projection jcr:name="Configuration" dna:projectionRules="/
=> /" />
+ </dna:projections>
+ </dna:workspace>
+ </dna:workspaces>
+</dna:federatedRepository>
+]]></programlisting>
+ <note>
+ <para>
+ We're using XML to represent a graph structure, since the two map pretty well.
Each XML element represents
+ a node and XML attributes represent properties on a node. The name of the node is
defined by either the
+ <code>jcr:name</code> attribute (if it exists) or the name of the XML
element. And we use XML namespaces
+ to define the namespaces used in the node and property names. BTW, this is exactly
how the XML graph importer
+ works.
+ </para>
+ </note>
+ <para>
+ Notice that there is a cache projection and three source projections, and each
projection defines
+ one or more <emphasis>projection rules</emphasis> that are of the form:
+ </para>
+ <programlisting>pathInFederatedRepository =>
pathInSourceRepository</programlisting>
+ <para>
+ So, a projection rule <code>/Vehicles => /</code> projects the
entire contents of the source so that
+ it appears in the federated repository under the
"<code>/Vehicles</code>" node.
+ </para>
+ <para>
+ The &FederatedRepositorySource; class provides a number of JavaBean properties
that control its behavior:
+ </para>
+ <table frame='all'>
+ <title>&FederatedRepositorySource; properties</title>
+ <tgroup cols='2' align='left' colsep='1'
rowsep='1'>
+ <colspec colname='c1' colwidth="1*"/>
+ <colspec colname='c2' colwidth="1*"/>
+ <thead>
+ <row>
+ <entry>Property</entry>
+ <entry>Description</entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry>name</entry>
+ <entry>The name of the repository source, which is used by the
&RepositoryService; when obtaining a &RepositoryConnection; by
name.</entry>
+ </row>
+ <row>
+ <entry>repositoryName</entry>
+ <entry>The name for the federated repository.</entry>
+ </row>
+ <row>
+ <entry>configurationSourceName</entry>
+ <entry>The name of the &RepositorySource; that should be used as the
configuration repository, and in which is defined
+ how this federated repository is to be set up and configured.
+ This name is supplied to the &RepositoryConnectionFactory; that is provided
to this instance when added to the
+ &RepositoryLibrary;.</entry>
+ </row>
+ <row>
+ <entry>configurationWorkspaceName</entry>
+ <entry>The name of the workspace in the configuration &RepositorySource;
with the content defining
+ how this federated repository is to be set up and configured.</entry>
+ </row>
+ <row>
+ <entry>configurationSourcePath</entry>
+ <entry>The path to the node in the configuration repository below which a
"dna:federation" node exists with the
+ graph structure describing how this federated repository is to be
configured.</entry>
+ </row>
+ <row>
+ <entry>securityDomain</entry>
+ <entry>Optional property that, if used, specifies the name of the JAAS
application context that should be used
+ to establish the <link linkend="execution-contenxt">execution
context</link> for this repository.
+ This should correspond to the JAAS login configuration located within the JAAS
login configuration file,
+ and should be used only if a "<code>username</code>"
property is defined.</entry>
+ </row>
+ <row>
+ <entry>username</entry>
+ <entry>Optional property that, if used, defines the name of the JAAS subject
that should be used
+ to establish the <link linkend="execution-contenxt">execution
context</link> for this repository.
+ This should be used if a "<code>securityDomain</code>"
property is defined.</entry>
+ </row>
+ <row>
+ <entry>password</entry>
+ <entry>Optional property that, if used, defines the password of the JAAS
subject that should be used
+ to establish the <link linkend="execution-contenxt">execution
context</link> for this repository.
+ If the password is not provided but values for the
"<code>securityDomain</code>" and
"<code>username</code>" properties are,
+ then authentication will use the default JAAS callback handlers.</entry>
+ </row>
+ <row>
+ <entry>retryLimit</entry>
+ <entry>Optional property that, if used, defines the number of times that any
single operation on a &RepositoryConnection; to this source should be retried
+ following a communication failure. The default value is
'0'.</entry>
+ </row>
+ <row>
+ <entry>defaultCachePolicy</entry>
+ <entry>Optional property that, if used, defines the default for how long
this information provided by this source may to be
+ cached by other, higher-level components. The default value of null implies that
this source does not define a specific
+ duration for caching information provided by this repository
source.</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+ </sect2>
+ </sect1>
+ <sect1 id="custom-connectors">
+ <title>Writing custom connectors</title>
+ <para>
+ There may come a time when you want to tackle creating your own repository connector.
Maybe the connectors we provide out-of-the-box
+ don't work with your source. Maybe you want to use a different cache system.
+ Maybe you have a system that you want to make available through a JBoss DNA
repository. Or, maybe you're
+ a contributor and want to help us round out our library with a new connector. No
matter what the reason, creating a new connector
+ is pretty straightforward, as we'll see in this section.
+ </para>
+ <para>
+ Creating a custom connector involves the following steps:
+ <orderedlist>
+ <listitem>
+ <para>Create a Maven 2 project for your connector;</para>
+ </listitem>
+ <listitem>
+ <para>
+ Implement the &RepositorySource; interface, using JavaBean properties for each
bit of information the implementation will
+ need to establish a connection to the source system.
+ </para>
+ <para>
+ Then, implement the &RepositoryConnection; interface with a class that
represents a connection to the source. The
+ <code>execute(&ExecutionContext;, &Request;)</code> method
should process any and all requests that may come down the pike,
+ and the results of each request can be put directly on that request.
+ </para>
+ <para>
+ Don't forget unit tests that verify that the connector is doing what it's
expected to do. (If you'll be committing the connector
+ code to the JBoss DNA project, please ensure that the unit tests can be run by
others that may not have access to the
+ source system. In this case, consider writing integration tests that can be easily
configured to use different sources
+ in different environments, and try to make the failure messages clear when the
tests can't connect to the underlying source.)
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Configure JBoss DNA to use your connector. This may involve just registering the
source with the &RepositoryService;,
+ or it may involve adding a source to a configuration repository used by the
<link linkend="dna-connector-federation">federated
repository</link>.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Deploy the JAR file with your connector (as well as any dependencies), and make
them available to JBoss DNA
+ in your application.
+ </para>
+ </listitem>
+ </orderedlist>
+ Let's go through each one of these steps in more detail.
+ </para>
+ <sect2 id="custom_connector_project">
+ <title>Creating the Maven 2 project</title>
+ <para>
+ The first step is to create the Maven 2 project that you can use to compile your code
and build the JARs.
+ Maven 2 automates a lot of the work, and since you're already <link
linkend="maven">set up to use Maven</link>,
+ using Maven for your project will save you a lot of time and effort. Of course,
you don't have to use Maven 2, but then you'll
+ have to get the required libraries and manage the compiling and building process
yourself.</para>
+ <note>
+ <para>JBoss DNA may provide in the future a Maven archetype for creating
connector projects. If you'd find this useful
+ and would like to help create it, please <link
linkend="preface">join the community</link>.
+ </para>
+ <para>In lieu of a Maven archetype, you may find it easier to start with a
small existing connector project.
+ The <emphasis
role="strong">dna-connector-filesystem</emphasis> project is small, but
it may be tough to separate
+ the stuff that every connector needs from the extra code and data structures that
manage the content.
+ See the subversion repository: <ulink
url="&Subversion;trunk/extensions/dna-connector-filesystem/">&Subversion;trunk/extensions/dna-connector-filesystem/</ulink>
+ </para>
+ </note>
+ <para>
+ You can create your Maven project any way you'd like. For examples, see the
+ <ulink
url="http://maven.apache.org/guides/getting-started/index.html#How_d...
2 documentation</ulink>.
+ Once you've done that, just add the dependencies in your project's
<code>pom.xml</code> dependencies section:
+ </para>
+ <programlisting role="XML"><![CDATA[
+<dependency>
+ <groupId>org.jboss.dna</groupId>
+ <artifactId>dna-graph</artifactId>
+ <version>0.4</version>
+</dependency>
+ ]]></programlisting>
+ <para>
+ This is the only dependency required for compiling a connector - Maven pulls in all
of the dependencies needed by
+ the 'dna-graph' artifact. Of course, you'll still have to add
dependencies for any library your connector needs
+ to talk to its underlying system.
+ </para>
+ <para>
+ As for testing, you probably will want to add more dependencies, such as those listed
here:
+ </para>
+ <programlisting role="XML"><![CDATA[
+<dependency>
+ <groupId>org.jboss.dna</groupId>
+ <artifactId>dna-graph</artifactId>
+ <version>0.4</version>
+ <type>test-jar</type>
+ <scope>test</scope>
+</dependency>
+<dependency>
+ <groupId>org.jboss.dna</groupId>
+ <artifactId>dna-common</artifactId>
+ <version>0.4</version>
+ <type>test-jar</type>
+ <scope>test</scope>
+</dependency>
+<dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <version>4.4</version>
+ <scope>test</scope>
+</dependency>
+<dependency>
+ <groupId>org.hamcrest</groupId>
+ <artifactId>hamcrest-library</artifactId>
+ <version>1.1</version>
+ <scope>test</scope>
+</dependency>
+<!-- Logging with Log4J -->
+<dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-log4j12</artifactId>
+ <version>1.4.3</version>
+ <scope>test</scope>
+</dependency>
+<dependency>
+ <groupId>log4j</groupId>
+ <artifactId>log4j</artifactId>
+ <version>1.2.14</version>
+ <scope>test</scope>
+</dependency>
+ ]]></programlisting>
+ <para>
+ Testing JBoss DNA connectors does not require a JCR repository or the JBoss DNA
services. (For more detail,
+ see the <link linkend="testing_custom_connectors">testing
section</link>.) However, if you want to do
+ integration testing with a JCR repository and the JBoss DNA services, you'll
need additional dependencies
+ (e.g., <code>dna-repository</code> and any other extensions).
+ </para>
+ <para>
+ At this point, your project should be set up correctly, and you're ready to move
on to
+ <link linkend="implementing_repository_source">writing the Java
implementation</link> for your connector.
+ </para>
+ </sect2>
+ <sect2 id="implementing_repository_source">
+ <title>Implementing a <code>RepositorySource</code></title>
+ <para>
+ As mentioned earlier, a <emphasis>connector</emphasis> consists of the
Java code that is used to access content
+ from a system. Perhaps the most important class that makes up a connector is the
implementation of the
+ &RepositorySource;. This class is analogous to JDBC's DataSource in that it
is instantiated to represent
+ a single instance of a system that will be accessed, and it contains enough
information (in the form of JavaBean properties)
+ so that it can create connections to the source.
+ </para>
+ <para>
+ Why is the &RepositorySource; implementation a JavaBean? Well, this is the class
that is instantiated, usually
+ reflectively, and so a no-arg constructor is required. Using JavaBean properties
makes it possible
+ to reflect upon the object's class to determine the properties that can be set
(using setters) and read
+ (using getters). This means that an administrative application can instantiate,
configure, and manage
+ the objects that represent the actual sources, without having to know anything about
the actual implementation.
+ </para>
+ <para>
+ So, your connector will need a public class that implements &RepositorySource;
and provides JavaBean properties
+ for any kind of inputs or options required to establish a connection to and interact
with the underlying source.
+ Most of the semantics of the class are defined by the &RepositorySource; and
inherited interface.
+ However, there are a few characteristics that are worth mentioning here.
+ </para>
+ <sect3 id="connector_cache_policy">
+ <title>Cache policy</title>
+ <para>
+ Each connector is responsible for determining whether and how long DNA is to cache
the
+ content made available by the connector. This is referred to as the
<emphasis>caching policy</emphasis>,
+ and consists of a <emphasis>time to live</emphasis> value representing
the number of milliseconds that
+ a piece of data may be cached. After the TTL has passed, the information is no
longer used.
+ </para>
+ <para>
+ DNA allows a connector to use a flexible and powerful caching policy. First, each
connection returns the
+ <emphasis>default</emphasis> caching policy for all information returned
by that connection.
+ Often this policy can be configured via properties on the &RepositorySource;
implementation.
+ This is optional, meaning the connector can return <code>null</code> if
it does not wish to
+ have a default caching policy.
+ </para>
+ <para>
+ Second, the connector is able to override its default caching policy on individual
requests
+ (which we'll cover in the <link
linkend="implementing_repository_connection">next section</link>).
+ Again, this is optional, meaning that a null caching policy on a request implies
that the
+ request has no overridden caching policy.
+ </para>
+ <para>
+ Third, if the connector has no default caching policy and none is set on the
individual requests,
+ DNA uses whatever caching policy is set up for that component using the connector.
For example, the federating
+ connector allows a default caching policy to be specified, and this policy is used
should the sources
+ being federated not define their own caching policy.
+ </para>
+ <para>
+ In summary, a connector has total control over whether and for how long the
information it provides
+ is cached.
+ </para>
+ </sect3>
+ <sect3 id="repository_source_jndi">
+ <title>Leveraging JNDI</title>
+ <para>
+ Sometimes it is necessary (or easier) for a &RepositorySource; implementation to
look up an object in JNDI.
+ One example of this is the JBoss Cache connector: while the connector can
+ instantiate a new JBoss Cache instance, more interesting use cases involve JBoss
Cache instances that are
+ set up for clustering and replication, something that is generally difficult to
configure in a single JavaBean.
+ Therefore the &JBossCacheSource; has optional JavaBean properties that define
how it is to look up a
+ JBoss Cache instance in JNDI.
+ </para>
+ <para>
+ This is a simple pattern that you may find useful in your connector. Basically, if
your source implementation
+ can look up an object in JNDI, simply use a single JavaBean String property that
defines the
+ full name that should be used to locate that object in JNDI. Usually it's best
to include "Jndi" in the
+ JavaBean property name so that administrative users understand the purpose of the
property.
+ (And some may suggest that any optional property also use the word
"optional" in the property name.)
+ </para>
+ </sect3>
+ <sect3 id="repository_source_capabilities">
+ <title>Capabilities</title>
+ <para>
+ Another characteristic of a &RepositorySource; implementation is that it
provides some hint as to whether
+ it supports several features. This is defined on the interface as a method that
returns a
+ &RepositorySourceCapabilities; object. This class currently provides methods
that say whether the connector supports
+ updates, whether it supports same-name-siblings (SNS), and whether the connector
supports listeners and events.
+ </para>
+ <para>
+ Note that these may be hard-coded values, or the connector's response may be
determined at runtime by various factors.
+ For example, a connector may interrogate the underlying system to decide whether it
can support updates.
+ </para>
+ <para>
+ The &RepositorySourceCapabilities; can be used as is (the class is immutable),
or it can be subclassed
+ to provide more complex behavior. It is important, however, that the capabilities
remain constant
+ throughout the lifetime of the &RepositorySource; instance.
+ </para>
+ <note>
+ <para>
+ Why a concrete class and not an interface? By using a concrete class, connectors
inherit the default
+ behavior. If additional capabilities need to be added to the class in future
releases, connectors may
+ not have to override the defaults. This provides some insulation against future
enhancements to the connector framework.
+ </para>
+ </note>
+ </sect3>
+ <sect3 id="repository_source_security">
+ <title>Security and authentication</title>
+ <para>
+ As we'll see in the next section, the main method connectors have to process
requests takes an &ExecutionContext;,
+ which contains the JAAS security information of the subject performing the request.
This means that the connector
+ can use this to determine authentication and authorization information for each
request.
+ </para>
+ <para>
+ Sometimes that is not sufficient. For example, it may be that the connector needs
its own authorization information
+ so that it can establish a connection (even if user-level privileges still use the
&ExecutionContext; provided with
+ each request). In this case, the &RepositorySource; implementation will
probably need JavaBean properties
+ that represent the connector's authentication information. This may take the
form of a username and password,
+ or it may be properties that are used to delegate authentication to JAAS.
+ Either way, just realize that it's perfectly acceptable for the connector to
require its own security properties.
+ </para>
+ </sect3>
+ </sect2>
+ <sect2 id="implementing_repository_connection">
+ <title>Implementing a
<code>RepositoryConnection</code></title>
+ <para>
+ One job of the &RepositorySource; implementation is to create connections to the
underlying sources.
+ Connections are represented by classes that implement the &RepositoryConnection;
interface, and creating this
+ class is the next step in writing a repository connector. This is what we'll
cover in this section.
+ </para>
+ <para>
+ The &RepositoryConnection; interface is pretty straightforward:
+ </para>
+ <programlisting>
+/**
+ * A connection to a repository source.
+ * <p>
+ * These connections need not support concurrent operations by multiple threads.
+ * </p>
+ */
+@NotThreadSafe
+public interface &RepositoryConnection; {
+
+ /**
+ * Get the name for this repository source. This value should be the same as that
returned
+ * by the same &RepositorySource; that created this connection.
+ *
+ * @return the identifier; never null or empty
+ */
+ String getSourceName();
+
+ /**
+ * Return the transactional resource associated with this connection. The transaction
manager
+ * will use this resource to manage the participation of this connection in a
distributed transaction.
+ *
+ * @return the XA resource, or null if this connection is not aware of distributed
transactions
+ */
+ XAResource getXAResource();
+
+ /**
+ * Ping the underlying system to determine if the connection is still valid and
alive.
+ *
+ * @param time the length of time to wait before timing out
+ * @param unit the time unit to use; may not be null
+ * @return true if this connection is still valid and can still be used, or false
otherwise
+ * @throws InterruptedException if the thread has been interrupted during the
operation
+ */
+ boolean ping( long time, &TimeUnit; unit ) throws InterruptedException;
+
+ /**
+ * Set the listener that is to receive notifications to changes to content within
this source.
+ *
+ * @param listener the new listener, or null if no component is interested in the
change notifications
+ */
+ void setListener( &RepositorySourceListener; listener );
+
+ /**
+ * Get the default cache policy for this repository. If none is provided, a global
cache policy
+ * will be used.
+ *
+ * @return the default cache policy
+ */
+ &CachePolicy; getDefaultCachePolicy();
+
+ /**
+ * Execute the supplied commands against this repository source.
+ *
+ * @param context the environment in which the commands are being executed; never
null
+ * @param request the request to be executed; never null
+ * @throws RepositorySourceException if there is a problem loading the node data
+ */
+ void execute( &ExecutionContext; context,
+ &Request; request ) throws &RepositorySourceException;;
+
+ /**
+ * Close this connection to signal that it is no longer needed and that any
accumulated
+ * resources are to be released.
+ */
+ void close();
+}</programlisting>
+ <para>
+ While most of these methods are straightforward, a few warrant additional
information.
+ The <code>ping(...)</code> allows DNA to check the connection to see if
it is
+ alive. This method can be used in a variety of situations, ranging from verifying
that a &RepositorySource;'s
+ JavaBean properties are correct to ensuring that a connection is still alive before
returning the connection from
+ a connection pool.
+ </para>
+ <para>
+ DNA hasn't yet defined the event mechanism, so connectors don't have any
methods to invoke on the &RepositorySourceListener;.
+ This will be defined in the next release, so feel free to manage the listeners now.
Note that by default the &RepositorySourceCapabilities; returns
+ <code>false</code> for <code>supportsEvents()</code>.
+ </para>
+ <para>
+ The most important method on this interface, though, is the
<code>execute(...)</code> method, which serves as the
+ mechanism by which the component using the connector access and manipulates the
content exposed by the connector.
+ The first parameter to this method is the &ExecutionContext;, which contains the
information about environment
+ as well as the subject performing the request. This was discussed <link
linkend="execution-context">earlier</link>.
+ </para>
+ <para>
+ The second parameter, however, represents a request that is to be processed by the
connector. Request objects can
+ take many different forms, as there are different classes for each kind of request
(see the table below).
+ Each request contains the information a connector needs to do the processing, and it
also is the place
+ where the connector places the results (or the error, if one occurs).
+ </para>
+ <para>
+ How do the requests reference a node (or nodes)? Since requests are coming from a
client, the client
+ may identify a particular node using a &Location; object that is created with:
+ <itemizedlist>
+ <listitem>
+ <para>the &Path; to the node; or</para>
+ </listitem>
+ <listitem>
+ <para>one or more <emphasis>identification
properties</emphasis> that are likely source=specific
+ and that are represented with &Property; objects; or</para>
+ </listitem>
+ <listitem>
+ <para>a combination of both.</para>
+ </listitem>
+ </itemizedlist>
+ So, when a client knows the path or the identification properties, they can create a
&Location;. However,
+ all of the requests return &Location; objects, so often times the client simply
uses the location
+ from a previous request. Since &Location; is an immutable class, it is perfectly
safe to reuse them.
+ </para>
+ <para>
+ One more thing about locations: while the request may have an incomplete location
(e.g., a path but no
+ identification properties), the connector is expected to set on the request the
<emphasis>actual</emphasis>
+ location that contains the path and all identification properties. So as long as the
client
+ reuses the actual locations in subsequent requests, the connectors will have the
benefit of having
+ both the path and identification properties. Connectors can then be written to
leverage this
+ information, although the connector should still perform as expected when requests
have incomplete locations.
+ </para>
+ <table frame='all'>
+ <title>Types of Node Operation Requests</title>
+ <tgroup cols='2' align='left' colsep='1'
rowsep='1'>
+ <colspec colname='c1' colwidth="1*"/>
+ <colspec colname='c2' colwidth="1*"/>
+ <thead>
+ <row>
+ <entry>Name</entry>
+ <entry>Description</entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry>ReadNodeRequest</entry>
+ <entry>
+ A request to read from the named workspace in the source a node's properties
and children.
+ The node may be specified by path and/or by identification properties.
+ The connector returns all properties and the locations for all children,
+ or sets a &PathNotFoundException; error on the request if the node did not
exist in the workspace.
+ If the node is found, the connector sets on the request the actual location of
the node (including the path and identification properties).
+ The connector sets a &InvalidWorkspaceException; error on the request if the
named workspace does not exist.
+ </entry>
+ </row>
+ <row>
+ <entry>VerifyNodeExistsRequest</entry>
+ <entry>
+ A request to verify the existance of a node at the specified location in the
named workspace of the source.
+ The connector returns all the actual location for the node if it exists, or
+ sets a &PathNotFoundException; error on the request if the node does not
exist in the workspace.
+ The connector sets a &InvalidWorkspaceException; error on the request if the
named workspace does not exist.
+ </entry>
+ </row>
+ <row>
+ <entry>ReadAllPropertiesRequest</entry>
+ <entry>
+ A request to read from the named workspace in the source all of the properties of
a node.
+ The node may be specified by path and/or by identification properties.
+ The connector returns all properties that were found on the node,
+ or sets a &PathNotFoundException; error on the request if the node did not
exist in the workspace.
+ If the node is found, the connector sets on the request the actual location of
the node (including the path and identification properties).
+ The connector sets a &InvalidWorkspaceException; error on the request if the
named workspace does not exist.
+ </entry>
+ </row>
+ <row>
+ <entry>ReadPropertyRequest</entry>
+ <entry>
+ A request to read from the named workspace in the source a single property of a
node.
+ The node may be specified by path and/or by identification properties,
+ and the property is specified by name.
+ The connector returns the property if found on the node,
+ or sets a &PathNotFoundException; error on the request if the node or
property did not exist in the workspace.
+ If the node is found, the connector sets on the request the actual location of
the node (including the path and identification properties).
+ The connector sets a &InvalidWorkspaceException; error on the request if the
named workspace does not exist.
+ </entry>
+ </row>
+ <row>
+ <entry>ReadAllChildrenRequest</entry>
+ <entry>
+ A request to read from the named workspace in the source all of the children of a
node.
+ The node may be specified by path and/or by identification properties.
+ The connector returns an ordered list of locations for each child found on the
node,
+ an empty list if the node had no children,
+ or sets a &PathNotFoundException; error on the request if the node did not
exist in the workspace.
+ If the node is found, the connector sets on the request the actual location of
the parent node (including the path and identification properties).
+ The connector sets a &InvalidWorkspaceException; error on the request if the
named workspace does not exist.
+ </entry>
+ </row>
+ <row>
+ <entry>ReadBlockOfChildrenRequest</entry>
+ <entry>
+ A request to read from the named workspace in the source a block of children of a
node, starting with the n<superscript>th</superscript> children.
+ This is designed to allow paging through the children, which is much more
efficient for large numbers of children.
+ The node may be specified by path and/or by identification properties, and the
block
+ is defined by a starting index and a count (i.e., the block size).
+ The connector returns an ordered list of locations for each of the node's
children found in the block,
+ or an empty list if there are no children in that range.
+ The connector also sets on the request the actual location of the parent node
(including the path and identification properties)
+ or sets a &PathNotFoundException; error on the request if the parent node did
not exist in the workspace.
+ The connector sets a &InvalidWorkspaceException; error on the request if the
named workspace does not exist.
+ </entry>
+ </row>
+ <row>
+ <entry>ReadNextBlockOfChildrenRequest</entry>
+ <entry>
+ A request to read from the named workspace in the source a block of children of a
node, starting with the children that immediately follow
+ a previously-returned child.
+ This is designed to allow paging through the children, which is much more
efficient for large numbers of children.
+ The node may be specified by path and/or by identification properties, and the
block
+ is defined by the location of the node immediately preceding the block and a
count (i.e., the block size).
+ The connector returns an ordered list of locations for each of the node's
children found in the block,
+ or an empty list if there are no children in that range.
+ The connector also sets on the request the actual location of the parent node
(including the path and identification properties)
+ or sets a &PathNotFoundException; error on the request if the parent node did
not exist in the workspace.
+ The connector sets a &InvalidWorkspaceException; error on the request if the
named workspace does not exist.
+ </entry>
+ </row>
+ <row>
+ <entry>ReadBranchRequest</entry>
+ <entry>
+ A request to read a portion of a subgraph that has as its root a particular node,
up to a maximum depth.
+ This request is an efficient mechanism when a branch (or part of a branch) is to
be navigated and processed,
+ and replaces some non-trivial code to read the branch iteratively using multiple
<code>ReadNodeRequest</code>s.
+ The connector reads the branch to the specified maximum depth, returning the
properties and children for all
+ nodes found in the branch.
+ The connector also sets on the request the actual location of the branch's
root node (including the path and identification properties).
+ The connector sets a &PathNotFoundException; error on the request if the node
at
+ the top of the branch does not exist in the workspace.
+ The connector sets a &InvalidWorkspaceException; error on the request if the
named workspace does not exist.
+ </entry>
+ </row>
+ <row>
+ <entry>CreateNodeRequest</entry>
+ <entry>
+ A request to create a node at the specified location and setting on the new node
the properties included in the request.
+ The connector creates the node at the desired location, adjusting any
same-name-sibling indexes as required.
+ (If an SNS index is provided in the new node's location, existing children
with the same name after that SNS index
+ will have their SNS indexes adjusted. However, if the requested location does
not include a SNS index, the new
+ node is added after all existing children, and it's SNS index is set
accordingly.)
+ The connector also sets on the request the actual location of the new node
(including the path and identification properties)..
+ The connector sets a &PathNotFoundException; error on the request if the
parent node does not exist in the workspace.
+ The connector sets a &InvalidWorkspaceException; error on the request if the
named workspace does not exist.
+ </entry>
+ </row>
+ <row>
+ <entry>RemovePropertiesRequest</entry>
+ <entry>
+ A request to remove a set of properties on an existing node. The request
contains the location of the node as well as the
+ names of the properties to be removed. The connector performs these changes and
sets on the request the
+ actual location (including the path and identification properties) of the node.
+ The connector sets a &PathNotFoundException; error on the request if the node
does not exist in the workspace.
+ The connector sets a &InvalidWorkspaceException; error on the request if the
named workspace does not exist.
+ </entry>
+ </row>
+ <row>
+ <entry>UpdatePropertiesRequest</entry>
+ <entry>
+ A request to set or update properties on an existing node. The request contains
the location of the node as well as the
+ properties to be set and those to be deleted. The connector performs these
changes and sets on the request the
+ actual location (including the path and identification properties) of the node.
+ The connector sets a &PathNotFoundException; error on the request if the node
does not exist in the workspace.
+ The connector sets a &InvalidWorkspaceException; error on the request if the
named workspace does not exist.
+ </entry>
+ </row>
+ <row>
+ <entry>RenameNodeRequest</entry>
+ <entry>
+ A request to change the name of a node. The connector changes the node's
name, adjusts all SNS indexes
+ accordingly, and returns the actual locations (including the path and
identification properties) of both the original
+ location and the new location.
+ The connector sets a &PathNotFoundException; error on the request if the node
does not exist in the workspace.
+ The connector sets a &InvalidWorkspaceException; error on the request if the
named workspace does not exist.
+ </entry>
+ </row>
+ <row>
+ <entry>CopyBranchRequest</entry>
+ <entry>
+ A request to copy a portion of a subgraph that has as its root a particular node,
up to a maximum depth.
+ The request includes the name of the workspace where the original node is located
as well as the name of the
+ workspace where the copy is to be placed (these may be the same, but may be
different).
+ The connector copies the branch from the original location, up to the specified
maximum depth, and places a copy
+ of the node as a child of the new location.
+ The connector also sets on the request the actual location (including the path
and identification properties)
+ of the original location as well as the location of the new copy.
+ The connector sets a &PathNotFoundException; error on the request if the node
at
+ the top of the branch does not exist in the workspace.
+ The connector sets a &InvalidWorkspaceException; error on the request if one
of the named workspaces does not exist.
+ </entry>
+ </row>
+ <row>
+ <entry>MoveBranchRequest</entry>
+ <entry>
+ A request to move a subgraph that has a particular node as its root.
+ The connector moves the branch from the original location and places it as child
of the specified new location.
+ The connector also sets on the request the actual location (including the path
and identification properties)
+ of the original and new locations. The connector will adjust SNS indexes
accordingly.
+ The connector sets a &PathNotFoundException; error on the request if the node
that is to be moved or the
+ new location do not exist in the workspace.
+ The connector sets a &InvalidWorkspaceException; error on the request if the
named workspace does not exist.
+ </entry>
+ </row>
+ <row>
+ <entry>DeleteBranchRequest</entry>
+ <entry>
+ A request to delete an entire branch specified by a single node's location.
+ The connector deletes the specified node and all nodes below it, and sets the
actual location,
+ including the path and identification properties, of the node that was deleted.
+ The connector sets a &PathNotFoundException; error on the request if the node
being deleted does not exist in the workspace.
+ The connector sets a &InvalidWorkspaceException; error on the request if the
named workspace does not exist.
+ </entry>
+ </row>
+ <row>
+ <entry>CompositeRequest</entry>
+ <entry>
+ A request that actually comprises multiple requests (none of which will be a
composite).
+ The connector simply processes all of the requests in the composite request, but
should set on the composite
+ request any error (usually the first error) that occurs during processing of the
contained requests.
+ </entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+ <para>There are also requests that deal with workspaces:</para>
+ <table frame='all'>
+ <title>Types of Workspace Requests</title>
+ <tgroup cols='2' align='left' colsep='1'
rowsep='1'>
+ <colspec colname='c1' colwidth="1*"/>
+ <colspec colname='c2' colwidth="1*"/>
+ <thead>
+ <row>
+ <entry>Name</entry>
+ <entry>Description</entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry>GetWorkspacesRequest</entry>
+ <entry>
+ A request to obtain the names of the existing workspaces that are accessible to
the caller.
+ </entry>
+ </row>
+ <row>
+ <entry>VerifyWorkspaceRequest</entry>
+ <entry>
+ A request to verify that a workspace with a particular name exists.
+ The connector returns the actual location for the root node if the workspace
exists, as well as the actual name of the workspace
+ (e.g., the default workspace name if a null name is supplied).
+ </entry>
+ </row>
+ <row>
+ <entry>CreateWorkspaceRequest</entry>
+ <entry>
+ A request to create a workspace with a particular name.
+ The connector returns the actual location for the root node if the workspace
exists, as well as the actual name of the workspace
+ (e.g., the default workspace name if a null name is supplied).
+ The connector sets a &InvalidWorkspaceException; error on the request if the
named workspace already exists.
+ </entry>
+ </row>
+ <row>
+ <entry>DestroyWorkspaceRequest</entry>
+ <entry>
+ A request to destroy a workspace with a particular name.
+ The connector sets a &InvalidWorkspaceException; error on the request if the
named workspace does not exist.
+ </entry>
+ </row>
+ <row>
+ <entry>CloneWorkspaceRequest</entry>
+ <entry>
+ A request to clone one named workspace as another new named workspace.
+ The connector sets a &InvalidWorkspaceException; error on the request if the
original workspace does not exist,
+ or if the new workspace already exists.
+ </entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+ <para>
+ Although there are over a dozen different kinds of requests, we do anticipate adding
more in future releases.
+ For example, DNA will likely support searching repository content in sources through
an additional subclass of &Request;.
+ Getting the version history for a node will likely be another kind of request added
in an upcoming release.
+ </para>
+ <para>
+ A connector is technically free to implement the
<code>execute(...)</code> method in any way, as long as the semantics
+ are maintained. But DNA provides a &RequestProcessor; class that can simplify
writing your own connector and at the
+ same time help insulate your connector from new kinds of requests that may be added
in the future. The &RequestProcessor;
+ is an abstract class that defines a <code>process(...)</code> method for
each concrete &Request; subclass.
+ In other words, there is a <code>process(CompositeRequest)</code> method,
a <code>process(ReadNodeRequest)</code> method,
+ and so on.
+ </para>
+ <para>
+ To use this in your connector, simply create a subclass of &RequestProcessor;,
overriding all of the abstract methods and optionally
+ overriding any of the other methods that have a default implementation.
+ </para>
+ <note>
+ <para>
+ The &RequestProcessor; abstract class contains default implementations for quite
a few of the <code>process(...)</code> methods,
+ and these will be <emphasis>sufficient</emphasis> but probably not
efficient or optimum. If you can provide a more efficient
+ implementation given your source, feel free to do so. However, if performance is
not a big issue, all of the concrete methods
+ will provide the correct behavior. Keep things simple to start out - you can always
provide better implementations later.
+ </para>
+ </note>
+ <para>
+ Then, in your connector's <code>execute(&ExecutionContext;,
&Request;)</code> method, instantiate your &RequestProcessor; subclass
+ and call its <code>process(&Request;) method, passing in the
<code>execute(...)</code> method's &Request; parameter.</code>
+ The &RequestProcessor; will determine the appropriate method given the actual
&Request; object and will then invoke that method:
+ </para>
+ <programlisting>
+public void execute( final &ExecutionContext; context,
+ final &Request; request ) throws RepositorySourceException {
+ RequestProcessor processor = new RequestProcessor(context);
+ try {
+ processor.process(request);
+ } finally {
+ processor.close();
+ }
+}</programlisting>
+ <para>
+ If you do this, the bulk of your connector implementation may be in the
&RequestProcessor; implementation methods.
+ This not only is pretty maintainable, it also lends itself to easier testing. And
should any new request types be added
+ in the future, your connector may work just fine without any changes. In fact, if
the &RequestProcessor; class
+ can implement meaningful methods for those new request types, your connector may
"just work". Or, at least
+ your connector will still be binary compatible, even if your connector won't
support any of the new features.
+ </para>
+ <para>
+ Finally, how should the connector handle exceptions? As mentioned above, each
&Request; object has a slot where the connector
+ can set any exception encountered during processing. This not only handles the
exception, but in the case of &CompositeRequest;s
+ it also correctly associates the problem with the request. However, it is perfectly
acceptable to throw an exception
+ if the connection becomes invalid (e.g., there is a communication failure) or if a
fatal error would prevent subsequent
+ requests from being processed.
+ </para>
+ </sect2>
+ <sect2 id="testing_custom_connectors">
+ <title>Testing custom connectors</title>
+ <para>
+ Testing connectors is not really that much different than testing other classes.
Using mocks may help to isolate your
+ instances so you can create more unit tests that don't require the underlying
source system.
+ </para>
+ <para>
+ However, there may be times when you have to use the underlying source system in your
tests. If this is the case,
+ we recommend using Maven integration tests, which run at a different point in the
Maven lifecycle. The benefit of
+ using integration tests is that by convention they're able to rely upon external
systems. Plus, your unit tests
+ don't become polluted with slow-running tests that break if the external system
is not available.
+ </para>
+ </sect2>
+ <sect2 id="deploying_custom_connectors">
+ <title>Configuring and deploying custom connectors</title>
+ <para>
+ After building your connector project, you need to configure the JBoss DNA components
your application is using so
+ that they use your connector. In a lot of cases, this will entail instantiating your
connector's &RepositorySource; class,
+ setting the various properties, and registering it with a &RepositoryLibrary;.
Or, it will entail using a configuration
+ repository to use your source and letting &RepositoryService; instantiate and set
up your &RepositorySource; instance.
+ Or, you can just instantiate and set it up manually, passing the instance to whatever
component needs it.
+ </para>
+ <para>
+ And of course you have to make the JAR file containing your connector (as well as any
dependency JARs) available to
+ your application's classpath.
+ </para>
+ </sect2>
+ </sect1>
+ <sect1 id="dna_graph_api">
+ <title>Graph API for using connectors</title>
+ <para>
+ So far we've talked about repositories, repository connectors, and how connectors
respond to the different kinds of requests.
+ Normally you'd code to the JCR API and use our JCR implementation. However, what
does your code look like if you want
+ to use the connectors directly, without using our JCR implementation? After all, you
may be a contributor to JBoss DNA,
+ or you may want to take advantage of our connectors without all the overhead of JCR.
+ </para>
+ <para>
+ One option, of course, is to explicitly create the different requests and pass them to
the connector's <code>execute(...)</code> method.
+ While this is the most efficient approach (and one taken in some key DNA components),
you probably want something that
+ is much less verbose and much easier to use. This is where the DNA graph API comes
in.
+ </para>
+ <para>
+ JBoss DNA's <emphasis>Graph API</emphasis> was designed as a
lightweight public API for working with graph information,
+ and it insulates components from the underlying requests and interacting with
connectors.
+ The &Graph; class is the primary class in API, and each instance represents a
single, independent
+ view of the graph of content from a single connector. &Graph; instances return
snapshots of state, and those snapshots
+ never change after they're retrieved. To obtain a &Graph; instance, use the
static <code>create(...)</code>
+ method, supplying the name of the source, a &RepositoryConnectionFactory; from
which a &RepositoryConnection; can be obtained,
+ and the &ExecutionContext;.
+ </para>
+ <para>
+ The &Graph; class basically represents an <ulink
url="http://www.martinfowler.com/bliki/DomainSpecificLanguage.html&q...
domain specific language (DSL)</ulink>,
+ designed to be easy to use in an application.
+ The Graph API makes extensive use of interfaces and method chaining, so that methods
return a concise interface that has only those
+ methods that make sense at that point. In fact, this should be really easy if your
IDE has code completion.
+ Just remember that under the covers, a &Graph; is just building &Request;
objects, submitting them to the connector,
+ and then exposing the results.
+ </para>
+ <sect2 id="dna_graph_api_workspaces">
+ <title>Using workspaces</title>
+ <para>
+ Let's look at some examples of how the Graph API works. This first example shows
how to obtain the names of the available workspaces:
+ </para>
+ <programlisting>
+&Set;<&String;> workspaceNames = graph.getWorkspaces();
+</programlisting>
+ <para>Once you know the name of the workspace, you can specify that the graph
should use it:
+ </para>
+ <programlisting>
+graph.useWorkspace("myWorkspace");
+</programlisting>
+ <para>
+ From this point forward, all requests will apply to the workspace named
"myWorkspace". At any time, you can use a different workspace,
+ which will affect all subsequent requests made using the graph. Of course, creating a
new workspace is just as easy:
+ </para>
+ <programlisting>
+graph.createWorkspace().named("newWorkspace");
+</programlisting>
+ <para>This will attempt to create a workspace named "newWorkspace",
which will fail if that workspace already exists. You may
+ want to create a new workspace with a name that should be altered if the name you
supply is already used. The following code shows
+ how you can do this:
+ </para>
+ <programlisting>
+graph.createWorkspace().namedSomethingLike("newWorkspace");
+</programlisting>
+ <para>If there is no existing workspace named "newWorkspace", a new
one will be created with this name. However, if "newWorkspace" already
+ exists, this call will create a workspace with a name that is some alteration of the
supplied name.
+ </para>
+ <para>
+ You can also clone workspaces, too:
+ </para>
+ <programlisting>
+graph.createWorkspace().clonedFrom("original").named("something");
+</programlisting>
+ <para>
+ or
+ </para>
+ <programlisting>
+graph.createWorkspace().clonedFrom("original").namedSomethingLike("something");
+</programlisting>
+ <para>
+ As you can see, it's very easy to specify which workspace you want to use or to
create new workspaces. You can also find out which workspace
+ the graph is currently using:
+ </para>
+ <programlisting>
+&String; current = graph.getCurrentWorkspaceName();
+</programlisting>
+ <para>or, if you want, you can get more information about the workspace:
+ </para>
+ <programlisting>
+&Workspace; current = graph.getCurrentWorkspace();
+&String; name = current.getName();
+&Location; rootLocation = current.getRoot();
+</programlisting>
+ </sect2>
+ <sect2 id="dna_graph_api_nodes">
+ <title>Working with nodes</title>
+ <para>
+ Now let's switch to working with nodes. This first example returns a map of
properties (keyed by property name)
+ for a node at a specific &Path;:
+ </para>
+ <programlisting>
+&Path; path = ...
+Map<&Name;,&Property;> propertiesByName =
graph.getPropertiesByName().on(path);
+</programlisting>
+ <para>
+ This next example shows how the graph can be used to obtain and loop over the
properties of a node:
+ </para>
+ <programlisting>
+&Path; path = ...
+for ( &Property; property : graph.getProperties().on(path) ) {
+ ...
+}
+</programlisting>
+ <para>
+ Likewise, the next example shows how the graph can be used to obtain and loop over the
children of a node:
+ </para>
+ <programlisting>
+&Path; path = ...
+for ( &Location; child : graph.getChildren().of(path) ) {
+ &Path; childPath = child.getPath();
+ ...
+}
+</programlisting>
+ <para>
+ Notice that the examples pass a &Path; instance to the
<code>on(...)</code> and <code>of(...)</code> methods. Many
+ of the Graph API methods take a variety of parameter types, including String,
&Path;s, &Location;s, &UUID;, or &Property; parameters.
+ This should make it easy to use in many different situations.
+ </para>
+ <para>
+ Of course, changing content is more interesting and offers more interesting
possibilities. Here are a few examples:
+ </para>
+ <programlisting>
+&Path; path = ...
+&Location; location = ...
+&Property; idProp1 = ...
+&Property; idProp2 = ...
+&UUID; uuid = ...
+graph.move(path).into(idProp1, idProp2);
+graph.copy(path).into(location);
+graph.delete(uuid);
+graph.delete(idProp1,idProp2);
+</programlisting>
+ <para>
+ The methods shown above work immediately, as soon as each request is built. However,
there is another way to use
+ the &Graph; object, and that is in a <emphasis>batch</emphasis> mode.
Simply create a &GraphBatch; object using the
+ <code>batch()</code> method, create the requests on that batch object, and
then execute all of the commands on the
+ batch by calling its <code>execute()</code> method. That
<code>execute()</code> method returns a &Results; interface
+ that can be used to read the node information retrieved by the batched requests.
+ </para>
+ <para>
+ Method chaining works really well with the batch mode, since multiple commands can be
assembled together very easily:
+ </para>
+ <programlisting>
+&Path; path = ...
+String path2 = ...
+&Location; location = ...
+&Property; idProp1 = ...
+&Property; idProp2 = ...
+&UUID; uuid = ...
+graph.batch().move(path).into(idProp1,
idProp2).and().copy(path2).into(location).and().delete(uuid).execute();
+&Results; results = graph.batch().read(path2).
+ and().readChildren().of(idProp1,idProp2).
+ and().readSugraphOfDepth(3).at(uuid2).
+ execute();
+for ( &Location; child : results.getNode(path2) ) {
+ ...
+}
+</programlisting>
+ <para>
+ Of course, this section provided just a hint of the Graph API.
+ The &Graph; interface is actually quite complete and offers a full-featured
approach for reading and updating a graph.
+ For more information, see the &Graph; JavaDocs.
+ </para>
+ </sect2>
+ </sect1>
+ <sect1>
+ <title>Summary</title>
+ <para>
+ In this chapter, we covered all the aspects of JBoss DNA repositories, including the
connector framework,
+ how DNA's JCR implementation works with connectors, what connectors are available
(and how to use them),
+ and how to write your own connector. So now that you know how to set up and use JBoss
DNA repositories,
+ the <link linkend="jcr">next chapter</link> describes how you
can leverage JBoss DNA's JCR implementation.
+ </para>
+ </sect1>
+</chapter>
Property changes on:
trunk/docs/reference/src/main/docbook/en-US/content/core/repositories.xml
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Copied: trunk/docs/reference/src/main/docbook/en-US/content/core/sequencing.xml (from rev
994, trunk/docs/reference/src/main/docbook/en-US/content/sequencing.xml)
===================================================================
--- trunk/docs/reference/src/main/docbook/en-US/content/core/sequencing.xml
(rev 0)
+++ trunk/docs/reference/src/main/docbook/en-US/content/core/sequencing.xml 2009-06-08
14:57:17 UTC (rev 995)
@@ -0,0 +1,793 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ JBoss DNA (
http://www.jboss.org/dna)
+ ~
+ ~ See the COPYRIGHT.txt file distributed with this work for information
+ ~ regarding copyright ownership. Some portions may be licensed
+ ~ to Red Hat, Inc. under one or more contributor license agreements.
+ ~ See the AUTHORS.txt file in the distribution for a full listing of
+ ~ individual contributors.
+ ~
+ ~ JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
+ ~ is licensed to you under the terms of the GNU Lesser General Public License as
+ ~ published by the Free Software Foundation; either version 2.1 of
+ ~ the License, or (at your option) any later version.
+ ~
+ ~ JBoss DNA is distributed in the hope that it will be useful,
+ ~ but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ ~ or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ ~ for more details.
+ ~
+ ~ You should have received a copy of the GNU Lesser General Public License
+ ~ along with this distribution; if not, write to:
+ ~ Free Software Foundation, Inc.
+ ~ 51 Franklin Street, Fifth Floor
+ ~ Boston, MA 02110-1301 USA
+ -->
+<!DOCTYPE preface PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
"http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd" [
+<!ENTITY % CustomDTD SYSTEM "../../custom.dtd">
+%CustomDTD;
+]>
+<chapter id="sequencing">
+ <title>Sequencing content</title>
+ <para>As we've mentioned before, JBoss DNA is able to work with existing JCR
repositories. Your client applications
+ make changes to the information in those repositories, and JBoss DNA automatically
uses its sequencers to extract
+ additional information from the uploaded files.</para>
+ <para>
+ This chapter discusses the sequencing features of JBoss DNA and the components that are
involved.
+ </para>
+ <sect1 id="sequencing-service">
+ <title>Sequencing Service</title>
+ <para>The JBoss DNA <emphasis>sequencing service</emphasis> is the
component that manages the <emphasis>sequencers</emphasis>,
+ reacting to changes in JCR repositories and then running the appropriate sequencers.
+ This involves processing the changes on a node, determining which (if any)
sequencers should be run on that node,
+ and for each sequencer constructing the execution environment, calling the
sequencer, and saving the information
+ generated by the sequencer.</para>
+ <note>
+ <para>Configuring JBoss DNA services is a bit more manual than is ideal. As
you'll see, JBoss DNA uses dependency
+ injection to allow a great deal of flexibility in how it can be configured and
customized. But this flexibility
+ makes it more difficult for you to use. We understand this, and will soon provide
a much easier way to set up
+ and manage JBoss DNA. Current plans are to use the <ulink
url="http://www.jboss.org/jbossmc">JBoss Microcontainer</ulink>
+ along with a configuration repository.</para>
+ </note>
+ <para>To set up the sequencing service, an instance is created, and dependent
components are injected into
+ the object. This includes among other things:
+ <itemizedlist>
+ <listitem>
+ <para>An <emphasis>execution context</emphasis> that defines the
context in which the service runs, including
+ a factory for JCR sessions given names of the repository and workspace. This
factory must be configured,
+ and is how JBoss DNA knows about your JCR repositories and how to connect to
them. More on this a bit later.</para>
+ </listitem>
+ <listitem>
+ <para>An optional <emphasis>factory for class loaders</emphasis>
used to load sequencers. If no factory is supplied,
+ the service uses the current thread's context class loader (or if that is
null, the class loader that loaded the
+ sequencing service class).</para>
+ </listitem>
+ <listitem>
+ <para>An &ExecutorService; used to execute the sequencing activites. If
none
+ is supplied, a new single-threaded executor is created by calling
<code>Executors.newSingleThreadExecutor()</code>.
+ (This can easily be changed by subclassing and overriding the
<code>SequencerService.createDefaultExecutorService()</code>
method.)</para>
+ </listitem>
+ <listitem>
+ <para>Filters for sequencers and events. By default, all sequencers are
considered for "node added", "property added"
+ and "property changed" events.</para>
+ </listitem>
+ </itemizedlist>
+ </para>
+ <para>As mentioned above, the &JcrExecutionContext; provides access to a
&SessionFactory; that is used
+ by JBoss DNA to establish sessions to your JCR repositories. Two implementations
are available:
+ <itemizedlist>
+ <listitem>
+ <para>The &JndiSessionFactory;> looks up JCR &Repository; instances
in JNDI using
+ names that are supplied when creating sessions. This implementation also
has methods to set the
+ JCR &Credentials; for a given workspace name.</para>
+ </listitem>
+ <listitem>
+ <para>The &SimpleSessionFactory; has methods to register the JCR
&Repository; instances
+ with names, as well as methods to set the JCR &Credentials; for a given
workspace name.</para>
+ </listitem>
+ </itemizedlist>
+ You can use the &JcrExecutionContext; and use one of these &SessionFactory;
implementations or another
+ implementation that you provide.</para>
+ <para>Here's an example of how to instantiate and configure the
&SequencingService;:</para>
+ <programlisting>
+&SimpleSessionFactory; sessionFactory = new &SimpleSessionFactory;();
+sessionFactory.registerRepository("Main Repository", this.repository);
+&Credentials; credentials = new &SimpleCredentials;("jsmith",
"secret".toCharArray());
+sessionFactory.registerCredentials("Main Repository/Workspace1", credentials);
+// Now create the JCR execution context, with a reference to the session factory
+// and the name of the repository from which sessions will be obtained ...
+ExecutionContext executionContext = new
&JcrExecutionContext;(sessionFactory,"Main Repository");
+
+// Create the sequencing service, passing in the execution context ...
+&SequencingService; sequencingService = new &SequencingService;();
+sequencingService.setExecutionContext(executionContext);
+</programlisting>
+ <para>After the sequencing service is created and configured, it must be started.
The &SequencingService;
+ has an <emphasis>administration object</emphasis> (that is an instance
of &ServiceAdministrator;)
+ with <code>start()</code>, <code>pause()</code>, and
<code>shutdown()</code> methods. The latter method will
+ close the queue for sequencing, but will allow sequencing operations already
running to complete normally.
+ To wait until all sequencing operations have completed, simply call the
<code>awaitTermination</code> method
+ and pass it the maximum amount of time you want to wait.</para>
+ <programlisting>
+sequencingService.getAdministrator().start();
+</programlisting>
+ <para>The JBoss DNA services are utilizing resources and threads that must be
released before your application is ready to shut down.
+ The safe way to do this is to simply obtain the &ServiceAdministrator; for each
service (via the <code>getServiceAdministrator()</code> method)
+ and call <code>shutdown()</code>. As previously mentioned, the shutdown
method will simply prevent new work from being processed
+ and will not wait for existing work to be completed. If you want to wait until the
service completes all its work, you must wait
+ until the service terminates. Here's an example that shows how this is
done:</para>
+ <programlisting>
+// Shut down the service and wait until it's all shut down ...
+sequencingService.getAdministrator().shutdown();
+sequencingService.getAdministrator().awaitTermination(5, TimeUnit.SECONDS);
+
+// Shut down the observation service ...
+observationService.getAdministrator().shutdown();
+observationService.getAdministrator().awaitTermination(5, TimeUnit.SECONDS);
+</programlisting>
+ </sect1>
+ <sect1 id="sequencer-configuration">
+ <title>Sequencer Configurations</title>
+ <para>The sequencing service must also be configured with the sequencers that it
will use. This is done using the
+ <code>addSequencer(SequencerConfig)</code> method and passing a
&SequencerConfig; instance that
+ you create. Here's the code that defines 3 sequencer configurations: 1 that
places image metadata into
+ "<code><![CDATA[/images/<filename>]]></code>",
another that places MP3 metadata into
"<code><![CDATA[/mp3s/<filename>]]></code>",
+ and a third that places a structure that represents the classes, methods, and
attributes found within Java source into
+
"<code><![CDATA[/java/<filename>]]></code>".</para>
+ <programlisting>
+String name = "Image Sequencer";
+String desc = "Sequences image files to extract the characteristics of the
image";
+String classname = "org.jboss.dna.sequencer.images.ImageMetadataSequencer";
+String[] classpath = null; // Use the current classpath
+String[] pathExpressions =
{"//(*.(jpg|jpeg|gif|bmp|pcx|png)[*])/jcr:content[@jcr:data] =>
/images/$1"};
+&SequencerConfig; imageSequencerConfig = new &SequencerConfig;(name, desc,
classname,
+ classpath, pathExpressions);
+sequencingService.addSequencer(imageSequencerConfig);
+
+name = "MP3 Sequencer";
+desc = "Sequences MP3 files to extract the ID3 tags from the audio file";
+classname = "org.jboss.dna.sequencer.mp3.Mp3MetadataSequencer";
+pathExpressions = {"//(*.mp3[*])/jcr:content[@jcr:data] => /mp3s/$1"};
+&SequencerConfig; mp3SequencerConfig = new &SequencerConfig;(name, desc,
classname,
+ classpath, pathExpressions);
+sequencingService.addSequencer(mp3SequencerConfig);
+
+name = "Java Sequencer";
+desc = "Sequences java files to extract the characteristics of the Java
source";
+classname = "org.jboss.dna.sequencer.java.JavaMetadataSequencer";
+pathExpressions = {"//(*.java[*])/jcr:content[@jcr:data] => /java/$1"};
+&SequencerConfig; javaSequencerConfig = new &SequencerConfig;(name, desc,
classname,
+ classpath, pathExpressions);
+this.sequencingService.addSequencer(javaSequencerConfig);
+</programlisting>
+ <para>Each configuration defines several things, including the name,
description, and sequencer implementation class.
+ The configuration also defines the classpath information, which can be passed to the
&ClassLoaderFactory; to get
+ a Java &ClassLoader; with which the sequencer class can be loaded. (If no
classpath information is provided, as is done
+ in the code above, the application class loader is used.) The configuration also
specifies the path expressions that
+ identify the nodes that should be sequenced with the sequencer and where to store
the output generated by the sequencer.
+ Path expressions are pretty straightforward but are quite powerful, so before we go
any further with the example,
+ let's dive into path expressions in more detail.</para>
+ <sect2 id="path_expressions">
+ <title>Path Expressions</title>
+ <para>Path expressions consist of two parts: a selection criteria (or an input
path) and an output path:</para>
+ <programlisting><![CDATA[ inputPath => outputPath
]]></programlisting>
+ <para>The <emphasis>inputPath</emphasis> part defines an expression
for the path of a node that is to be sequenced.
+ Input paths consist of '<code>/</code>' separated segments,
where each segment represents a pattern for a single node's
+ name (including the same-name-sibling indexes) and
'<code>@</code>' signifies a property name.</para>
+ <para>Let's first look at some simple examples:</para>
+ <table frame='all'>
+ <title>Simple Input Path Examples</title>
+ <tgroup cols='2' align='left' colsep='1'
rowsep='1'>
+ <colspec colname='c1' colwidth="1*"/>
+ <colspec colname='c2' colwidth="1*"/>
+ <thead>
+ <row>
+ <entry>Input Path</entry>
+ <entry>Description</entry>
+ </row>
+ </thead>
+ <tbody>
+ <row><entry>/a/b</entry><entry>Match node
"<code>b</code>" that is a child of the top level node
"<code>a</code>". Neither node
+ may have any same-name-sibilings.</entry></row>
+ <row><entry>/a/*</entry><entry>Match any child node of the
top level node "<code>a</code>".</entry></row>
+ <row><entry>/a/*.txt</entry><entry>Match any child node of
the top level node "<code>a</code>" that also has a name ending in
"<code>.txt</code>".</entry></row>
+ <row><entry>/a/*.txt</entry><entry>Match any child node of
the top level node "<code>a</code>" that also has a name ending in
"<code>.txt</code>".</entry></row>
+ <row><entry>/a/b@c</entry><entry>Match the property
"<code>c</code>" of node
"<code>/a/b</code>".</entry></row>
+ <row><entry>/a/b[2]</entry><entry>The second child named
"<code>b</code>" below the top level node
"<code>a</code>".</entry></row>
+ <row><entry>/a/b[2,3,4]</entry><entry>The second, third or
fourth child named "<code>b</code>" below the top level node
"<code>a</code>".</entry></row>
+ <row><entry>/a/b[*]</entry><entry>Any (and every) child
named "<code>b</code>" below the top level node
"<code>a</code>".</entry></row>
+ <row><entry>//a/b</entry><entry>Any node named
"<code>b</code>" that exists below a node named
"<code>a</code>", regardless
+ of where node "<code>a</code>" occurs. Again, neither
node may have any same-name-sibilings.</entry></row>
+ </tbody>
+ </tgroup>
+ </table>
+ <para>With these simple examples, you can probably discern the most important
rules. First, the '<code>*</code>' is a wildcard character
+ that matches any character or sequence of characters in a node's name (or index
if appearing in between square brackets), and
+ can be used in conjunction with other characters (e.g.,
"<code>*.txt</code>").</para>
+ <para>Second, square brackets (i.e., '<code>[</code>' and
'<code>]</code>') are used to match a node's same-name-sibiling
index.
+ You can put a single non-negative number or a comma-separated list of non-negative
numbers. Use '0' to match a node that has no
+ same-name-sibilings, or any positive number to match the specific
same-name-sibling.</para>
+ <para>Third, combining two delimiters (e.g.,
"<code>//</code>") matches any sequence of nodes, regardless of what
their names are
+ or how many nodes. Often used with other patterns to identify nodes at any level
matching other patterns.
+ Three or more sequential slash characters are treated as two.</para>
+ <para>Many input paths can be created using just these simple rules. However,
input paths can be more complicated. Here are some
+ more examples:</para>
+ <table frame='all'>
+ <title>More Complex Input Path Examples</title>
+ <tgroup cols='2' align='left' colsep='1'
rowsep='1'>
+ <colspec colname='c1' colwidth="1*"/>
+ <colspec colname='c2' colwidth="1*"/>
+ <thead>
+ <row>
+ <entry>Input Path</entry>
+ <entry>Description</entry>
+ </row>
+ </thead>
+ <tbody>
+ <row><entry>/a/(b|c|d)</entry><entry>Match children of the
top level node "<code>a</code>" that are named
"<code>a</code>",
+ "<code>b</code>" or
"<code>c</code>". None of the nodes may have same-name-sibling
indexes.</entry></row>
+ <row><entry>/a/b[c/d]</entry><entry>Match node
"<code>b</code>" child of the top level node
"<code>a</code>", when node
+ "<code>b</code>" has a child named
"<code>c</code>", and "<code>c</code>" has a
child named "<code>d</code>".
+ Node "<code>b</code>" is the selected node, while nodes
"<code>b</code>" and "<code>b</code>" are used
as criteria but are not
+ selected.</entry></row>
+ <row><entry>/a(/(b|c|d|)/e)[f/g/@something]</entry><entry>Match
node "<code>/a/b/e</code>",
"<code>/a/c/e</code>", "<code>/a/d/e</code>",
+ or "<code>/a/e</code>" when they also have a child
"<code>f</code>" that itself has a child
"<code>g</code>" with property
+ "<code>something</code>". None of the nodes may have
same-name-sibling indexes.</entry></row>
+ </tbody>
+ </tgroup>
+ </table>
+ <para>These examples show a few more advanced rules. Parentheses (i.e.,
'<code>(</code>' and '<code>)</code>') can be
used
+ to define a set of options for names, as shown in the first and third rules.
Whatever part of the selected node's path
+ appears between the parentheses is captured for use within the output path. Thus,
the first input path in the previous table
+ would match node "<code>/a/b</code>", and "b" would
be captured and could be used within the output path using
"<code>$1</code>",
+ where the number used in the output path identifies the parentheses.</para>
+ <para>Square brackets can also be used to specify criteria on a node's
properties or children. Whatever appears in between the square
+ brackets does not appear in the selected node.</para>
+ <para>Let's go back to the previous code fragment and look at the first
path expression:</para>
+ <programlisting><![CDATA[
//(*.(jpg|jpeg|gif|bmp|pcx|png)[*])/jcr:content[@jcr:data] => /images/$1
]]></programlisting>
+ <para>This matches a node named
"<code>jcr:content</code>" with property
"<code>jcr:data</code>" but no siblings with the same name,
+ and that is a child of a node whose name ends with
"<code>.jpg</code>", "<code>.jpeg</code>",
"<code>.gif</code>", "<code>.bmp</code>",
"<code>.pcx</code>",
+ or "<code>.png</code>" that may have any same-name-sibling
index. These nodes can appear at any level in the repository.
+ Note how the input path capture the filename (the segment containing the file
extension), including any same-name-sibling index.
+ This filename is then used in the output path, which is where the sequenced content
is placed.</para>
+ </sect2>
+ </sect1>
+ <sect1 id="sequencers">
+ <title>JBoss DNA Sequencers</title>
+ <para>
+ JBoss DNA includes a number of sequencers "out of the box". These
sequencers can be used within your application to sequence
+ a variety of common file formats. To use them, the only thing you have to do is
define the appropriate sequencer configurations
+ and include the appropriate JAR files.
+ </para>
+ <sect2 id="dna-sequencer-images">
+ <title>Image sequencer</title>
+ <para>
+ A sequencer that extracts metadata from JPEG, GIF, BMP, PCX, PNG, IFF, RAS, PBM, PGM,
PPM and PSD image files.
+ This sequencer extracts the file format, image resolution, number of bits per pixel
and optionally number of images, comments
+ and physical resolution, and then writes this information into the repository using
the following structure:
+ </para>
+ <itemizedlist>
+ <listitem>
+ <para>
+ <emphasis role="strong">image:metadata</emphasis> node of
type <code>image:metadata</code>
+ </para>
+ </listitem>
+ <listitem>
+ <itemizedlist>
+ <listitem>
+ <para><emphasis role="strong">jcr:mimeType</emphasis>
- optional string property for the mime type of the image</para>
+ </listitem>
+ <listitem>
+ <para><emphasis role="strong">jcr:encoding</emphasis>
- optional string property for the encoding of the image</para>
+ </listitem>
+ <listitem>
+ <para><emphasis
role="strong">image:formatName</emphasis> - string property for the
name of the format</para>
+ </listitem>
+ <listitem>
+ <para><emphasis role="strong">image:width</emphasis> -
optional integer property for the image's width in pixels</para>
+ </listitem>
+ <listitem>
+ <para><emphasis role="strong">image:height</emphasis>
- optional integer property for the image's height in pixles</para>
+ </listitem>
+ <listitem>
+ <para><emphasis
role="strong">image:bitsPerPixel</emphasis> - optional integer property
for the number of bits per pixel</para>
+ </listitem>
+ <listitem>
+ <para><emphasis
role="strong">image:progressive</emphasis> - optional boolean property
specifying whether the image is stored in a progressive
+ (i.e., interlaced) form</para>
+ </listitem>
+ <listitem>
+ <para><emphasis
role="strong">image:numberOfImages</emphasis> - optional integer
property for the number of images stored in the file; defaults
+ to 1</para>
+ </listitem>
+ <listitem>
+ <para><emphasis
role="strong">image:physicalWidthDpi</emphasis> - optional integer
property for the physical width of the image in dots per inch</para>
+ </listitem>
+ <listitem>
+ <para><emphasis
role="strong">image:physicalHeightDpi</emphasis> - optional integer
property for the physical height of the image in dots per
+ inch</para>
+ </listitem>
+ <listitem>
+ <para><emphasis
role="strong">image:physicalWidthInches</emphasis> - optional double
property for the physical width of the image in inches</para>
+ </listitem>
+ <listitem>
+ <para><emphasis
role="strong">image:physicalHeightInches</emphasis> - optional double
property for the physical height of the image in inches</para>
+ </listitem>
+ </itemizedlist>
+ </listitem>
+ </itemizedlist>
+ <para>
+ This structure could be extended in the future to add EXIF and IPTC metadata as child
nodes. For example, EXIF metadata is
+ structured as tags in directories, where the directories form something like
namespaces, and which are used by different camera
+ vendors to store custom metadata. This structure could be mapped with each directory
(e.g. "EXIF" or "Nikon Makernote" or
+ "IPTC") as the name of a child node, with the EXIF tags values stored as
either properties or child nodes.
+ </para>
+ <para>
+ To use this sequencer, simply include the
<code>dna-sequencer-images</code> JAR
+ in your application and configure the Sequencing Service to use this sequencer using
something similar to:
+ </para>
+ <programlisting>
+String name = "Image Sequencer";
+String desc = "Sequences image files to extract the characteristics of the
image";
+String classname = "org.jboss.dna.sequencer.images.ImageMetadataSequencer";
+String[] classpath = null; // Use the current classpath
+String[] pathExpressions =
{"//(*.(jpg|jpeg|gif|bmp|pcx|png|iff|ras|pbm|pgm|ppm|psd)[*])/jcr:content[@jcr:data]
=> /images/$1"};
+&SequencerConfig; sequencerConfig = new &SequencerConfig;(name, desc, classname,
+ classpath, pathExpressions);
+sequencingService.addSequencer(sequencerConfig);
+</programlisting>
+ </sect2>
+ <sect2 id="dna-sequencer-msoffice">
+ <title>Microsoft Office document sequencer</title>
+ <para>
+ This sequencer is included in JBoss DNA and processes Microsoft Office documents,
including Word documents, Excel spreadsheets,
+ and PowerPoint presentations. With documents, the sequencer extracts the internal
structure based on Heading styles.
+ With presentations, the sequencer extracts the slides, titles, text and slide
thumbnails.
+ With spreadsheets, the sequencer extracts the names of the sheets. And, the
sequencer extracts for all the files the
+ general file information, including the name of the author, title, keywords, subject,
comments, and various dates.
+ </para>
+ <para>
+ To use this sequencer, simply include the
<code>dna-sequencer-msoffice</code> JAR and all of the
+ <ulink
url="http://poi.apache.org/">POI</ulink> JARs
+ in your application and configure the Sequencing Service to use this sequencer using
something similar to:
+ </para>
+ <programlisting>
+String name = "Microsoft Office Document Sequencer";
+String desc = "Sequences MS Office documents, including spreadsheets and
presentations";
+String classname =
"org.jboss.dna.sequencer.msoffice.MSOfficeMetadataSequencer";
+String[] classpath = null; // Use the current classpath
+String[] pathExpressions = {"//(*.(doc|docx|ppt|pps|xls)[*])/jcr:content[@jcr:data]
=> /msoffice/$1"};
+&SequencerConfig; sequencerConfig = new &SequencerConfig;(name, desc, classname,
+ classpath, pathExpressions);
+sequencingService.addSequencer(sequencerConfig);
+</programlisting>
+ </sect2>
+ <sect2 id="dna-sequencer-zip">
+ <title>ZIP archive sequencer</title>
+ <para>
+ The ZIP file sequencer is included in JBoss DNA and extracts the files and folders
contained in the ZIP archive file,
+ extracting the files and folders into the repository using JCR's
<code>nt:file</code> and <code>nt:folder</code> node types.
+ </para>
+ <para>
+ To use this sequencer, simply include the <code>dna-sequencer-zip</code>
JAR
+ in your application and configure the Sequencing Service to use this sequencer using
something similar to:
+ </para>
+ <programlisting>
+String name = "ZIP Sequencer";
+String desc = "Sequences ZIP archives to extract the files and folders";
+String classname = "org.jboss.dna.sequencer.zip.ZipSequencer";
+String[] pathExpressions = {"//(*.zip[*])/jcr:content[@jcr:data] =>
/zips/$1"};
+&SequencerConfig; sequencerConfig = new &SequencerConfig;(name, desc, classname,
+ classpath, pathExpressions);
+this.sequencingService.addSequencer(sequencerConfig);
+</programlisting>
+ </sect2>
+ <sect2 id="dna-sequencer-java">
+ <title>Java source sequencer</title>
+ <para>
+ One of the sequencers that included in JBoss DNA is the <emphasis
role="strong">dna-sequencer-java</emphasis> subproject.
+ This sequencer parses Java source code added to the repository and extracts the basic
structure of the classes and enumerations
+ defined in the code.
+ This structure includes: the package structures, class declarations, class and member
attribute declarations,
+ class and member method declarations with signature (but not implementation logic),
enumerations with each enumeration literal value,
+ annotations, and JavaDoc information for all of the above.
+ After extracting this information from the source code, the sequencer then writes
this structure into the repository,
+ where it can be further processed, analyzed, searched, navigated, or referenced.
+ </para>
+ <para>
+ To use this sequencer, simply include the <code>dna-sequencer-java</code>
JAR (plus all of the JARs that it is dependent upon)
+ in your application and configure the Sequencing Service to use this sequencer using
something similar to:
+ </para>
+ <programlisting>
+String name = "Java Sequencer";
+String desc = "Sequences java files to extract the characteristics of the Java
source";
+String classname = "org.jboss.dna.sequencer.java.JavaMetadataSequencer";
+String[] classpath = null; // Use the current classpath
+String[] pathExpressions = {"//(*.java[*])/jcr:content[@jcr:data] =>
/java/$1"};
+&SequencerConfig; sequencerConfig = new &SequencerConfig;(name, desc, classname,
+ classpath, pathExpressions);
+this.sequencingService.addSequencer(sequencerConfig);
+</programlisting>
+ </sect2>
+ <sect2 id="dna-sequencer-mp3">
+ <title>MP3 audio file sequencer</title>
+ <para>
+ Another sequencer that is included in JBoss DNA is the <emphasis
role="strong">dna-sequencer-mp3</emphasis> sequencer project.
+ This sequencer processes MP3 audio files added to a repository and extracts the
<ulink
url="http://www.id3.org/">ID3</ulink>
+ metadata for the file, including the track's title, author, album name, year,
and comment.
+ After extracting this information from the audio files, the sequencer then writes
this structure into the repository,
+ where it can be further processed, analyzed, searched, navigated, or referenced.
+ </para>
+ <para>
+ To use this sequencer, simply include the <code>dna-sequencer-mp3</code>
JAR and the <ulink
url="http://www.jthink.net/jaudiotagger/">JAudioTagger</u...
+ library in your application and configure the Sequencing Service to use this
sequencer using something similar to:
+ </para>
+ <programlisting>
+String name = "MP3 Sequencer";
+String desc = "Sequences MP3 files to extract the ID3 tags of the audio file";
+String classname = "org.jboss.dna.sequencer.mp3.Mp3MetadataSequencer";
+String[] pathExpressions = {"//(*.mp3[*])/jcr:content[@jcr:data] =>
/mp3s/$1"};
+&SequencerConfig; sequencerConfig = new &SequencerConfig;(name, desc, classname,
+ classpath, pathExpressions);
+this.sequencingService.addSequencer(sequencerConfig);
+</programlisting>
+ </sect2>
+ <sect2 id="dna-sequencer-cnd">
+ <title>JCR Compact Node Definition (CND) file sequencer</title>
+ <para>
+ This sequencer processes JCR Compact Node Definition (CND) files
+ to extract the node definitions with their property definitions, and inserts these
into the repository using JCR standard notation.
+ </para>
+ </sect2>
+ <sect2 id="dna-sequencer-jbpm-jpdl">
+ <title>jBPM JPDL file sequencer</title>
+ <para>
+ This sequencer is incomplete and is not currently usable. The purpose is to
sequence jBPM JPDL files to identify metadata
+ about processes, their actors, and their dependencies.
+ </para>
+ </sect2>
+ <sect2 id="dna-sequencer-xml">
+ <title>XML file sequencer</title>
+ <para>
+ This sequencer stores the structure and data of an XML file into the repository.
+ </para>
+ </sect2>
+
+ </sect1>
+ <sect1 id="custom-sequencers">
+ <title>Creating custom sequencers</title>
+ <para>The current release of JBoss DNA comes with six sequencers. However,
it's very easy to create your own
+ sequencers and to then configure JBoss DNA to use them in your own application.
+ </para>
+ <para>
+ Creating a custom sequencer involves the following steps:
+ <orderedlist>
+ <listitem>
+ <para>Create a Maven 2 project for your sequencer;</para>
+ </listitem>
+ <listitem>
+ <para>Implement the &StreamSequencer; interface with your own
implementation, and create unit tests to verify
+ the functionality and expected behavior;</para>
+ </listitem>
+ <listitem>
+ <para>Add the sequencer configuration to the JBoss DNA
&SequencingService; in your application
+ as described in the <link linkend="using_dna">previous
chapter</link>; and</para>
+ </listitem>
+ <listitem>
+ <para>Deploy the JAR file with your implementation (as well as any
dependencies), and make them available to JBoss DNA
+ in your application.</para>
+ </listitem>
+ </orderedlist>
+ It's that simple.
+ </para>
+ <sect2 id="custom_sequencer_project">
+ <title>Creating the Maven 2 project</title>
+ <para>The first step is to create the Maven 2 project that you can use to
compile your code and build the JARs.
+ Maven 2 automates a lot of the work, and since you're already <link
linkend="maven">set up to use Maven</link>,
+ using Maven for your project will save you a lot of time and effort. Of course, you
don't have to use Maven 2, but then you'll
+ have to get the required libraries and manage the compiling and building process
yourself.</para>
+ <note>
+ <para>JBoss DNA may provide in the future a Maven archetype for creating
sequencer projects. If you'd find this useful
+ and would like to help create it, please <link
linkend="preface">join the community</link>.
+ </para>
+ <para>In lieu of a Maven archetype, you may find it easier to start with a
small existing sequencer project.
+ The <emphasis role="strong">dna-sequencer-images</emphasis>
project is a small, self-contained sequencer implementation that
+ has only the minimal dependencies.
+ See the subversion repository: <ulink
url="&Subversion;trunk/extensions/dna-sequencer-images/">&Subversion;trunk/extensions/dna-sequencer-images/</ulink>
+ </para>
+ </note>
+ <para>You can create your Maven project any way you'd like. For examples,
see the <ulink
url="http://maven.apache.org/guides/getting-started/index.html#How_d...
2 documentation</ulink>.
+ Once you've done that, just add the dependencies in your project's
<code>pom.xml</code> dependencies section:</para>
+ <programlisting role="XML"><![CDATA[
+<dependency>
+ <groupId>org.jboss.dna</groupId>
+ <artifactId>dna-common</artifactId>
+ <version>0.3</version>
+</dependency>
+<dependency>
+ <groupId>org.jboss.dna</groupId>
+ <artifactId>dna-graph</artifactId>
+ <version>0.3</version>
+</dependency>
+ ]]></programlisting>
+ <para>These are minimum dependencies required for compiling a sequencer. Of
course, you'll have to add
+ other dependencies that your sequencer needs.</para>
+ <para>As for testing, you probably will want to add more dependencies, such as
those listed here:</para>
+ <programlisting role="XML"><![CDATA[
+<dependency>
+ <groupId>org.jboss.dna</groupId>
+ <artifactId>dna-graph</artifactId>
+ <version>0.5</version>
+ <type>test-jar</type>
+ <scope>test</scope>
+</dependency>
+<dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <version>4.4</version>
+ <scope>test</scope>
+</dependency>
+<dependency>
+ <groupId>org.hamcrest</groupId>
+ <artifactId>hamcrest-library</artifactId>
+ <version>1.1</version>
+ <scope>test</scope>
+</dependency>
+<!-- Logging with Log4J -->
+<dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-log4j12</artifactId>
+ <version>1.4.3</version>
+ <scope>test</scope>
+</dependency>
+<dependency>
+ <groupId>log4j</groupId>
+ <artifactId>log4j</artifactId>
+ <version>1.2.14</version>
+ <scope>test</scope>
+</dependency>
+ ]]></programlisting>
+ <para>Testing JBoss DNA sequencers does not require a JCR repository or the
JBoss DNA services. (For more detail,
+ see the <link linkend="testing_custom_sequencers">testing
section</link>.) However, if you want to do
+ integration testing with a JCR repository and the JBoss DNA services, you'll
need additional dependencies for these libraries.</para>
+ <programlisting role="XML"><![CDATA[
+<!-- JBoss DNA JCR Repository -->
+<dependency>
+ <groupId>org.jboss.dna</groupId>
+ <artifactId>dna-jcr</artifactId>
+ <version>0.5</version>
+ <scope>test</scope>
+</dependency>
+<!-- Java Content Repository API -->
+<dependency>
+ <groupId>javax.jcr</groupId>
+ <artifactId>jcr</artifactId>
+ <version>1.0.1</version>
+ <scope>test</scope>
+</dependency>
+ ]]></programlisting>
+ <para>At this point, your project should be set up correctly, and you're
ready to move on to
+ <link linkend="custom_sequencer_implementation">writing the Java
implementation</link> for your sequencer.</para>
+ </sect2>
+ <sect2 id="custom_sequencer_implementation">
+ <title>Implementing the StreamSequencer interface</title>
+ <para>After creating the project and setting up the dependencies, the next
step is to create a Java class that implements
+ the &StreamSequencer; interface. This interface is very straightforward and
involves a single method:</para>
+ <programlisting>
+public interface &StreamSequencer; {
+
+ /**
+ * Sequence the data found in the supplied stream, placing the output
+ * information into the supplied map.
+ *
+ * @param stream the stream with the data to be sequenced; never null
+ * @param output the output from the sequencing operation; never null
+ * @param context the context for the sequencing operation; never null
+ */
+ void sequence( &InputStream; stream, &SequencerOutput; output,
&SequencerContext; context );
+}
+</programlisting>
+ <para>The job of a stream sequencer is to process the data in the supplied
stream, and place into the &SequencerOutput;
+ any information that is to go into the JCR repository. JBoss DNA figures out when
your sequencer should be called
+ (of course, using the sequencing configuration you'll add in a bit), and then
makes sure the generated information
+ is saved in the correct place in the repository.
+ </para>
+ <para>The &SequencerContext; provides information about
+ the current sequencing operation, including the location and properties of the node
being sequenced, the MIME type
+ of the node being sequenced, and a location to record problems that aren't
severe enough to warrant throwing an exception.
+ </para>
+ <para>The &SequencerOutput; class is fairly easy to use. There are
basically two methods you need to call.
+ One method sets the property values, while the other sets references to other nodes
in the repository. Use these
+ methods to describe the properties of the nodes you want to create, using relative
paths for the nodes and
+ valid JCR property names for properties and references. JBoss DNA will ensure that
nodes are created or updated
+ whenever they're needed.</para>
+ <programlisting>
+public interface &SequencerOutput; {
+
+ /**
+ * Set the supplied property on the supplied node. The allowable
+ * values are any of the following:
+ * - primitives (which will be autoboxed)
+ * - String instances
+ * - String arrays
+ * - byte arrays
+ * - InputStream instances
+ * - Calendar instances
+ *
+ * @param nodePath the path to the node containing the property;
+ * may not be null
+ * @param property the name of the property to be set
+ * @param values the value(s) for the property; may be empty if
+ * any existing property is to be removed
+ */
+ void setProperty( String nodePath, String property, Object... values );
+
+ /**
+ * Set the supplied reference on the supplied node.
+ *
+ * @param nodePath the path to the node containing the property;
+ * may not be null
+ * @param property the name of the property to be set
+ * @param paths the paths to the referenced property, which may be
+ * absolute paths or relative to the sequencer output node;
+ * may be empty if any existing property is to be removed
+ */
+ void setReference( String nodePath, String property, String... paths );
+}
+</programlisting>
+ <para>JBoss DNA will create nodes of type
<code>nt:unstructured</code> unless you specify the value for the
+ <code>jcr:primaryType</code> property. You can also specify the
values for the <code>jcr:mixinTypes</code> property
+ if you want to add mixins to any node.</para>
+ <para>For a complete example of a sequencer, let's look at the
&ImageMetadataSequencer;
+ implementation:</para>
+ <programlisting>
+public class &ImageMetadataSequencer; implements &StreamSequencer; {
+
+ public static final String METADATA_NODE = "image:metadata";
+ public static final String IMAGE_PRIMARY_TYPE = "jcr:primaryType";
+ public static final String IMAGE_MIXINS = "jcr:mixinTypes";
+ public static final String IMAGE_MIME_TYPE = "jcr:mimeType";
+ public static final String IMAGE_ENCODING = "jcr:encoding";
+ public static final String IMAGE_FORMAT_NAME = "image:formatName";
+ public static final String IMAGE_WIDTH = "image:width";
+ public static final String IMAGE_HEIGHT = "image:height";
+ public static final String IMAGE_BITS_PER_PIXEL = "image:bitsPerPixel";
+ public static final String IMAGE_PROGRESSIVE = "image:progressive";
+ public static final String IMAGE_NUMBER_OF_IMAGES =
"image:numberOfImages";
+ public static final String IMAGE_PHYSICAL_WIDTH_DPI =
"image:physicalWidthDpi";
+ public static final String IMAGE_PHYSICAL_HEIGHT_DPI =
"image:physicalHeightDpi";
+ public static final String IMAGE_PHYSICAL_WIDTH_INCHES =
"image:physicalWidthInches";
+ public static final String IMAGE_PHYSICAL_HEIGHT_INCHES =
"image:physicalHeightInches";
+
+ /**
+ * {@inheritDoc}
+ */
+ public void sequence( &InputStream; stream, &SequencerOutput; output,
+ &SequencerContext; context ) {
+ &ImageMetadata; metadata = new &ImageMetadata;();
+ metadata.setInput(stream);
+ metadata.setDetermineImageNumber(true);
+ metadata.setCollectComments(true);
+
+ // Process the image stream and extract the metadata ...
+ if (!metadata.check()) {
+ metadata = null;
+ }
+ // Generate the output graph if we found useful metadata ...
+ if (metadata != null) {
+ // Place the image metadata into the output map ...
+ output.setProperty(METADATA_NODE, IMAGE_PRIMARY_TYPE,
"image:metadata");
+ // output.psetProperty(METADATA_NODE, IMAGE_MIXINS, "");
+ output.setProperty(METADATA_NODE, IMAGE_MIME_TYPE, metadata.getMimeType());
+ // output.setProperty(METADATA_NODE, IMAGE_ENCODING, "");
+ output.setProperty(METADATA_NODE, IMAGE_FORMAT_NAME,
metadata.getFormatName());
+ output.setProperty(METADATA_NODE, IMAGE_WIDTH, metadata.getWidth());
+ output.setProperty(METADATA_NODE, IMAGE_HEIGHT, metadata.getHeight());
+ output.setProperty(METADATA_NODE, IMAGE_BITS_PER_PIXEL,
metadata.getBitsPerPixel());
+ output.setProperty(METADATA_NODE, IMAGE_PROGRESSIVE,
metadata.isProgressive());
+ output.setProperty(METADATA_NODE, IMAGE_NUMBER_OF_IMAGES,
metadata.getNumberOfImages());
+ output.setProperty(METADATA_NODE, IMAGE_PHYSICAL_WIDTH_DPI,
metadata.getPhysicalWidthDpi());
+ output.setProperty(METADATA_NODE, IMAGE_PHYSICAL_HEIGHT_DPI,
metadata.getPhysicalHeightDpi());
+ output.setProperty(METADATA_NODE, IMAGE_PHYSICAL_WIDTH_INCHES,
metadata.getPhysicalWidthInch());
+ output.setProperty(METADATA_NODE, IMAGE_PHYSICAL_HEIGHT_INCHES,
metadata.getPhysicalHeightInch());
+ }
+ }
+}
+</programlisting>
+ <para>
+ Notice how the image metadata is extracted and the output graph is generated. A
single node is created with the name <code>image:metadata</code>
+ and with the <code>image:metadata</code> node type. No mixins are
defined for the node, but several properties are set on the node
+ using the values obtained from the image metadata. After this method returns, the
constructed graph will be saved to the repository
+ in all of the places defined by its configuration. (This is why only relative paths
are used in the sequencer.)
+ </para>
+ </sect2>
+ <sect2 id="testing_custom_sequencers">
+ <title>Testing custom sequencers</title>
+ <para>The sequencing framework was designed to make testing sequencers much
easier. In particular, the
+ &StreamSequencer; interface does not make use of the JCR API. So instead of
requiring a fully-configured
+ JCR repository and JBoss DNA system, unit tests for a sequencer can focus on testing
that the content is
+ processed correctly and the desired output graph is generated.</para>
+ <note>
+ <para>For a complete example of a sequencer unit test, see the
<code>ImageMetadataSequencerTest</code> unit test
+ in the <code>org.jboss.dna.sequencer.images</code> package of the
<code>dna-sequencers-image</code> project.
+ </para>
+ </note>
+ <para>The following code fragment shows one way of testing a sequencer, using
JUnit 4.4 assertions and
+ some of the classes made available by JBoss DNA. Of course,
+ this example code does not do any error handling and does not make all the
assertions a real test would.</para>
+ <programlisting>
+&StreamSequencer; sequencer = new &ImageMetadataSequencer;();
+&MockSequencerOutput; output = new &MockSequencerOutput;();
+&MockSequencerContext; context = new &MockSequencerContext;();
+&InputStream; stream = null;
+try {
+ stream =
this.getClass().getClassLoader().getResource("caution.gif").openStream();
+ sequencer.sequence(stream,output,context); // writes to 'output'
+ assertThat(output.getPropertyValues("image:metadata",
"jcr:primaryType"),
+ is(new Object[] {"image:metadata"}));
+ assertThat(output.getPropertyValues("image:metadata",
"jcr:mimeType"),
+ is(new Object[] {"image/gif"}));
+ // ... make more assertions here
+ assertThat(output.hasReferences(), is(false));
+} finally {
+ stream.close();
+}
+</programlisting>
+ <para>It's also useful to test that a sequencer produces no output for
something it should not understand:</para>
+ <programlisting>
+&Sequencer; sequencer = new &ImageMetadataSequencer;();
+&MockSequencerOutput; output = new &MockSequencerOutput;();
+&MockSequencerContext; context = new &MockSequencerContext;();
+&InputStream; stream = null;
+try {
+ stream =
this.getClass().getClassLoader().getResource("caution.pict").openStream();
+ sequencer.sequence(stream,output,context); // writes to 'output'
+ assertThat(output.hasProperties(), is(false));
+ assertThat(output.hasReferences(), is(false));
+} finally {
+ stream.close();
+}
+</programlisting>
+ <para>These are just two simple tests that show ways of testing a sequencer.
Some tests may get quite involved,
+ especially if a lot of output data is produced.
+ </para>
+ <para>It may also be useful to create some integration tests
+ that <link linkend="using_dna">configure JBoss DNA</link> to
use a custom sequencer, and to then upload
+ content using the JCR API, verifying that the custom sequencer did run. However,
remember that JBoss DNA
+ runs sequencers asynchronously in the background, and you must synchronize your
tests to ensure that the
+ sequencers have a chance to run before checking the results. (One way of doing this
(although, granted, not always reliable) is to wait for a second
+ after uploading your content, shutdown the &SequencingService; and await its
termination,
+ and then check that the sequencer output has been saved to the JCR repository. For
an example of this technique,
+ see the <code>SequencingClientTest</code> unit test in the example
application.)
+ </para>
+ </sect2>
+ <sect2 id="deploying_custom_sequencers">
+ <title>Deploying custom sequencers</title>
+ <para>The first step of deploying a sequencer consists of adding/changing the
sequencer configuration (e.g., &SequencerConfig;)
+ in the &SequencingService;. This was covered in the <link
linkend="sequencing_service">previous chapter</link>.
+ </para>
+ <para>
+ The second step is to make the sequencer implementation available to JBoss DNA. At
this time, the JAR containing
+ your new sequencer, as well as any JARs that your sequencer depends on, should be
placed on your application classpath.</para>
+ <note>
+ <para>A future goal of JBoss DNA is to allow sequencers, connectors, and
other extensions to be easily deployed into
+ a runtime repository. This process will not only be much simpler, but it will
also provide JBoss DNA
+ with the information necessary to update configurations and create the
appropriate class loaders for each extension.
+ Having separate class loaders for each extension helps prevent the pollution of
the common classpath,
+ facilitates an isolated runtime environment to eliminate any dependency
conflicts, and may potentially
+ enable hot redeployment of newer extension versions.
+ </para>
+ </note>
+ </sect2>
+ </sect1>
+ <sect1>
+ <title>Summary</title>
+ <para>
+ In this chapter, we described how JBoss DNA sequences files as they're uploaded
into a repository.
+ And one of the things we mentioned was that each sequencer is handed (with other
inputs) the MIME type of the file it is to process.
+ How does DNA know what the MIME type is?
+ JBoss DNA uses <emphasis>MIME type detectors</emphasis>, and this is the
topic of the <link linkend="mimetypes">next chapter</link>.
+ </para>
+ </sect1>
+</chapter>
Property changes on:
trunk/docs/reference/src/main/docbook/en-US/content/core/sequencing.xml
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Copied: trunk/docs/reference/src/main/docbook/en-US/content/developers/testing.xml (from
rev 994, trunk/docs/reference/src/main/docbook/en-US/content/testing.xml)
===================================================================
--- trunk/docs/reference/src/main/docbook/en-US/content/developers/testing.xml
(rev 0)
+++ trunk/docs/reference/src/main/docbook/en-US/content/developers/testing.xml 2009-06-08
14:57:17 UTC (rev 995)
@@ -0,0 +1,152 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ JBoss DNA (
http://www.jboss.org/dna)
+ ~
+ ~ See the COPYRIGHT.txt file distributed with this work for information
+ ~ regarding copyright ownership. Some portions may be licensed
+ ~ to Red Hat, Inc. under one or more contributor license agreements.
+ ~ See the AUTHORS.txt file in the distribution for a full listing of
+ ~ individual contributors.
+ ~
+ ~ JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
+ ~ is licensed to you under the terms of the GNU Lesser General Public License as
+ ~ published by the Free Software Foundation; either version 2.1 of
+ ~ the License, or (at your option) any later version.
+ ~
+ ~ JBoss DNA is distributed in the hope that it will be useful,
+ ~ but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ ~ or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ ~ for more details.
+ ~
+ ~ You should have received a copy of the GNU Lesser General Public License
+ ~ along with this distribution; if not, write to:
+ ~ Free Software Foundation, Inc.
+ ~ 51 Franklin Street, Fifth Floor
+ ~ Boston, MA 02110-1301 USA
+ -->
+<!DOCTYPE preface PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
"http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd" [
+<!ENTITY % CustomDTD SYSTEM "../../custom.dtd">
+%CustomDTD;
+]>
+<chapter id="testing">
+ <title>Testing</title>
+ <para>
+ The JBoss DNA project uses automated testing to verify that the software is doing what
it's supposed to
+ and not doing what it shouldn't do. These automated tests are run continuously and
also act as regression tests,
+ ensuring that we known if any problems we find and fix reappear later. All of our
tests are executed as part of
+ our <link linkend="maven">Maven</link> build process, and the
entire build process (including the tests)
+ is automatically run using <link linkend="hudson">Hudson</link>
continuous integration system.
+ </para>
+ <sect1 id="unit-tests">
+ <title>Unit tests</title>
+ <para>
+ <emphasis role="strong">Unit tests</emphasis> verify the
behavior of a single class (or small set of classes) in isolation
+ from other classes.
+ We use the JUnit 4.4 testing framework, which has significant improvements over
earlier versions and makes
+ it very easy to quickly write unit tests with little extra code. We also frequently
use the Mockito library
+ to help create mock implementations of other classes that are not under test but are
used in the tests.
+ </para>
+ <para>
+ Unit tests should generally run quickly and should not require large assemblies of
components. Additionally,
+ they may rely upon the file resources included in the project, but these tests should
require no external resources
+ (like databases or servers). Note that our unit tests are run during the
"test" phase of the standard
+ <ulink
url="http://maven.apache.org/guides/introduction/introduction-to-the...
lifecycle</ulink>.
+ This means that they are executed against the raw .class files created during
complication.
+ </para>
+ <para>
+ Developers are expected to run all of the JBoss DNA unit tests in their local
environment before committing changes to SVN.
+ So, if you're a developer and you've made changes to your local copy of the
source, you can run those tests that are
+ related to your changes using your IDE or with Maven (or any other mechanism). But
before you commit your changes,
+ you are expected to run a full Maven build using <code>mvn clean
install</code> (in the "trunk/" directory).
+ Please do <emphasis>not</emphasis> rely upon continuous integration to run
all of the tests for you - the CI
+ system is there to catch the occasional mistakes and to also run the <link
linkend="integration-tests">integration tests</link>.
+ </para>
+ </sect1>
+ <sect1 id="integration-tests">
+ <title>Integration tests</title>
+ <para>
+ While <link linkend="unit-tests">unit tests</link> test
individual classes in (relative) isolation, the purpose of
+ <emphasis role="strong">integration tests</emphasis> are to
verify that assemblies of classes and components are
+ behaving correctly. These assemblies are often the same ones that end users will
actually use. In fact,
+ integration tests are executed during the "integration-test" phase of the
standard
+ <ulink
url="http://maven.apache.org/guides/introduction/introduction-to-the...
lifecycle</ulink>,
+ meaning they are executed against the packaged JARs and artifacts of the project.
+ </para>
+ <para>
+ Integration tests also use the JUnit 4.4 framework, so they are again easy to write
and follow the same pattern
+ as unit tests. However, because they're working with larger assemblies of
components, they often will take longer
+ to set up, longer to run, and longer to tear down. They also may require initializing
"external resources", like
+ databases or servers.
+ </para>
+ <para>
+ Note, that while external resources may be required, care should be taken to minimize
these dependencies and to
+ ensure that most (if not all) integration tests may be run by anyone who downloads the
source code. This means
+ that these external resources should be available and set up within the tests. For
example, use in-memory databases
+ where possible. Or, if a database is required, use an open-source database (e.g.,
MySQL or PostgreSQL). And when
+ these external resources are not available, it should be obvious from the test class
names and/or test method names
+ that it involved an external resource (e.g.,
"<code>MySqlConnectorIntegrationTest.shouldFindNodeStoredInDatabase()</code>").
+ </para>
+ </sect1>
+ <sect1 id="writing-tests">
+ <title>Writing tests</title>
+ <para>
+ As mentioned in <link linkend="methodology">the
introduction</link>, the JBoss DNA project doesn't follow any one methodology
+ or process. Instead, we simply have a goal that as much code as possible is tested to
ensure it behaves as expected.
+ Do we expect 100% of the code is covered by automated tests? No, but we do want to
test as much as we can.
+ Maybe a simple JavaBean class doesn't need many tests, but any class with
non-trivial logic should be tested.
+ </para>
+ <para>
+ We do encourage writing tests either before or while you write the code. Again,
we're not blindly following a methodology.
+ Instead, there's a very practical reason: writing the tests early on helps you
write classes that are testable.
+ If you wait until after the class (or classes) are done, you'll probably find that
it's not easy to test all
+ of the logic (especially the complicated logic).
+ </para>
+ <para>
+ Another suggestion is to write tests so that they specify and verify the behavior that
is expected from a class or component.
+ One challenge developers often have is knowing what they should even test and what the
tests should look like.
+ This is where <emphasis role="strong"><ulink
url="http://behaviour-driven.org/">Behavior-driven development
(BDD)</ulink></emphasis>
+ helps out. If you think about what a class' behaviors are supposed to be (e.g.,
requirements), simply capture those
+ requirements as test methods (with no implementations). For example, a test class for
sequencer
+ implementation might have a test
method <code>shouldNotThrowAnErrorWhenTheSuppliedStreamIsNull() { }</code>.
Then, after you enumerate
+ all the requirements you can think of, go back and start implementing the test
methods.
+ </para>
+ <para>
+ If you look at the existing test cases, you'll find that the names of the unit and
integration tests in JBoss DNA
+ follow a naming style, where the test method names are readable sentences. Actually,
we try to name the test methods
+ <emphasis>and</emphasis> the test classes such that they form a
concisely-worded requirement. For example,
+ </para>
+ <programlisting>InMemorySequencerTest.shouldNotThrowAnErrorWhenTheSuppliedStreamIsNull()</programlisting>
+ <para>
+ is easily translated into a readable requirement:
+ </para>
+ <programlisting>InMemorySequencer should not throw an error when the supplied
stream is null.</programlisting>
+ <para>
+ In fact, at some point in the future, we'd like to process the source to
automatically generate a list of the behavior specifications
+ that are asserted by the tests.
+ </para>
+ <para>
+ But for now, we write tests - a lot of them. And by following a few simple
conventions and practices, we're able
+ to do it quickly and in a way that makes it easy to understand what the code is
supposed to do (or not do).
+ </para>
+ </sect1>
+ <sect1 id="tck-tests">
+ <title>Technology Compatibility Kit (TCK) tests</title>
+ <para>
+ Many Java specifications provide TCK test suites that can be used to check or verify
that an implementation
+ correctly implements the API or SPI defined by the specification. These TCK tests
vary by technology, but
+ <ulink url="&JSR170;">JSR-170</ulink> does provide TCK tests
that ensure that a JCR repository implementation exhibits the correct and expected
+ behavior.
+ </para>
+ <para>
+ JBoss DNA has not yet passed enough of the TCK tests to publish the results. We
still have to implement
+ queries, which is a required feature of Level 1 repositories. However, suffice to
say that JBoss DNA has passed
+ many of the individual tests that make up the Level 1 and Level 2 tests, and it is a
major objective of the next
+ release to pass the remaining Level 1 and Level 2 tests (along with some other
optional features).
+ </para>
+ <para>
+ JBoss DNA also frequently runs the JCR unit tests from the Apache Jackrabbit project.
(Those these tests are not
+ the official TCK, they apparently are used within the official TCK.) These unit tests
are set up in the
+ <code>dna-jcr-tck</code> project.
+ </para>
+ </sect1>
+</chapter>
Property changes on:
trunk/docs/reference/src/main/docbook/en-US/content/developers/testing.xml
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Copied: trunk/docs/reference/src/main/docbook/en-US/content/developers/tools.xml (from rev
994, trunk/docs/reference/src/main/docbook/en-US/content/development_tools.xml)
===================================================================
--- trunk/docs/reference/src/main/docbook/en-US/content/developers/tools.xml
(rev 0)
+++ trunk/docs/reference/src/main/docbook/en-US/content/developers/tools.xml 2009-06-08
14:57:17 UTC (rev 995)
@@ -0,0 +1,502 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ JBoss DNA (
http://www.jboss.org/dna)
+ ~
+ ~ See the COPYRIGHT.txt file distributed with this work for information
+ ~ regarding copyright ownership. Some portions may be licensed
+ ~ to Red Hat, Inc. under one or more contributor license agreements.
+ ~ See the AUTHORS.txt file in the distribution for a full listing of
+ ~ individual contributors.
+ ~
+ ~ JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
+ ~ is licensed to you under the terms of the GNU Lesser General Public License as
+ ~ published by the Free Software Foundation; either version 2.1 of
+ ~ the License, or (at your option) any later version.
+ ~
+ ~ JBoss DNA is distributed in the hope that it will be useful,
+ ~ but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ ~ or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ ~ for more details.
+ ~
+ ~ You should have received a copy of the GNU Lesser General Public License
+ ~ along with this distribution; if not, write to:
+ ~ Free Software Foundation, Inc.
+ ~ 51 Franklin Street, Fifth Floor
+ ~ Boston, MA 02110-1301 USA
+ -->
+<!DOCTYPE preface PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
"http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd" [
+<!ENTITY % CustomDTD SYSTEM "../../custom.dtd">
+%CustomDTD;
+]>
+<chapter id="development-tools">
+ <title>Developer tools</title>
+ <para>
+ The JBoss DNA project uses <link linkend="maven">Maven</link> as
its primary build tool, <link linkend="svn">Subversion</link>
+ for its source code repository, <link linkend="jira">JIRA</link>
for the issue management and bug tracking system,
+ and <link linkend="hudson">Hudson</link> for the continuous
integration system. We do not stipulate a specific integrated
+ development environment (IDE), although most of us use <link
linkend="eclipse">Eclipse</link> and rely upon the code formatting
+ and compile preferences to ensure no warnings or errors.
+ </para>
+ <para>
+ The rest of this chapter talks in more detail about these different tools and how to
set them up.
+ </para>
+ <sect1 id="jdk">
+ <title>JDK</title>
+ <para>
+ Currently, JBoss DNA is developed and built using <ulink
url="http://java.sun.com/javase/downloads/index_jdk5.jsp">JDK
5</ulink>.
+ So if you're trying to get JBoss DNA to compile locally, you should make sure you
have the JDK 5 installed and are using it.
+ If you're a contributor, you should make sure that you're using JDK 5 before
committing any changes.
+ </para>
+ <note>
+ <para>
+ You should be able to use the <ulink
url="http://java.sun.com/javase/downloads/index.jsp">latest
JDK</ulink>,
+ which is currently JDK 6. It is possible to build JBoss DNA using JDK 6 without any
code changes, but it's
+ not our official JDK (yet).
+ </para>
+ <para>
+ Why do we build using JDK 5 and not 6? The main reason is that if we were to use JDK
6, then JBoss DNA couldn't really be used in any
+ applications or projects that still used JDK 5. Plus, anybody using JDK 6 can still
use JBoss DNA.
+ However, considering that the end-of-life for Java 5 is
+ <ulink
url="http://java.sun.com/products/archive/eol.policy.html">O...
2009</ulink>, we may be switching to
+ Java 6 sometime in 2009.
+ </para>
+ </note>
+ <para>
+ When installing a JDK, simply follow the procedure for your particular platform. On
most platforms, this should set the
+ <code>JAVA_HOME</code> environment variable. But if you run into any
problems, first check that this environment
+ variable was set to the correct location, and then check that you're running the
version you expect by running
+ the following command:
+ </para>
+ <programlisting>$ java -version</programlisting>
+ <para>
+ If you don't see the correct version, double-check your JDK installation.
+ </para>
+ </sect1>
+ <sect1 id="jira">
+ <title>JIRA</title>
+ <para>
+ JBoss DNA uses <ulink url="&JIRA;">JIRA</ulink> as its bug
tracking, issue tracking, and project management tool.
+ This is a browser-based tool, with very good functionality for managing the different
tasks. It also serves as
+ the community's roadmap, since we can define new features and manage them along
side the bugs and other issues.
+ Although most of the issues have been created by community members, we encourage any
users to suggest new features,
+ log defects, or identify shortcomings in JBoss DNA.
+ </para>
+ <para>
+ The JBoss DNA community also encourages its members to work only issues that are
managed in JIRA, and preferably those
+ that are targeted to the current release effort. If something isn't in JIRA but
needs to get done, then create an
+ issue before you start working on the code changes. Once you have code changes, you
can upload a patch to the JIRA issue
+ if the change is complex, if you want someone to review it, or if you don't have
commit privileges and have fixed
+ a bug.
+ </para>
+ </sect1>
+ <sect1 id="svn">
+ <title>Subversion</title>
+ <para>JBoss DNA uses Subversion as its source code management system, and
specifically the instance at
+ <ulink url="http://www.jboss.org">JBoss.org</ulink>. Although
you can view the
+ <ulink url="&Subversion;trunk/">trunk</ulink> of the
Subversion repository directly
+ (or using <ulink url="&Fisheye;trunk">FishEye</ulink>)
through your browser,
+ it order to get more than just a few files of the latest version of the source code,
you probably want
+ to have an SVN client installed. Several IDE's have SVN support included (or
available as plugins),
+ but having the command-line SVN client is recommended. See
+ <ulink
url="http://subversion.tigris.org/">http://subversion.tigris...
for downloads and instructions for your
+ particular platform.
+ </para>
+ <para>
+ Here are some useful URLs for the JBoss DNA Subversion:
+ </para>
+ <table frame='all'>
+ <title>SVN URLs for JBoss DNA</title>
+ <tgroup cols='2' align='left' colsep='1'
rowsep='1'>
+ <colspec colname='c1' colwidth="1*"/>
+ <colspec colname='c2' colwidth="1*"/>
+ <thead>
+ <row>
+ <entry>Repository</entry>
+ <entry>URL</entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry>Anonymous Access URL</entry>
+ <entry><ulink
url="&Subversion;trunk/">&Subversion;trunk/</ulink></entry>
+ </row>
+ <row>
+ <entry>Secure Developer Access URL</entry>
+ <entry><ulink
url="&Fisheye;trunk/">&Fisheye;trunk/</ulink></entry>
+ </row>
+ <row>
+ <entry>FishEye Code Browser</entry>
+ <entry><ulink
url="&SecureSubversion;trunk/">&SecureSubversion;trunk/</ulink></entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+ <para>
+ When committing to SVN, be sure to include in a commit comment that includes the JIRA
issue that the commit applies to and a very
+ good and thorough description of what was done. It only takes a minute or two to be
very clear about the change. And including
+ the JIRA issue (e.g., "DNA-123") in the comment allows the JIRA system to
track the changes that have been made for each issue.
+ </para>
+ <para>
+ Also, any single SVN commit should apply to one and only one JIRA issue. Doing this
helps ensure that each commit is atomic
+ and focused on a single activity. There are exceptions to this rule, but they are
rare.
+ </para>
+ <para>
+ Sometimes you may have some local changes that you don't want to (or aren't
allowed to) commit. You can make a patch file
+ and upload it to the JIRA issue, allowing other committers to review the patch.
However, to ensure that patches are easily
+ applied, please use SVN to create the patch. To do this, simply do the following in
the top of the codebase (e.g., the
+ <code>trunk</code> directory):
+ </para>
+ <programlisting>$ svn diff . > ~/DNA-000.patch</programlisting>
+ <para>
+ where <code>DNA-000</code> represents the DNA issue number. Note that the
above command places the patch file in your home directory,
+ but you can place the patch file anywhere. Then, simply use JIRA to attach the patch
file to the particular issue, also adding
+ a comment that describes the version number against which the patch was created.
+ </para>
+ <para>
+ To apply a patch, you usually want to start with a workspace that has no changes.
Download the patch file, then issue the
+ following command (again, from the top-level of the workspace):
+ </para>
+ <programlisting>$ patch -E -p0 < ~/DNA-000.patch</programlisting>
+ <para>
+ The "-E" option specifies to delete any files that were made empty by the
application of the patch, and the "-p0" option instructs
+ the patch tool to not change any of the paths. After you run this command, your
working area should have the changes defined
+ by the patch.
+ </para>
+ </sect1>
+ <sect1 id="maven">
+ <title>Maven</title>
+ <para>JBoss DNA uses Maven 2 for its build system, as is this example. Using
Maven 2 has several advantages, including
+ the ability to manage dependencies. If a library is needed, Maven automatically
finds and downloads that library, plus
+ everything that library needs. This means that it's very easy to build the
examples - or even create a maven project that
+ depends on the JBoss DNA JARs.</para>
+ <para>
+ To use Maven with JBoss DNA, you'll need to have <link
linkend="jdk">JDK 5 or 6</link> and Maven 2.0.9 (or
higher).</para>
+ <para>
+ Maven can be downloaded from <ulink
url="http://maven.apache.org/">http://maven.apache.org/</...;, and is
installed by unzipping the
+ <code>maven-2.0.9-bin.zip</code> file to a convenient location on your
local disk. Simply add <code>$MAVEN_HOME/bin</code>
+ to your path and add the following profile to your
<code>~/.m2/settings.xml</code> file:
+ </para>
+ <programlisting role="XML"><![CDATA[
+<settings>
+ <profiles>
+ <profile>
+ <id>jboss.repository</id>
+ <activation>
+ <property>
+ <name>!jboss.repository.off</name>
+ </property>
+ </activation>
+ <repositories>
+ <repository>
+ <id>snapshots.jboss.org</id>
+ <
url>http://snapshots.jboss.org/maven2</url>
+ <snapshots>
+ <enabled>true</enabled>
+ </snapshots>
+ </repository>
+ <repository>
+ <id>repository.jboss.org</id>
+ <
url>http://repository.jboss.org/maven2</url>
+ <snapshots>
+ <enabled>false</enabled>
+ </snapshots>
+ </repository>
+ </repositories>
+ <pluginRepositories>
+ <pluginRepository>
+ <id>repository.jboss.org</id>
+ <
url>http://repository.jboss.org/maven2</url>
+ <snapshots>
+ <enabled>false</enabled>
+ </snapshots>
+ </pluginRepository>
+ <pluginRepository>
+ <id>snapshots.jboss.org</id>
+ <
url>http://snapshots.jboss.org/maven2</url>
+ <snapshots>
+ <enabled>true</enabled>
+ </snapshots>
+ </pluginRepository>
+ </pluginRepositories>
+ </profile>
+ </profiles>
+</settings>
+]]></programlisting>
+ <para>This profile informs Maven of the two JBoss repositories (<ulink
url="http://repository.jboss.org/maven2">snapshots</ulink...
+ and <ulink
url="http://snapshots.jboss.org/maven2">releases</ulink>) that contain
all of the JARs for JBoss DNA and all dependent libraries.
+ </para>
+ <para>While you're adding <code>$MAVEN_HOME/bin</code> to your
path, you should also set the <code>$MAVEN_OPTS</code> environment variable
+ to "<code>-Xmx256m</code>". If you don't do this,
you'll likely see an <code>java.lang.OutOfMemoryError</code> sometime
during a full
+ build.
+ </para>
+ <note>
+ <para>
+ The JBoss Maven repository provides a central location for not only the artifacts
produced by the
JBoss.org projects (well, at least those
+ that use Maven), but also is where those projects can place the artifacts that they
depend on. JBoss DNA has a policy that
+ the <emphasis>source code and JARs</emphasis> for
<emphasis>all</emphasis> dependencies <emphasis>must</emphasis> be
loaded into the
+ JBoss Maven repository. It may be a little bit more work for the developers, but it
does help ensure that developers have easy
+ access to the source and that the project (and dependencies) can always be rebuilt
when needed.
+ </para>
+ <para>
+ For more information about the JBoss Maven repository, including instructions for
adding source and JAR artifacts,
+ see the <ulink
url="http://wiki.jboss.org/wiki/Maven">JBoss.org
Wiki</ulink>.
+ </para>
+ </note>
+ <para>
+ There are just a few commands that are useful for building JBoss DNA (and it's
<link linkend="modules">subprojects</link>).
+ Usually, these are issued while at the top level of the code (usually just below
<code>trunk/</code>), although issuing
+ them inside a subproject just applies to that subproject.
+ </para>
+ <table frame='all'>
+ <title>Useful Maven commands</title>
+ <tgroup cols='2' align='left' colsep='1'
rowsep='1'>
+ <colspec colname='c1' colwidth="1*"/>
+ <colspec colname='c2' colwidth="1*"/>
+ <thead>
+ <row>
+ <entry>Command</entry>
+ <entry>Description</entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry><code>mvn clean</code></entry>
+ <entry>Clean up all built artifacts (e.g., the
<code>target/</code> directory in each project)</entry>
+ </row>
+ <row>
+ <entry><code>mvn clean install</code></entry>
+ <entry>Clean up all built artifacts, then compile, run the unit tests, and
install the resulting JAR artifact(s)
+ into your local Maven repository (e.g, usually
<code>~/.m2/repository</code>).
+ </entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+ </sect1>
+ <sect1 id="hudson">
+ <title>Continuous integration with Hudson</title>
+ <para>JBoss DNA's continuous integration is done with several Hudson jobs on
<ulink url="http://www.jboss.org">JBoss.org</ulink>.
+ These jobs run periodically and basically run the Maven build process. Any build
failures or test failures are reported,
+ as are basic statistics and history for each job.
+ </para>
+ <table frame='all'>
+ <title>Continuous integration jobs</title>
+ <tgroup cols='2' align='left' colsep='1'
rowsep='1'>
+ <colspec colname='c1' colwidth="1*"/>
+ <colspec colname='c2' colwidth="1*"/>
+ <thead>
+ <row>
+ <entry>Job</entry>
+ <entry>Description</entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry><ulink
url="http://hudson.jboss.org/hudson/job/DNA%20continuous%20on%20JDK1...
on JDK 5</ulink></entry>
+ <entry>Continuous build that runs after changes are committed to SVN. SVN is
polled every 15 minutes.</entry>
+ </row>
+ <row>
+ <entry><ulink
url="http://hudson.jboss.org/hudson/job/DNA%20nightly%20integration%...
on JDK 5</ulink></entry>
+ <entry>Build that runs every night (usually around 2 a.m. EDT), regardless of
whether changes have been committed to SVN
+ since the previous night.</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+ </sect1>
+ <sect1 id="eclipse">
+ <title>Eclipse IDE</title>
+ <para>Many of the JBoss DNA committers use the Eclipse IDE, and all project files
required by Eclipse are committed in SVN, making
+ it pretty easy to get an Eclipse workspace running with all of the JBoss DNA
projects.
+ Many of the JBoss DNA committers use the Eclipse IDE, and all project files required
by Eclipse are committed in SVN, making
+ it pretty easy to get an Eclipse workspace running with all of the JBoss DNA
projects.
+ </para>
+ <para>We're using the latest released version of Eclipse (3.4, called
"Ganymede"),
+ available from <ulink
url="http://www.eclipse.org/">Eclipse.org</ulink>. Simply follow the
instructions for your platform.
+ </para>
+ <para>
+ After Eclipse is installed, create a new workspace. Before importing the JBoss DNA
projects, import (via
+ <emphasis
role="strong">File->Import->Preferences</emphasis>) the subset of
the Eclipse preferences by importing the
+ <code>eclipse-preferences.epf</code> file (located under
<code>trunk</code>). Then, open the Eclipse preferences and
+ open the <emphasis role="strong">Java->Code Style->
Formatter</emphasis> preference page, and press the "Import" button and
+ choose the <code>eclipse-code-formatter-profile.xml</code> file (also
located under <code>trunk</code>). This will load the code
+ formatting preferences for the JBoss DNA project.
+ </para>
+ <para>
+ Then install Eclipse plugins for SVN and Maven. (Remember, you will have to restart
Eclipse after installing them.)
+ We use the following plugins:
+ </para>
+ <table frame='all'>
+ <title>Eclipse Subversion Plugins</title>
+ <tgroup cols='2' align='left' colsep='1'
rowsep='1'>
+ <colspec colname='c1' colwidth="1*"/>
+ <colspec colname='c2' colwidth="1*"/>
+ <thead>
+ <row>
+ <entry>Eclipse Plugins</entry>
+ <entry>Update Site URLs</entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry>Subversive SVN Client</entry>
+ <entry>
+ <ulink
url="http://www.polarion.org/projects/subversive/download/eclipse/2....
+ <ulink
url="http://www.polarion.org/projects/subversive/download/integratio...
+ </entry>
+ </row>
+ <row>
+ <entry>Maven Integration for Eclipse</entry>
+ <entry><ulink
url="http://m2eclipse.sonatype.org/update/">http://m2eclipse...
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+ <para>
+ After you check out the JBoss DNA codebase, you can import the JBoss DNA Maven
projects into Eclipse as Eclipse projects.
+ To do this, go to "File->Import->Existing Projects", navigate to the
<code>trunk/</code> folder in the import wizard,
+ and then check each of the <link
linkend="modules">subprojects</link> that you want to have in your
workspace.
+ Don't forget about the projects under <code>extensions/</code> or
<code>docs/</code>.
+ </para>
+ </sect1>
+ <sect1 id="releasing">
+ <title>Releasing</title>
+ <para>
+ This section outlines the basic process of releasing JBoss DNA. This <emphasis
role="strong">must</emphasis> be done
+ either by the project lead or only after communicating with the project lead.
+ </para>
+ <para>
+ Before continuing, your local workspace should contain no changes and should be a
perfect reflection of Subversion.
+ You can verify this by getting the latest from Subversion
+ </para>
+ <programlisting>$ svn update</programlisting>
+ <para>
+ and ensuring that you have no additional changes with
+ </para>
+ <programlisting>$ svn status</programlisting>
+ <para>
+ You may also want to note the revision number for use later on in the process. The
release number is returned by
+ the <code>svn update</code> command, but may also be found using
+ </para>
+ <programlisting>$ svn info</programlisting>
+ <para>
+ At this point, you're ready to verify that everything builds normally.
+ </para>
+ <sect2 id="build-all-artifacts-and-assemblies">
+ <title>Building all artifacts and assemblies</title>
+ <para>
+ By default, the project's Maven build process is does
<emphasis>not</emphasis> build the documentation, JavaDocs, or assemblies.
+ These take extra time, and most of our builds don't require them. So the first
step of releasing JBoss DNA
+ is to use Maven to build all of regular artifacts (e.g., JARs) and these extra
documents and assemblies.
+ </para>
+ <note>
+ <para>
+ Before running Maven commands to build the releases, increase the memory available
to Maven with this command:
+ <code>$ export MAVEN_OPTS=-Xmx256m</code>
+ </para>
+ </note>
+ <para>
+ To perform this complete build, issue the following command while in the
<code>target/</code> directory:
+ </para>
+ <programlisting>$ mvn -P assembly clean javadoc:javadoc
install</programlisting>
+ <para>
+ This command runs the "clean", "javadoc:javadoc", and
"install" goals using the "assembly" profile,
+ which adds the production of JavaDocs, the Getting Started document, the Reference
Guide document,
+ the Getting Started examples, and several ZIP archives. The order of the goals is
important,
+ since the "install" goal attempts to include the JavaDoc in the archives.
+ </para>
+ <para>
+ After this build has completed, verify that the assemblies under
<code>target/</code> have actually been created and that
+ they contain the correct information.
+ At this point, we know that the actual Maven build process is building
+ everything we want and will complete without errors. We can now proceed with
preparing for the release.
+ </para>
+ </sect2>
+ <sect2 id="determine-version">
+ <title>Determine the version to be released</title>
+ <para>
+ The version being released should match the <ulink
url="&JIRA;">JIRA</ulink> road map. Make sure that all issues
related to the release are closed.
+ The project lead should be notified and approve that the release is taking place.
+ </para>
+ </sect2>
+ <sect2 id="release-dry-run">
+ <title>Release dry run</title>
+ <para>
+ The next step is to ensure that all information in the POM is correct and contains
all the information required for
+ the release process. This is called a <emphasis>dry run</emphasis>, and
is done with the Maven "release" plugin:
+ </para>
+ <programlisting>$ mvn -Passembly release:prepare
-DdryRun=true</programlisting>
+ <para>
+ This may download a lot of Maven plugins if they already haven't been downloaded,
but it will eventually prompt you for
+ the release version of each of the Maven projects, the tag name for the release, and
the next development versions
+ (again for each of the Maven projects). The default values are probably acceptable;
if not, then check that the
+ "<code><version></code>" tags in each of the POM
files is correct and end with "-SNAPSHOT".
+ </para>
+ <para>
+ After the dry run completes you should clean up the files that the release plugin
created in the dry run:
+ </para>
+ <programlisting>$ mvn -Passembly release:clean</programlisting>
+ </sect2>
+ <sect2 id="prepare-release">
+ <title>Prepare for the release</title>
+ <para>
+ Run the prepare step (without the <code>dryRun</code> option):
+ </para>
+ <programlisting>$ mvn -Passembly release:prepare</programlisting>
+ <para>
+ You will again be prompted for the release versions and tag name. These should be the
same as what was used during the dry run.
+ This will run the same steps as the dry run, with the additional step of tagging the
release in SVN.
+ </para>
+ <para>
+ If there are any problems during this step, you should go back and try the dry run
option. But after this runs successfully,
+ the release will be tagged in SVN, and the <code>pom.xml</code> files in
SVN under <code>/trunk</code> will have the
+ next version in the "<version>" values.
+ However, the artifacts for the release are not yet published. That's the next
step.
+ </para>
+ </sect2>
+ <sect2 id="release">
+ <title>Perform the release</title>
+ <para>
+ At this point, the release's artifacts need to be published to the JBoss Maven
repository. This next command check outs the
+ files from the release tag created earlier (into a
<code>trunk/target/checkout</code> directory), runs a build, and then
+ deploys the generated artifacts. Note that this ensures that the artifacts are built
from the tagged code.
+ </para>
+ <programlisting>$ mvn release:perform
-DuseReleaseProfile=false</programlisting>
+ <note>
+ <para>
+ If during this process you get an error finding the released artifacts in your local
Maven repository, you may
+ need to go into the <code>trunk/target/checkout</code> folder and run
<code>$ mvn install</code>. This is a simple
+ workaround to make the artifacts available locally. Another option to try is adding
<code>-Dgoals=install,assembly</code>
+ to the <code>$ mvn release:perform... </code> command above.
+ </para>
+ </note>
+ <para>
+ The artifacts are deployed to the local file system, which is comprised of a local
checkout of the JBoss Maven2 repository
+ in a location specified by a combination of the
<code><distributionManagement></code> section of several
<code>pom.xml</code>
+ files and your personal <code>settings.xml</code> file. Once this Maven
command completes, you will need to
+ commit the new files after they are deployed. For more information, see the
+ <ulink
url="http://wiki.jboss.org/wiki/Maven">JBoss
wiki</ulink>.
+ </para>
+ <para>
+ At this point, the software has been released and tagged, and it's been deployed
to a local checked-out copy of the
+ JBoss DNA Maven 2 repository (via the "<distribution>"
section of the pom.xml files). Those need to be committed
+ into the Maven 2 repository using SVN. And finally, the last thing is to publish the
release onto
+ the project's <ulink
url="&Downloads;">downloads</ulink> and <ulink
url="&Home;/docs">documentation</ulink> pages.
+ </para>
+ <para>
+ The assemblies of the source, binaries, etc. also need to be published onto the
&Downloads; area of the
+ <ulink
url="http://www.jboss.org/dna">the project page</ulink>.
This process is expected to change, as <ulink
url="http://www.jboss.org">JBoss.org</ulink>
+ improves its infrastructure.
+ </para>
+ </sect2>
+ </sect1>
+ <sect1>
+ <title>Summary</title>
+ <para>
+ In this chapter, we described the various aspects of developing code for the JBoss DNA
project. Before we start talking
+ about some of the details of JBoss DNA repositories, connectors, and sequencers,
we'll first talk about
+ some very ubiquitous information: how does JBoss DNA load all of the extension
classes?
+ This is the topic of the <link linkend="classloaders">next
chapter</link>.
+ </para>
+ </sect1>
+</chapter>
Property changes on:
trunk/docs/reference/src/main/docbook/en-US/content/developers/tools.xml
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Deleted: trunk/docs/reference/src/main/docbook/en-US/content/development_tools.xml
===================================================================
--- trunk/docs/reference/src/main/docbook/en-US/content/development_tools.xml 2009-06-08
00:52:09 UTC (rev 994)
+++ trunk/docs/reference/src/main/docbook/en-US/content/development_tools.xml 2009-06-08
14:57:17 UTC (rev 995)
@@ -1,502 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ JBoss DNA (
http://www.jboss.org/dna)
- ~
- ~ See the COPYRIGHT.txt file distributed with this work for information
- ~ regarding copyright ownership. Some portions may be licensed
- ~ to Red Hat, Inc. under one or more contributor license agreements.
- ~ See the AUTHORS.txt file in the distribution for a full listing of
- ~ individual contributors.
- ~
- ~ JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
- ~ is licensed to you under the terms of the GNU Lesser General Public License as
- ~ published by the Free Software Foundation; either version 2.1 of
- ~ the License, or (at your option) any later version.
- ~
- ~ JBoss DNA is distributed in the hope that it will be useful,
- ~ but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- ~ or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
- ~ for more details.
- ~
- ~ You should have received a copy of the GNU Lesser General Public License
- ~ along with this distribution; if not, write to:
- ~ Free Software Foundation, Inc.
- ~ 51 Franklin Street, Fifth Floor
- ~ Boston, MA 02110-1301 USA
- -->
-<!DOCTYPE preface PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
"http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd" [
-<!ENTITY % CustomDTD SYSTEM "../custom.dtd">
-%CustomDTD;
-]>
-<chapter id="development-tools">
- <title>Developer tools</title>
- <para>
- The JBoss DNA project uses <link linkend="maven">Maven</link> as
its primary build tool, <link linkend="svn">Subversion</link>
- for its source code repository, <link linkend="jira">JIRA</link>
for the issue management and bug tracking system,
- and <link linkend="hudson">Hudson</link> for the continuous
integration system. We do not stipulate a specific integrated
- development environment (IDE), although most of us use <link
linkend="eclipse">Eclipse</link> and rely upon the code formatting
- and compile preferences to ensure no warnings or errors.
- </para>
- <para>
- The rest of this chapter talks in more detail about these different tools and how to
set them up.
- </para>
- <sect1 id="jdk">
- <title>JDK</title>
- <para>
- Currently, JBoss DNA is developed and built using <ulink
url="http://java.sun.com/javase/downloads/index_jdk5.jsp">JDK
5</ulink>.
- So if you're trying to get JBoss DNA to compile locally, you should make sure you
have the JDK 5 installed and are using it.
- If you're a contributor, you should make sure that you're using JDK 5 before
committing any changes.
- </para>
- <note>
- <para>
- You should be able to use the <ulink
url="http://java.sun.com/javase/downloads/index.jsp">latest
JDK</ulink>,
- which is currently JDK 6. It is possible to build JBoss DNA using JDK 6 without any
code changes, but it's
- not our official JDK (yet).
- </para>
- <para>
- Why do we build using JDK 5 and not 6? The main reason is that if we were to use JDK
6, then JBoss DNA couldn't really be used in any
- applications or projects that still used JDK 5. Plus, anybody using JDK 6 can still
use JBoss DNA.
- However, considering that the end-of-life for Java 5 is
- <ulink
url="http://java.sun.com/products/archive/eol.policy.html">O...
2009</ulink>, we may be switching to
- Java 6 sometime in 2009.
- </para>
- </note>
- <para>
- When installing a JDK, simply follow the procedure for your particular platform. On
most platforms, this should set the
- <code>JAVA_HOME</code> environment variable. But if you run into any
problems, first check that this environment
- variable was set to the correct location, and then check that you're running the
version you expect by running
- the following command:
- </para>
- <programlisting>$ java -version</programlisting>
- <para>
- If you don't see the correct version, double-check your JDK installation.
- </para>
- </sect1>
- <sect1 id="jira">
- <title>JIRA</title>
- <para>
- JBoss DNA uses <ulink url="&JIRA;">JIRA</ulink> as its bug
tracking, issue tracking, and project management tool.
- This is a browser-based tool, with very good functionality for managing the different
tasks. It also serves as
- the community's roadmap, since we can define new features and manage them along
side the bugs and other issues.
- Although most of the issues have been created by community members, we encourage any
users to suggest new features,
- log defects, or identify shortcomings in JBoss DNA.
- </para>
- <para>
- The JBoss DNA community also encourages its members to work only issues that are
managed in JIRA, and preferably those
- that are targeted to the current release effort. If something isn't in JIRA but
needs to get done, then create an
- issue before you start working on the code changes. Once you have code changes, you
can upload a patch to the JIRA issue
- if the change is complex, if you want someone to review it, or if you don't have
commit privileges and have fixed
- a bug.
- </para>
- </sect1>
- <sect1 id="svn">
- <title>Subversion</title>
- <para>JBoss DNA uses Subversion as its source code management system, and
specifically the instance at
- <ulink url="http://www.jboss.org">JBoss.org</ulink>. Although
you can view the
- <ulink url="&Subversion;trunk/">trunk</ulink> of the
Subversion repository directly
- (or using <ulink url="&Fisheye;trunk">FishEye</ulink>)
through your browser,
- it order to get more than just a few files of the latest version of the source code,
you probably want
- to have an SVN client installed. Several IDE's have SVN support included (or
available as plugins),
- but having the command-line SVN client is recommended. See
- <ulink
url="http://subversion.tigris.org/">http://subversion.tigris...
for downloads and instructions for your
- particular platform.
- </para>
- <para>
- Here are some useful URLs for the JBoss DNA Subversion:
- </para>
- <table frame='all'>
- <title>SVN URLs for JBoss DNA</title>
- <tgroup cols='2' align='left' colsep='1'
rowsep='1'>
- <colspec colname='c1' colwidth="1*"/>
- <colspec colname='c2' colwidth="1*"/>
- <thead>
- <row>
- <entry>Repository</entry>
- <entry>URL</entry>
- </row>
- </thead>
- <tbody>
- <row>
- <entry>Anonymous Access URL</entry>
- <entry><ulink
url="&Subversion;trunk/">&Subversion;trunk/</ulink></entry>
- </row>
- <row>
- <entry>Secure Developer Access URL</entry>
- <entry><ulink
url="&Fisheye;trunk/">&Fisheye;trunk/</ulink></entry>
- </row>
- <row>
- <entry>FishEye Code Browser</entry>
- <entry><ulink
url="&SecureSubversion;trunk/">&SecureSubversion;trunk/</ulink></entry>
- </row>
- </tbody>
- </tgroup>
- </table>
- <para>
- When committing to SVN, be sure to include in a commit comment that includes the JIRA
issue that the commit applies to and a very
- good and thorough description of what was done. It only takes a minute or two to be
very clear about the change. And including
- the JIRA issue (e.g., "DNA-123") in the comment allows the JIRA system to
track the changes that have been made for each issue.
- </para>
- <para>
- Also, any single SVN commit should apply to one and only one JIRA issue. Doing this
helps ensure that each commit is atomic
- and focused on a single activity. There are exceptions to this rule, but they are
rare.
- </para>
- <para>
- Sometimes you may have some local changes that you don't want to (or aren't
allowed to) commit. You can make a patch file
- and upload it to the JIRA issue, allowing other committers to review the patch.
However, to ensure that patches are easily
- applied, please use SVN to create the patch. To do this, simply do the following in
the top of the codebase (e.g., the
- <code>trunk</code> directory):
- </para>
- <programlisting>$ svn diff . > ~/DNA-000.patch</programlisting>
- <para>
- where <code>DNA-000</code> represents the DNA issue number. Note that the
above command places the patch file in your home directory,
- but you can place the patch file anywhere. Then, simply use JIRA to attach the patch
file to the particular issue, also adding
- a comment that describes the version number against which the patch was created.
- </para>
- <para>
- To apply a patch, you usually want to start with a workspace that has no changes.
Download the patch file, then issue the
- following command (again, from the top-level of the workspace):
- </para>
- <programlisting>$ patch -E -p0 < ~/DNA-000.patch</programlisting>
- <para>
- The "-E" option specifies to delete any files that were made empty by the
application of the patch, and the "-p0" option instructs
- the patch tool to not change any of the paths. After you run this command, your
working area should have the changes defined
- by the patch.
- </para>
- </sect1>
- <sect1 id="maven">
- <title>Maven</title>
- <para>JBoss DNA uses Maven 2 for its build system, as is this example. Using
Maven 2 has several advantages, including
- the ability to manage dependencies. If a library is needed, Maven automatically
finds and downloads that library, plus
- everything that library needs. This means that it's very easy to build the
examples - or even create a maven project that
- depends on the JBoss DNA JARs.</para>
- <para>
- To use Maven with JBoss DNA, you'll need to have <link
linkend="jdk">JDK 5 or 6</link> and Maven 2.0.9 (or
higher).</para>
- <para>
- Maven can be downloaded from <ulink
url="http://maven.apache.org/">http://maven.apache.org/</...;, and is
installed by unzipping the
- <code>maven-2.0.9-bin.zip</code> file to a convenient location on your
local disk. Simply add <code>$MAVEN_HOME/bin</code>
- to your path and add the following profile to your
<code>~/.m2/settings.xml</code> file:
- </para>
- <programlisting role="XML"><![CDATA[
-<settings>
- <profiles>
- <profile>
- <id>jboss.repository</id>
- <activation>
- <property>
- <name>!jboss.repository.off</name>
- </property>
- </activation>
- <repositories>
- <repository>
- <id>snapshots.jboss.org</id>
- <
url>http://snapshots.jboss.org/maven2</url>
- <snapshots>
- <enabled>true</enabled>
- </snapshots>
- </repository>
- <repository>
- <id>repository.jboss.org</id>
- <
url>http://repository.jboss.org/maven2</url>
- <snapshots>
- <enabled>false</enabled>
- </snapshots>
- </repository>
- </repositories>
- <pluginRepositories>
- <pluginRepository>
- <id>repository.jboss.org</id>
- <
url>http://repository.jboss.org/maven2</url>
- <snapshots>
- <enabled>false</enabled>
- </snapshots>
- </pluginRepository>
- <pluginRepository>
- <id>snapshots.jboss.org</id>
- <
url>http://snapshots.jboss.org/maven2</url>
- <snapshots>
- <enabled>true</enabled>
- </snapshots>
- </pluginRepository>
- </pluginRepositories>
- </profile>
- </profiles>
-</settings>
-]]></programlisting>
- <para>This profile informs Maven of the two JBoss repositories (<ulink
url="http://repository.jboss.org/maven2">snapshots</ulink...
- and <ulink
url="http://snapshots.jboss.org/maven2">releases</ulink>) that contain
all of the JARs for JBoss DNA and all dependent libraries.
- </para>
- <para>While you're adding <code>$MAVEN_HOME/bin</code> to your
path, you should also set the <code>$MAVEN_OPTS</code> environment variable
- to "<code>-Xmx256m</code>". If you don't do this,
you'll likely see an <code>java.lang.OutOfMemoryError</code> sometime
during a full
- build.
- </para>
- <note>
- <para>
- The JBoss Maven repository provides a central location for not only the artifacts
produced by the
JBoss.org projects (well, at least those
- that use Maven), but also is where those projects can place the artifacts that they
depend on. JBoss DNA has a policy that
- the <emphasis>source code and JARs</emphasis> for
<emphasis>all</emphasis> dependencies <emphasis>must</emphasis> be
loaded into the
- JBoss Maven repository. It may be a little bit more work for the developers, but it
does help ensure that developers have easy
- access to the source and that the project (and dependencies) can always be rebuilt
when needed.
- </para>
- <para>
- For more information about the JBoss Maven repository, including instructions for
adding source and JAR artifacts,
- see the <ulink
url="http://wiki.jboss.org/wiki/Maven">JBoss.org
Wiki</ulink>.
- </para>
- </note>
- <para>
- There are just a few commands that are useful for building JBoss DNA (and it's
<link linkend="modules">subprojects</link>).
- Usually, these are issued while at the top level of the code (usually just below
<code>trunk/</code>), although issuing
- them inside a subproject just applies to that subproject.
- </para>
- <table frame='all'>
- <title>Useful Maven commands</title>
- <tgroup cols='2' align='left' colsep='1'
rowsep='1'>
- <colspec colname='c1' colwidth="1*"/>
- <colspec colname='c2' colwidth="1*"/>
- <thead>
- <row>
- <entry>Command</entry>
- <entry>Description</entry>
- </row>
- </thead>
- <tbody>
- <row>
- <entry><code>mvn clean</code></entry>
- <entry>Clean up all built artifacts (e.g., the
<code>target/</code> directory in each project)</entry>
- </row>
- <row>
- <entry><code>mvn clean install</code></entry>
- <entry>Clean up all built artifacts, then compile, run the unit tests, and
install the resulting JAR artifact(s)
- into your local Maven repository (e.g, usually
<code>~/.m2/repository</code>).
- </entry>
- </row>
- </tbody>
- </tgroup>
- </table>
- </sect1>
- <sect1 id="hudson">
- <title>Continuous integration with Hudson</title>
- <para>JBoss DNA's continuous integration is done with several Hudson jobs on
<ulink url="http://www.jboss.org">JBoss.org</ulink>.
- These jobs run periodically and basically run the Maven build process. Any build
failures or test failures are reported,
- as are basic statistics and history for each job.
- </para>
- <table frame='all'>
- <title>Continuous integration jobs</title>
- <tgroup cols='2' align='left' colsep='1'
rowsep='1'>
- <colspec colname='c1' colwidth="1*"/>
- <colspec colname='c2' colwidth="1*"/>
- <thead>
- <row>
- <entry>Job</entry>
- <entry>Description</entry>
- </row>
- </thead>
- <tbody>
- <row>
- <entry><ulink
url="http://hudson.jboss.org/hudson/job/DNA%20continuous%20on%20JDK1...
on JDK 5</ulink></entry>
- <entry>Continuous build that runs after changes are committed to SVN. SVN is
polled every 15 minutes.</entry>
- </row>
- <row>
- <entry><ulink
url="http://hudson.jboss.org/hudson/job/DNA%20nightly%20integration%...
on JDK 5</ulink></entry>
- <entry>Build that runs every night (usually around 2 a.m. EDT), regardless of
whether changes have been committed to SVN
- since the previous night.</entry>
- </row>
- </tbody>
- </tgroup>
- </table>
- </sect1>
- <sect1 id="eclipse">
- <title>Eclipse IDE</title>
- <para>Many of the JBoss DNA committers use the Eclipse IDE, and all project files
required by Eclipse are committed in SVN, making
- it pretty easy to get an Eclipse workspace running with all of the JBoss DNA
projects.
- Many of the JBoss DNA committers use the Eclipse IDE, and all project files required
by Eclipse are committed in SVN, making
- it pretty easy to get an Eclipse workspace running with all of the JBoss DNA
projects.
- </para>
- <para>We're using the latest released version of Eclipse (3.4, called
"Ganymede"),
- available from <ulink
url="http://www.eclipse.org/">Eclipse.org</ulink>. Simply follow the
instructions for your platform.
- </para>
- <para>
- After Eclipse is installed, create a new workspace. Before importing the JBoss DNA
projects, import (via
- <emphasis
role="strong">File->Import->Preferences</emphasis>) the subset of
the Eclipse preferences by importing the
- <code>eclipse-preferences.epf</code> file (located under
<code>trunk</code>). Then, open the Eclipse preferences and
- open the <emphasis role="strong">Java->Code Style->
Formatter</emphasis> preference page, and press the "Import" button and
- choose the <code>eclipse-code-formatter-profile.xml</code> file (also
located under <code>trunk</code>). This will load the code
- formatting preferences for the JBoss DNA project.
- </para>
- <para>
- Then install Eclipse plugins for SVN and Maven. (Remember, you will have to restart
Eclipse after installing them.)
- We use the following plugins:
- </para>
- <table frame='all'>
- <title>Eclipse Subversion Plugins</title>
- <tgroup cols='2' align='left' colsep='1'
rowsep='1'>
- <colspec colname='c1' colwidth="1*"/>
- <colspec colname='c2' colwidth="1*"/>
- <thead>
- <row>
- <entry>Eclipse Plugins</entry>
- <entry>Update Site URLs</entry>
- </row>
- </thead>
- <tbody>
- <row>
- <entry>Subversive SVN Client</entry>
- <entry>
- <ulink
url="http://www.polarion.org/projects/subversive/download/eclipse/2....
- <ulink
url="http://www.polarion.org/projects/subversive/download/integratio...
- </entry>
- </row>
- <row>
- <entry>Maven Integration for Eclipse</entry>
- <entry><ulink
url="http://m2eclipse.sonatype.org/update/">http://m2eclipse...
- </row>
- </tbody>
- </tgroup>
- </table>
- <para>
- After you check out the JBoss DNA codebase, you can import the JBoss DNA Maven
projects into Eclipse as Eclipse projects.
- To do this, go to "File->Import->Existing Projects", navigate to the
<code>trunk/</code> folder in the import wizard,
- and then check each of the <link
linkend="modules">subprojects</link> that you want to have in your
workspace.
- Don't forget about the projects under <code>extensions/</code> or
<code>docs/</code>.
- </para>
- </sect1>
- <sect1 id="releasing">
- <title>Releasing</title>
- <para>
- This section outlines the basic process of releasing JBoss DNA. This <emphasis
role="strong">must</emphasis> be done
- either by the project lead or only after communicating with the project lead.
- </para>
- <para>
- Before continuing, your local workspace should contain no changes and should be a
perfect reflection of Subversion.
- You can verify this by getting the latest from Subversion
- </para>
- <programlisting>$ svn update</programlisting>
- <para>
- and ensuring that you have no additional changes with
- </para>
- <programlisting>$ svn status</programlisting>
- <para>
- You may also want to note the revision number for use later on in the process. The
release number is returned by
- the <code>svn update</code> command, but may also be found using
- </para>
- <programlisting>$ svn info</programlisting>
- <para>
- At this point, you're ready to verify that everything builds normally.
- </para>
- <sect2 id="build-all-artifacts-and-assemblies">
- <title>Building all artifacts and assemblies</title>
- <para>
- By default, the project's Maven build process is does
<emphasis>not</emphasis> build the documentation, JavaDocs, or assemblies.
- These take extra time, and most of our builds don't require them. So the first
step of releasing JBoss DNA
- is to use Maven to build all of regular artifacts (e.g., JARs) and these extra
documents and assemblies.
- </para>
- <note>
- <para>
- Before running Maven commands to build the releases, increase the memory available
to Maven with this command:
- <code>$ export MAVEN_OPTS=-Xmx256m</code>
- </para>
- </note>
- <para>
- To perform this complete build, issue the following command while in the
<code>target/</code> directory:
- </para>
- <programlisting>$ mvn -P assembly clean javadoc:javadoc
install</programlisting>
- <para>
- This command runs the "clean", "javadoc:javadoc", and
"install" goals using the "assembly" profile,
- which adds the production of JavaDocs, the Getting Started document, the Reference
Guide document,
- the Getting Started examples, and several ZIP archives. The order of the goals is
important,
- since the "install" goal attempts to include the JavaDoc in the archives.
- </para>
- <para>
- After this build has completed, verify that the assemblies under
<code>target/</code> have actually been created and that
- they contain the correct information.
- At this point, we know that the actual Maven build process is building
- everything we want and will complete without errors. We can now proceed with
preparing for the release.
- </para>
- </sect2>
- <sect2 id="determine-version">
- <title>Determine the version to be released</title>
- <para>
- The version being released should match the <ulink
url="&JIRA;">JIRA</ulink> road map. Make sure that all issues
related to the release are closed.
- The project lead should be notified and approve that the release is taking place.
- </para>
- </sect2>
- <sect2 id="release-dry-run">
- <title>Release dry run</title>
- <para>
- The next step is to ensure that all information in the POM is correct and contains
all the information required for
- the release process. This is called a <emphasis>dry run</emphasis>, and
is done with the Maven "release" plugin:
- </para>
- <programlisting>$ mvn -Passembly release:prepare
-DdryRun=true</programlisting>
- <para>
- This may download a lot of Maven plugins if they already haven't been downloaded,
but it will eventually prompt you for
- the release version of each of the Maven projects, the tag name for the release, and
the next development versions
- (again for each of the Maven projects). The default values are probably acceptable;
if not, then check that the
- "<code><version></code>" tags in each of the POM
files is correct and end with "-SNAPSHOT".
- </para>
- <para>
- After the dry run completes you should clean up the files that the release plugin
created in the dry run:
- </para>
- <programlisting>$ mvn -Passembly release:clean</programlisting>
- </sect2>
- <sect2 id="prepare-release">
- <title>Prepare for the release</title>
- <para>
- Run the prepare step (without the <code>dryRun</code> option):
- </para>
- <programlisting>$ mvn -Passembly release:prepare</programlisting>
- <para>
- You will again be prompted for the release versions and tag name. These should be the
same as what was used during the dry run.
- This will run the same steps as the dry run, with the additional step of tagging the
release in SVN.
- </para>
- <para>
- If there are any problems during this step, you should go back and try the dry run
option. But after this runs successfully,
- the release will be tagged in SVN, and the <code>pom.xml</code> files in
SVN under <code>/trunk</code> will have the
- next version in the "<version>" values.
- However, the artifacts for the release are not yet published. That's the next
step.
- </para>
- </sect2>
- <sect2 id="release">
- <title>Perform the release</title>
- <para>
- At this point, the release's artifacts need to be published to the JBoss Maven
repository. This next command check outs the
- files from the release tag created earlier (into a
<code>trunk/target/checkout</code> directory), runs a build, and then
- deploys the generated artifacts. Note that this ensures that the artifacts are built
from the tagged code.
- </para>
- <programlisting>$ mvn release:perform
-DuseReleaseProfile=false</programlisting>
- <note>
- <para>
- If during this process you get an error finding the released artifacts in your local
Maven repository, you may
- need to go into the <code>trunk/target/checkout</code> folder and run
<code>$ mvn install</code>. This is a simple
- workaround to make the artifacts available locally. Another option to try is adding
<code>-Dgoals=install,assembly</code>
- to the <code>$ mvn release:perform... </code> command above.
- </para>
- </note>
- <para>
- The artifacts are deployed to the local file system, which is comprised of a local
checkout of the JBoss Maven2 repository
- in a location specified by a combination of the
<code><distributionManagement></code> section of several
<code>pom.xml</code>
- files and your personal <code>settings.xml</code> file. Once this Maven
command completes, you will need to
- commit the new files after they are deployed. For more information, see the
- <ulink
url="http://wiki.jboss.org/wiki/Maven">JBoss
wiki</ulink>.
- </para>
- <para>
- At this point, the software has been released and tagged, and it's been deployed
to a local checked-out copy of the
- JBoss DNA Maven 2 repository (via the "<distribution>"
section of the pom.xml files). Those need to be committed
- into the Maven 2 repository using SVN. And finally, the last thing is to publish the
release onto
- the project's <ulink
url="&Downloads;">downloads</ulink> and <ulink
url="&Home;/docs">documentation</ulink> pages.
- </para>
- <para>
- The assemblies of the source, binaries, etc. also need to be published onto the
&Downloads; area of the
- <ulink
url="http://www.jboss.org/dna">the project page</ulink>.
This process is expected to change, as <ulink
url="http://www.jboss.org">JBoss.org</ulink>
- improves its infrastructure.
- </para>
- </sect2>
- </sect1>
- <sect1>
- <title>Summary</title>
- <para>
- In this chapter, we described the various aspects of developing code for the JBoss DNA
project. Before we start talking
- about some of the details of JBoss DNA repositories, connectors, and sequencers,
we'll first talk about
- some very ubiquitous information: how does JBoss DNA load all of the extension
classes?
- This is the topic of the <link linkend="classloaders">next
chapter</link>.
- </para>
- </sect1>
-</chapter>
Deleted: trunk/docs/reference/src/main/docbook/en-US/content/environment.xml
===================================================================
--- trunk/docs/reference/src/main/docbook/en-US/content/environment.xml 2009-06-08
00:52:09 UTC (rev 994)
+++ trunk/docs/reference/src/main/docbook/en-US/content/environment.xml 2009-06-08
14:57:17 UTC (rev 995)
@@ -1,310 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ JBoss DNA (
http://www.jboss.org/dna)
- ~
- ~ See the COPYRIGHT.txt file distributed with this work for information
- ~ regarding copyright ownership. Some portions may be licensed
- ~ to Red Hat, Inc. under one or more contributor license agreements.
- ~ See the AUTHORS.txt file in the distribution for a full listing of
- ~ individual contributors.
- ~
- ~ JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
- ~ is licensed to you under the terms of the GNU Lesser General Public License as
- ~ published by the Free Software Foundation; either version 2.1 of
- ~ the License, or (at your option) any later version.
- ~
- ~ JBoss DNA is distributed in the hope that it will be useful,
- ~ but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- ~ or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
- ~ for more details.
- ~
- ~ You should have received a copy of the GNU Lesser General Public License
- ~ along with this distribution; if not, write to:
- ~ Free Software Foundation, Inc.
- ~ 51 Franklin Street, Fifth Floor
- ~ Boston, MA 02110-1301 USA
- -->
-<!DOCTYPE preface PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
"http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd" [
-<!ENTITY % CustomDTD SYSTEM "../custom.dtd">
-%CustomDTD;
-]>
-<chapter id="environment">
- <title>Environment</title>
- <para>
- The various components of JBoss DNA are designed as plain old Java objects, or POJOs.
And rather than making assumptions
- about their environment, each component instead requires that any external dependencies
necessary for it to operate
- must be supplied to it. This pattern is known as Dependency Injection, and it allows
the components to be simpler
- and allows for a great deal of flexibility and customization in how the components are
configured.
- And, JBoss DNA will soon provide a higher-level component that leverages the
- <ulink
url="http://www.jboss.org/jbossmc">JBoss
Microcontainer</ulink> to automatically assemble and wire together
- all the lower-level components.
- </para>
- <sect1 id="execution-context">
- <title>Execution contexts</title>
- <para>
- One of the objects that must be supplied to many JBoss DNA components is an
&ExecutionContext;. Some components
- require this context to be passed into individual methods, allowing the context to
vary with each method invocation.
- Other components require the context to be provided before it's used, and will use
that context for all its operations
- (until it is given a different one).
- </para>
- <para>
- What does an &ExecutionContext; represent? Quite simply, it's the set of
objects that define the environment
- or context in which the method or component is currently operating. It includes a way
for recording and reporting
- errors and problems. It includes the ability to <link
linkend="classloaders">create class loaders</link>
- given a classpath of class loader names. It also includes information about the
current <link linkend="security">user</link>.
- It includes access to factories that can be used to create and convert property
values. And it includes factories
- for working with namespaces and fully-qualified names. In fact, as JBoss DNA evolves,
more things may need to be
- added. Here is what the &ExecutionContext; interface looks like:
- </para>
- <programlisting>
-public class &ExecutionContext; implements &ClassLoaderFactory; {
-
- /**
- * Get the factories that should be used to create values for {@link Property
properties}.
- * @return the property value factory; never null
- */
- public &ValueFactories; getValueFactories() {...}
-
- /**
- * Get the namespace registry for this context.
- * @return the namespace registry; never null
- */
- public &NamespaceRegistry; getNamespaceRegistry() {...}
-
- /**
- * Get the factory for creating {@link Property} objects.
- * @return the property factory; never null
- */
- public &PropertyFactory; getPropertyFactory() {...}
-
- /**
- * Get the security context for this environment.
- * @return the security context; never <code>null</code>
- */
- public &SecurityContext; getSecurityContext() {...}
-
- /**
- * Return a logger associated with this context. This logger records only those
activities within the
- * context and provide a way to capture the context-specific activities. All log
messages are also
- * sent to the system logger, so classes that log via this mechanism should
<i>not</i> also
- * {@link Logger#getLogger(Class) obtain a system logger}.
- * @param clazz the class that is doing the logging
- * @return the logger, named after <code>clazz</code>; never null
- */
- public &Logger; getLogger( Class<?> clazz ) {...}
-
- /**
- * Return a logger associated with this context. This logger records only those
activities within the
- * context and provide a way to capture the context-specific activities. All log
messages are also
- * sent to the system logger, so classes that log via this mechanism should
<i>not</i> also
- * {@link Logger#getLogger(Class) obtain a system logger}.
- * @param name the name for the logger
- * @return the logger, named after <code>clazz</code>; never null
- */
- public &Logger; getLogger( String name ) {...}
-
- ...
-}
-</programlisting>
- <para>
- Notice that &ExecutionContext; implements the &ClassLoaderFactory; interface
described in the
- <link linkend="classloaders">previous chapter</link>, meaning it
can be used to create other contexts. These other methods are not shown,
- but can be used to create create subcontexts with different <link
linkend="security">security contexts</link>,
- with different namespace registry, or with different combinations of components.
- </para>
- <para>
- The fact that so many of the JBoss DNA components take &ExecutionContext;
instances gives us some interesting possibilities.
- For example, one execution context instance can be used as the highest-level (or
"application-level") context for all of the services
- (e.g., &RepositoryService;, &SequencingService;, etc.).
- Then, an execution context could be created for each user that will be performing
operations, and that user's context can
- be passed around to not only provide security information about the user but also to
allow the activities being performed
- to be recorded for user feedback, monitoring and/or auditing purposes.
- </para>
- <para>
- The following code fragment shows how easy it is to create various execution
contexts:
- </para>
- <programlisting>
-&ExecutionContext; context1 = new &ExecutionContext;();
-String jaasRealm = ...;
-
-// Create a context for a user, authenticating using a JAAS LoginContext...
-char[] password = "password".toCharArray();
-&SecurityContext; securityContext = new JaasSecurityContext(jaasRealm,
"username", password);
-&ExecutionContext; context2 = context1.with(securityContext);
-
-// Create a context for the same user, authenticating using JAAS, and using a different
callback handler ...
-&CallbackHandler; callbackHandler = ...
-&ExecutionContext; context3 = context1.with(new JaasSecurityContext(jaasRealm,
callbackHandler);
-
-// Create a context that uses a provided &SecurityContext; (see the <link
linkend="security">next section</link>)...
-&SecurityContext; mySecurityContext = ...
-&ExecutionContext; context4 = context1.with(mySecurityContext);
-</programlisting>
- <para>
- These contexts can then be passed to the various components as needed.
- </para>
- </sect1>
- <sect1 id="security">
- <title>Security</title>
- <para>
- JBoss DNA uses a simple abstraction layer to isolate it from the security
infrastructure used within an application.
- The &SecurityContext; interface is defined as follows:
- </para>
- <programlisting>
-public interface &SecurityContext; {
-
- /**
- * Get the name of the authenticated user.
- * @return the authenticated user's name
- */
- &String; getUserName();
-
- /**
- * Determine whether the authenticated user has the given role.
- * @param roleName the name of the role to check
- * @return true if the user has the role and is logged in; false otherwise
- */
- boolean hasRole( String roleName );
-
- /**
- * Logs the user out of the authentication mechanism.
- * For some authentication mechanisms, this will be implemented as a no-op.
- */
- void logout();
-}
-</programlisting>
- <para>
- As noted below, this security context is made available through the
&ExecutionContext; described above.
- </para>
- <sect2 id="jaas_security">
- <title>JAAS</title>
- <para>
- One such implementation is the &JaasSecurityContext;, which delegates any
authentication or authorization requests to a
- <ulink
url="http://java.sun.com/javase/technologies/security/">Java
Authentication and Authorization Service (JAAS)</ulink>
- provider. This is the standard approach for authenticating and authorizing in
Java, and is the default mechanism
- used by the &JcrEngine;.
- </para>
- <para>
- There are quite a few JAAS providers available, but one of the best and most powerful
providers is
- <ulink
url="http://www.jboss.org/jbosssecurity/">JBoss
Security</ulink>, the open source
- security framework used by JBoss. JBoss Security offers a number of JAAS login
modules, including:
- <itemizedlist>
- <listitem>
- <para>
- <emphasis role="strong">User-Roles Login
Module</emphasis>
- is a simple
- <code>javax.security.auth.login.LoginContext</code>
- implementation that uses usernames and passwords stored in a properties
file.
- </para>
- </listitem>
- <listitem>
- <para>
- <emphasis role="strong">Client Login Module</emphasis>
- prompts the user for their username and password.
- </para>
- </listitem>
- <listitem>
- <para>
- <emphasis role="strong">Database Server Login
Module</emphasis>
- uses a JDBC database to authenticate principals and associate them with
roles.
- </para>
- </listitem>
- <listitem>
- <para>
- <emphasis role="strong">LDAP Login Module</emphasis>
- uses an LDAP directory to authenticate principals. Two implementations are
available.
- </para>
- </listitem>
- <listitem>
- <para>
- <emphasis role="strong">Certificate Login
Module</emphasis>
- authenticates using X509 certificates, obtaining roles from either property
files or a JDBC database.
- </para>
- </listitem>
- <listitem>
- <para>
- <emphasis role="strong">Operating System Login
Module</emphasis>
- authenticates using the operating system's mechanism.
- </para>
- </listitem>
- </itemizedlist>
- and many others. Plus, JBoss Security also provides other capabilities, such as
using XACML policies or using federated single sign-on.
- For more detail, see the <ulink
url="http://www.jboss.org/jbosssecurity/">JBoss Security</ulink>
project.
- </para>
- </sect2>
- <sect2 id="web_security">
- <title>Web application security</title>
- <para>
- If JBoss DNA is being used within a web application, then it is probably desirable to
reuse the security infrastructure
- of the application server. This can be accomplished by implementing the
&SecurityContext; interface with an implementation
- that delegates to the <interface>HttpServletRequest</interface>. Then,
for each request, create a &SecurityContextCredentials;
- instance around your &SecurityContext;, and use that credentials to obtain a JCR
&Session;.
- </para>
- <para>
- Here is an example of the &SecurityContext; implementation that uses the servlet
request:
- </para>
- <programlisting>
-@Immutable
-public class ServletSecurityContext implements &SecurityContext; {
-
- private final String userName;
- private final HttpServletRequest request;
-
- /**
- * Create a {@link ServletSecurityContext} with the supplied
- * {@link HttpServletRequest servlet information}.
- *
- * @param request the servlet request; may not be null
- */
- public ServletSecurityContext( HttpServletRequest request ) {
- this.request = request;
- this.userName = request.getUserPrincipal() != null ?
request.getUserPrincipal().getName() : null;
- }
-
- /**
- * Get the name of the authenticated user.
- * @return the authenticated user's name
- */
- public &String; getUserName() {
- return userName;
- }
-
- /**
- * Determine whether the authenticated user has the given role.
- * @param roleName the name of the role to check
- * @return true if the user has the role and is logged in; false otherwise
- */
- boolean hasRole( String roleName ) {
- request.isUserInRole(roleName);
- }
-
- /**
- * Logs the user out of the authentication mechanism.
- * For some authentication mechanisms, this will be implemented as a no-op.
- */
- public void logout() {
- }
-}</programlisting>
- <para>
- Then use this to create a &Session;:
- </para>
- <programlisting>
-HttpServletRequest request = ...
-&Repository; repository = engine.getRepository("my repository");
-&SecurityContext; securityContext = new ServletSecurityContext(httpServletRequest);
-SecurityContextCredentials credentials = new
SecurityContextCredentials(securityContext);
-&Session; session = repository.login(credentials, workspaceName);
-</programlisting>
- </sect2>
- </sect1>
- <sect1>
- <title>Summary</title>
- <para>
- In this chapter, we covered security and environment topics as used throughout JBoss
DNA.
- The <link linkend="repositories">next chapter</link> will cover
JBoss DNA repositories, including the connector framework,
- how DNA's JCR implementation works with connectors, what connectors are available
(and how to use them),
- and how to write your own connector.
- </para>
- </sect1>
-</chapter>
-
Copied: trunk/docs/reference/src/main/docbook/en-US/content/jcr/jcr.xml (from rev 994,
trunk/docs/reference/src/main/docbook/en-US/content/jcr.xml)
===================================================================
--- trunk/docs/reference/src/main/docbook/en-US/content/jcr/jcr.xml
(rev 0)
+++ trunk/docs/reference/src/main/docbook/en-US/content/jcr/jcr.xml 2009-06-08 14:57:17
UTC (rev 995)
@@ -0,0 +1,120 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ JBoss DNA (
http://www.jboss.org/dna)
+ ~
+ ~ See the COPYRIGHT.txt file distributed with this work for information
+ ~ regarding copyright ownership. Some portions may be licensed
+ ~ to Red Hat, Inc. under one or more contributor license agreements.
+ ~ See the AUTHORS.txt file in the distribution for a full listing of
+ ~ individual contributors.
+ ~
+ ~ JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
+ ~ is licensed to you under the terms of the GNU Lesser General Public License as
+ ~ published by the Free Software Foundation; either version 2.1 of
+ ~ the License, or (at your option) any later version.
+ ~
+ ~ JBoss DNA is distributed in the hope that it will be useful,
+ ~ but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ ~ or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ ~ for more details.
+ ~
+ ~ You should have received a copy of the GNU Lesser General Public License
+ ~ along with this distribution; if not, write to:
+ ~ Free Software Foundation, Inc.
+ ~ 51 Franklin Street, Fifth Floor
+ ~ Boston, MA 02110-1301 USA
+ -->
+<!DOCTYPE preface PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
"http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd" [
+<!ENTITY % CustomDTD SYSTEM "../../custom.dtd">
+%CustomDTD;
+]>
+<chapter id="jcr">
+ <title>Content Repositories for Java (JCR)</title>
+ <para>
+ The
+ <ulink url="&JSR170;">Content Repository for Java technology
API</ulink>
+ provides a standard Java API for working with content repositories. Abbreviated
"JCR", this API was developed as part of the
+ Java Community Process under <ulink
url="&JSR170;">JSR-170</ulink> (JCR 1.0) and is being revised under
<ulink url="&JSR283;">JSR-283</ulink>.
+ JBoss DNA provides a partial JCR 1.0 implementation that allows you to work with the
contents of a repository using the
+ JCR API. For information about how to use the JCR API, please see the <ulink
url="&JSR170;">JSR-170</ulink> specification.
+ </para>
+ <sect1 id="jcr-repositories">
+ <title>Obtaining JCR repositories</title>
+ <para>The JCR API doesn't define how your application first obtains a
reference to a &Repository; implementation.
+ With JBoss DNA, you simply creating a &JcrRepository; object and supply
+ an &ExecutionContextFactory; and a &RepositoryConnectionFactory; (such as a
&RepositoryLibrary; or &RepositoryService;).
+ Since &JcrRepository; implements the JCR &Repository; interface, from this
point forward you can just use the
+ standard JCR API.
+ </para>
+ <note>
+ <para>
+ For more information about the &ExecutionContextFactory; and
&RepositoryConnectionFactory; classes, see
+ the chapter on <link linkend="environment">setting up a JBoss DNA
environment</link> and
+ <link linkend="repositories">setting up the
&RepositoryService;</link>.
+ </para>
+ </note>
+ </sect1>
+ <sect1 id="jcr-sessions">
+ <title>Creating JCR sessions</title>
+ <para>Creating sessions is done using a &Repository; one of its
<code>login(...)</code> methods, where the
+ name of the workspace corresponds to the name of the &RepositorySource;:
+ </para>
+ <programlisting>
+&JcrRepository; jcrRepository = new &JcrRepository;(contextFactory, sources);
+&Session; session = jcrRepository.login(sourceName);
+</programlisting>
+ <para>
+ Now, this code doesn't do any authentication; it essentially trusts the caller has
the appropriate privileges.
+ Normally, your application will need to authenticate the user, so let's look at
how that's done.
+ </para>
+ <para>
+ As we mentioned in the <link linkend="security">security
section</link>, JBoss DNA uses JAAS for authentication
+ and authorization. So how does this work with the JCR API?
+ </para>
+ <para>The JCR API defines a &Credentials; marker interface, an instance of
which can be passed to the
+ <code>&Session;.login(...)</code> method. Rather than provide a
concrete implementation of this interface, JBoss DNA
+ allows you to pass any implementation of &Credentials; that also has one of the
following methods:
+ <itemizedlist>
+ <listitem>
+ <para><code>getLoginContext()</code> that returns a
&LoginContext; instance.</para>
+ </listitem>
+ <listitem>
+ <para><code>getAccessControlContext()</code> that returns a
&AccessControlContext; instance.</para>
+ </listitem>
+ </itemizedlist>
+ This way, your application can obtain the JAAS &LoginContext; or
&AccessControlContext; however it wants,
+ and then merely passes that into DNA through the JCR &Credentials;. No interfaces
or classes specific to JBoss DNA are required.
+ </para>
+ <para>
+ The following code shows how this is done, using an anonymous inner class for the
&Credentials; implementation.
+ </para>
+ <programlisting>
+&CallbackHandler; callbackHandler = // as needed by your app, according to JAAS
+final &LoginContext; loginContext = new
&LoginContext;("MyAppContextName",callbackHandler);
+
+// Now pass to JBoss DNA to create a JCR Session ...
+&Credentials; credentials = new &Credentials;() {
+public &LoginContext; getLoginContext() { return loginContext; }
+};
+&JcrRepository; jcrRepository = new &JcrRepository;(contextFactory, sources);
+&Session; session = jcrRepository.login(credentials, sourceName);
+</programlisting>
+ </sect1>
+ <sect1>
+ <title>JCR Support</title>
+ <para>
+ JBoss DNA currently supports most of the Level 1 and Level 2 feature set defined by
the <ulink url="&JSR170;">JSR-170</ulink> specification.
+ Queries, which are part of Level 1, are not implemented, nor are all of the update
methods that make up Level 2.
+ So while the current implementation provides many of the features that may be needed
by an application, we really hope
+ that this release will allow you to give us some feedback on what we have so far.
+ </para>
+ </sect1>
+ <sect1>
+ <title>Summary</title>
+ <para>
+ In this chapter, we covered how to use JCR with JBoss DNA. Now that you know how
JBoss DNA repositories work,
+ and how to use JCR to work with DNA repositories, we'll move on in
+ the <link linkend="sequencing">next chapter</link> to describing
in detail how the sequencing of file content works.
+ </para>
+ </sect1>
+</chapter>
Property changes on: trunk/docs/reference/src/main/docbook/en-US/content/jcr/jcr.xml
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Added: trunk/docs/reference/src/main/docbook/en-US/content/jcr/rest_service.xml
===================================================================
--- trunk/docs/reference/src/main/docbook/en-US/content/jcr/rest_service.xml
(rev 0)
+++ trunk/docs/reference/src/main/docbook/en-US/content/jcr/rest_service.xml 2009-06-08
14:57:17 UTC (rev 995)
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ JBoss DNA (
http://www.jboss.org/dna)
+ ~
+ ~ See the COPYRIGHT.txt file distributed with this work for information
+ ~ regarding copyright ownership. Some portions may be licensed
+ ~ to Red Hat, Inc. under one or more contributor license agreements.
+ ~ See the AUTHORS.txt file in the distribution for a full listing of
+ ~ individual contributors.
+ ~
+ ~ JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
+ ~ is licensed to you under the terms of the GNU Lesser General Public License as
+ ~ published by the Free Software Foundation; either version 2.1 of
+ ~ the License, or (at your option) any later version.
+ ~
+ ~ JBoss DNA is distributed in the hope that it will be useful,
+ ~ but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ ~ or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ ~ for more details.
+ ~
+ ~ You should have received a copy of the GNU Lesser General Public License
+ ~ along with this distribution; if not, write to:
+ ~ Free Software Foundation, Inc.
+ ~ 51 Franklin Street, Fifth Floor
+ ~ Boston, MA 02110-1301 USA
+ -->
+<!DOCTYPE preface PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
"http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd" [
+<!ENTITY % CustomDTD SYSTEM "../../custom.dtd">
+%CustomDTD;
+]>
+<chapter id="rest-service">
+ <title>RESTful Web Service</title>
+ <para>
+ blah blah
+ </para>
+ <sect1>
+ <title>Summary</title>
+ <para>
+ In this chapter, we covered .... something .... FIX ME.
+ </para>
+ </sect1>
+</chapter>
+
Property changes on:
trunk/docs/reference/src/main/docbook/en-US/content/jcr/rest_service.xml
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Deleted: trunk/docs/reference/src/main/docbook/en-US/content/jcr.xml
===================================================================
--- trunk/docs/reference/src/main/docbook/en-US/content/jcr.xml 2009-06-08 00:52:09 UTC
(rev 994)
+++ trunk/docs/reference/src/main/docbook/en-US/content/jcr.xml 2009-06-08 14:57:17 UTC
(rev 995)
@@ -1,120 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ JBoss DNA (
http://www.jboss.org/dna)
- ~
- ~ See the COPYRIGHT.txt file distributed with this work for information
- ~ regarding copyright ownership. Some portions may be licensed
- ~ to Red Hat, Inc. under one or more contributor license agreements.
- ~ See the AUTHORS.txt file in the distribution for a full listing of
- ~ individual contributors.
- ~
- ~ JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
- ~ is licensed to you under the terms of the GNU Lesser General Public License as
- ~ published by the Free Software Foundation; either version 2.1 of
- ~ the License, or (at your option) any later version.
- ~
- ~ JBoss DNA is distributed in the hope that it will be useful,
- ~ but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- ~ or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
- ~ for more details.
- ~
- ~ You should have received a copy of the GNU Lesser General Public License
- ~ along with this distribution; if not, write to:
- ~ Free Software Foundation, Inc.
- ~ 51 Franklin Street, Fifth Floor
- ~ Boston, MA 02110-1301 USA
- -->
-<!DOCTYPE preface PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
"http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd" [
-<!ENTITY % CustomDTD SYSTEM "../custom.dtd">
-%CustomDTD;
-]>
-<chapter id="jcr">
- <title>Content Repositories for Java (JCR)</title>
- <para>
- The
- <ulink url="&JSR170;">Content Repository for Java technology
API</ulink>
- provides a standard Java API for working with content repositories. Abbreviated
"JCR", this API was developed as part of the
- Java Community Process under <ulink
url="&JSR170;">JSR-170</ulink> (JCR 1.0) and is being revised under
<ulink url="&JSR283;">JSR-283</ulink>.
- JBoss DNA provides a partial JCR 1.0 implementation that allows you to work with the
contents of a repository using the
- JCR API. For information about how to use the JCR API, please see the <ulink
url="&JSR170;">JSR-170</ulink> specification.
- </para>
- <sect1 id="jcr-repositories">
- <title>Obtaining JCR repositories</title>
- <para>The JCR API doesn't define how your application first obtains a
reference to a &Repository; implementation.
- With JBoss DNA, you simply creating a &JcrRepository; object and supply
- an &ExecutionContextFactory; and a &RepositoryConnectionFactory; (such as a
&RepositoryLibrary; or &RepositoryService;).
- Since &JcrRepository; implements the JCR &Repository; interface, from this
point forward you can just use the
- standard JCR API.
- </para>
- <note>
- <para>
- For more information about the &ExecutionContextFactory; and
&RepositoryConnectionFactory; classes, see
- the chapter on <link linkend="environment">setting up a JBoss DNA
environment</link> and
- <link linkend="repositories">setting up the
&RepositoryService;</link>.
- </para>
- </note>
- </sect1>
- <sect1 id="jcr-sessions">
- <title>Creating JCR sessions</title>
- <para>Creating sessions is done using a &Repository; one of its
<code>login(...)</code> methods, where the
- name of the workspace corresponds to the name of the &RepositorySource;:
- </para>
- <programlisting>
-&JcrRepository; jcrRepository = new &JcrRepository;(contextFactory, sources);
-&Session; session = jcrRepository.login(sourceName);
-</programlisting>
- <para>
- Now, this code doesn't do any authentication; it essentially trusts the caller has
the appropriate privileges.
- Normally, your application will need to authenticate the user, so let's look at
how that's done.
- </para>
- <para>
- As we mentioned in the <link linkend="security">security
section</link>, JBoss DNA uses JAAS for authentication
- and authorization. So how does this work with the JCR API?
- </para>
- <para>The JCR API defines a &Credentials; marker interface, an instance of
which can be passed to the
- <code>&Session;.login(...)</code> method. Rather than provide a
concrete implementation of this interface, JBoss DNA
- allows you to pass any implementation of &Credentials; that also has one of the
following methods:
- <itemizedlist>
- <listitem>
- <para><code>getLoginContext()</code> that returns a
&LoginContext; instance.</para>
- </listitem>
- <listitem>
- <para><code>getAccessControlContext()</code> that returns a
&AccessControlContext; instance.</para>
- </listitem>
- </itemizedlist>
- This way, your application can obtain the JAAS &LoginContext; or
&AccessControlContext; however it wants,
- and then merely passes that into DNA through the JCR &Credentials;. No interfaces
or classes specific to JBoss DNA are required.
- </para>
- <para>
- The following code shows how this is done, using an anonymous inner class for the
&Credentials; implementation.
- </para>
- <programlisting>
-&CallbackHandler; callbackHandler = // as needed by your app, according to JAAS
-final &LoginContext; loginContext = new
&LoginContext;("MyAppContextName",callbackHandler);
-
-// Now pass to JBoss DNA to create a JCR Session ...
-&Credentials; credentials = new &Credentials;() {
-public &LoginContext; getLoginContext() { return loginContext; }
-};
-&JcrRepository; jcrRepository = new &JcrRepository;(contextFactory, sources);
-&Session; session = jcrRepository.login(credentials, sourceName);
-</programlisting>
- </sect1>
- <sect1>
- <title>JCR Support</title>
- <para>
- JBoss DNA currently supports most of the Level 1 and Level 2 feature set defined by
the <ulink url="&JSR170;">JSR-170</ulink> specification.
- Queries, which are part of Level 1, are not implemented, nor are all of the update
methods that make up Level 2.
- So while the current implementation provides many of the features that may be needed
by an application, we really hope
- that this release will allow you to give us some feedback on what we have so far.
- </para>
- </sect1>
- <sect1>
- <title>Summary</title>
- <para>
- In this chapter, we covered how to use JCR with JBoss DNA. Now that you know how
JBoss DNA repositories work,
- and how to use JCR to work with DNA repositories, we'll move on in
- the <link linkend="sequencing">next chapter</link> to describing
in detail how the sequencing of file content works.
- </para>
- </sect1>
-</chapter>
Deleted: trunk/docs/reference/src/main/docbook/en-US/content/mimetypes.xml
===================================================================
--- trunk/docs/reference/src/main/docbook/en-US/content/mimetypes.xml 2009-06-08 00:52:09
UTC (rev 994)
+++ trunk/docs/reference/src/main/docbook/en-US/content/mimetypes.xml 2009-06-08 14:57:17
UTC (rev 995)
@@ -1,204 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ JBoss DNA (
http://www.jboss.org/dna)
- ~
- ~ See the COPYRIGHT.txt file distributed with this work for information
- ~ regarding copyright ownership. Some portions may be licensed
- ~ to Red Hat, Inc. under one or more contributor license agreements.
- ~ See the AUTHORS.txt file in the distribution for a full listing of
- ~ individual contributors.
- ~
- ~ JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
- ~ is licensed to you under the terms of the GNU Lesser General Public License as
- ~ published by the Free Software Foundation; either version 2.1 of
- ~ the License, or (at your option) any later version.
- ~
- ~ JBoss DNA is distributed in the hope that it will be useful,
- ~ but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- ~ or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
- ~ for more details.
- ~
- ~ You should have received a copy of the GNU Lesser General Public License
- ~ along with this distribution; if not, write to:
- ~ Free Software Foundation, Inc.
- ~ 51 Franklin Street, Fifth Floor
- ~ Boston, MA 02110-1301 USA
- -->
-<!DOCTYPE preface PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
"http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd" [
-<!ENTITY % CustomDTD SYSTEM "../custom.dtd">
-%CustomDTD;
-]>
-<chapter id="mimetypes">
- <title>MIME types</title>
- <para>
- JBoss DNA often needs the ability to determine the MIME type for some binary content.
When uploading content into
- a repository, we may want to add the MIME type as metadata. Or, we may want to make
some processing decisions
- based upon the MIME type. So, JBoss DNA created a small pluggable framework for
determining the MIME type by using
- the name of the file (e.g., extensions) and/or by reading the actual content.
Technically, the framework
- delegates this to one or more extensions. And we've provided one extension that
does a very good job at
- determining the MIME type from a large variety of file types. But if that isn't
sufficient, you can always
- incorporate your own detector into JBoss DNA.
- </para>
- <para>
- To use this system, you simply invoke a static method and supply the name of the
content (e.g., the name of the file, with the extension)
- and the &InputStream; to the actual binary content:
- </para>
- <programlisting>&MimeType;.of(name,content)</programlisting>
- <para>
- The result is a &String; containing the <ulink
url="http://www.iana.org/assignments/media-types/">MIME type</ulink>
- (e.g., "text/plain") or null if the MIME type cannot be determined. Note
that the name or &InputStream; may be
- null, making this a very versatile utility.
- </para>
- <sect1 id="detectors">
- <title>JBoss DNA MIME type detectors</title>
- <para>
- The principle component in this framework is the concept of a <emphasis
role="strong">detector</emphasis>.
- A detector attempts to determine the MIME type using the name of the content (e.g.,
the file name)
- and the actual content itself. If the detector is able to determine the MIME type, it
simply returns
- it as a string. If not, it merely returns null. Note, however, that a detector must
be thread-safe.
- </para>
- <para>
- Here is the interface:
- </para>
- <programlisting>
-package org.jboss.dna.graph.mimetype;
-@ThreadSafe
-public interface &MimeTypeDetector; {
-
- /**
- * Returns the MIME-type of a data source, using its supplied content and/or
- * its supplied name, depending upon the implementation. If the MIME-type
- * cannot be determined, either a "default" MIME-type or
<code>null</code> may
- * be returned, where the former will prevent earlier registered MIME-type
- * detectors from being consulted.
- *
- * @param name The name of the data source; may be <code>null</code>.
- * @param content The content of the data source; may be
<code>null</code>.
- * @return The MIME-type of the data source, or optionally
<code>null</code>
- * if the MIME-type could not be determined.
- * @throws &IOException; If an error occurs reading the supplied content.
- */
- &String; mimeTypeOf( &String; name,
- &InputStream; content ) throws &IOException;;
-}</programlisting>
- <para>
- Detectors can be added to the &MimeType; class using the
<code>addDetector(&MimeTypeDetectorConfig; config)</code>
- method, where the &MimeTypeDetectorConfig; defines the name of the detector class,
the name of the
- <link linkend="classloaders">class loader</link>, a name, and a
description. It is also possible
- to set the &ClassLoaderFactory; that the &MimeType; singleton will use.
- </para>
- <para>
- We'll next look at the MIME type detectors that are provided out by JBoss DNA out
of the box, and how to write your own.
- </para>
- <sect2 id="dna-mimetype-detector-aperture">
- <title>Aperture MIME type detector</title>
- <para>
- The &ApertureMimeTypeDetector; class is an implementation of
&MimeTypeDetector; that uses the
- <ulink
url="http://aperture.sourceforge.net/">Aperture</ulink>
open-source library, which
- is a very capable utility for determining the MIME type for a wide range of file
types,
- using both the file name and the actual content.
- </para>
- </sect2>
- </sect1>
- <sect1 id="custom-detectors">
- <title>Writing custom detectors</title>
- <para>
- Creating a custom detector involves the following steps:
- <itemizedlist>
- <listitem>
- <para>Create a Maven 2 project for your detector;</para>
- </listitem>
- <listitem>
- <para>Implement the &MimeTypeDetector; interface with your own
implementation, and create unit tests to verify
- the functionality and expected behavior;</para>
- </listitem>
- <listitem>
- <para>Add a &MimeTypeDetectorConfig; to the &MimeType; class in your
application
- as described <link linkend="detectors">earlier</link>;
and</para>
- </listitem>
- <listitem>
- <para>Deploy the JAR file with your implementation (as well as any
dependencies), and make them available to JBoss DNA
- in your application.</para>
- </listitem>
- </itemizedlist>
- It's that simple.
- </para>
- <sect2 id="custom_detector_project">
- <title>Creating the Maven 2 project</title>
- <para>The first step is to create the Maven 2 project that you can use to
compile your code and build the JARs.
- Maven 2 automates a lot of the work, and since you're already <link
linkend="maven">set up to use Maven</link>,
- using Maven for your project will save you a lot of time and effort. Of course, you
don't have to use Maven 2, but then you'll
- have to get the required libraries and manage the compiling and building process
yourself.</para>
- <note>
- <para>JBoss DNA may provide in the future a Maven archetype for creating
detector projects. If you'd find this useful
- and would like to help create it, please <link
linkend="preface">join the community</link>.</para>
- </note>
- <note>
- <para>
- The <emphasis
role="strong">dna-mimetype-detector-aperture</emphasis> project is a
small, self-contained detector implementation that
- that you can use to help you get going. Starting with this project's source
and modifying it to suit your needs
- may be the easiest way to get started.
- See the subversion repository:
- <ulink
url="&Subversion;trunk/extensions/dna-mimetype-detector-aperture/">&Subversion;trunk/sequencers/dna-mimetype-detector-aperture/</ulink>
- </para>
- </note>
- <para>You can create your Maven project any way you'd like. For examples,
see the <ulink
url="http://maven.apache.org/guides/getting-started/index.html#How_d...
2 documentation</ulink>.
- Once you've done that, just add the dependencies in your project's
<code>pom.xml</code> dependencies section:</para>
- <programlisting role="XML"><![CDATA[<dependency>
- <groupId>org.jboss.dna</groupId>
- <artifactId>dna-common</artifactId>
- <version>0.1</version>
-</dependency>
-<dependency>
- <groupId>org.jboss.dna</groupId>
- <artifactId>dna-graph</artifactId>
- <version>0.1</version>
-</dependency>
-<dependency>
- <groupId>org.slf4j</groupId>
- <artifactId>slf4j-api</artifactId>
-</dependency>
-]]></programlisting>
- <para>These are minimum dependencies required for compiling a detector. Of
course, you'll have to add
- other dependencies that your sequencer needs.</para>
- <para>As for testing, you probably will want to add more dependencies, such as
those listed here:</para>
- <programlisting role="XML"><![CDATA[<dependency>
- <groupId>junit</groupId>
- <artifactId>junit</artifactId>
- <version>4.4</version>
- <scope>test</scope>
-</dependency>
-<dependency>
- <groupId>org.hamcrest</groupId>
- <artifactId>hamcrest-library</artifactId>
- <version>1.1</version>
- <scope>test</scope>
-</dependency>
-<!-- Logging with Log4J -->
-<dependency>
- <groupId>org.slf4j</groupId>
- <artifactId>slf4j-log4j12</artifactId>
- <version>1.4.3</version>
- <scope>test</scope>
-</dependency>
-<dependency>
- <groupId>log4j</groupId>
- <artifactId>log4j</artifactId>
- <version>1.2.14</version>
- <scope>test</scope>
-</dependency>
-]]></programlisting>
- <para>
- After you've created the project, simply implement the &MimeTypeDetector;
interface. And testing should be
- quite straightforward, MIME type detectors don't require any other components.
In your tests,
- simply instantiate your &MimeTypeDetector; implementation, supply various
combinations of names and/or &InputStream;s,
- and verify the output is what you expect.
- </para>
- <para>
- To use in your application, create a &MimeTypeDetectorConfig; object with the
name, description, and class information
- for your detector, and add to the &MimeType; class using the
<code>addDetector(&MimeTypeDetectorConfig; config)</code> method.
- Then, just use the &MimeType; class.
- </para>
- </sect2>
- </sect1>
-</chapter>
Deleted: trunk/docs/reference/src/main/docbook/en-US/content/repositories.xml
===================================================================
--- trunk/docs/reference/src/main/docbook/en-US/content/repositories.xml 2009-06-08
00:52:09 UTC (rev 994)
+++ trunk/docs/reference/src/main/docbook/en-US/content/repositories.xml 2009-06-08
14:57:17 UTC (rev 995)
@@ -1,1468 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ JBoss DNA (
http://www.jboss.org/dna)
- ~
- ~ See the COPYRIGHT.txt file distributed with this work for information
- ~ regarding copyright ownership. Some portions may be licensed
- ~ to Red Hat, Inc. under one or more contributor license agreements.
- ~ See the AUTHORS.txt file in the distribution for a full listing of
- ~ individual contributors.
- ~
- ~ JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
- ~ is licensed to you under the terms of the GNU Lesser General Public License as
- ~ published by the Free Software Foundation; either version 2.1 of
- ~ the License, or (at your option) any later version.
- ~
- ~ JBoss DNA is distributed in the hope that it will be useful,
- ~ but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- ~ or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
- ~ for more details.
- ~
- ~ You should have received a copy of the GNU Lesser General Public License
- ~ along with this distribution; if not, write to:
- ~ Free Software Foundation, Inc.
- ~ 51 Franklin Street, Fifth Floor
- ~ Boston, MA 02110-1301 USA
- -->
-<!DOCTYPE preface PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
"http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd" [
-<!ENTITY % CustomDTD SYSTEM "../custom.dtd">
-%CustomDTD;
-]>
-<chapter id="repositories">
- <title>Repositories</title>
- <para></para>
- <para>There is a lot of information stored in many of different places:
databases, repositories, SCM systems,
- registries, file systems, services, etc. The purpose of the federation engine is to
allow applications to use the JCR API
- to access that information as if it were all stored in a single JCR repository, but
to really leave the information where
- it is.</para>
- <para>Why not just copy or move the information into a JCR repository? Moving it
is probably pretty difficult, since most
- likely there are existing applications that rely upon that information being where it
is. All of those applications
- would break or have to change. And copying the information means that we'd have to
continually synchronize the changes.
- This not only is a lot of work, but it often makes it difficult to know whether
information is accurate and "the master" data.
- </para>
- <para>JBoss DNA lets us leave information where it, yet access it through the JCR
API as if it were in one big repository.
- One major benefit is that existing applications that use the information in the
original locations don't break, since they
- can keep using the information. But now our JCR clients can also access all the
information, too. And if our federating JBoss DNA repository is
- configured to allow updates, JCR client applications can change the information in
the repository and JBoss DNA will propagate
- those changes down to the original source, making those changes visible to all the
other applications.
- </para>
- <para>
- In short, all clients see the correct information, even when it changes in the
underlying systems. But the JCR clients can get to all of the information
- in one spot, using one powerful standard API.
- </para>
- <sect1 id="connectors">
- <title>Repository connectors</title>
- <para>
- With JBoss DNA, your applications use the <ulink
url="&JSR170;">JCR API</ulink> to work with the repository,
- but the DNA repository transparently fetches the information from different kinds of
repositories and storage systems,
- not just a single purpose-built store. This is fundamentally what makes JBoss DNA
different.
- </para>
- <para>How does JBoss DNA do this? At the heart of JBoss DNA and it's JCR
implementation is a simple graph-based
- <emphasis>repository connector</emphasis> system. Essentially, JBoss
DNA's JCR implementation uses a single
- repository connector to access all content:
- <figure id="dnajcr-and-connector">
- <title>JBoss DNA's JCR implementation delegates to a repository
connector</title>
- <graphic align="center" scale="100"
fileref="dnajcr-and-connector.png"/>
- </figure>
- That single repository connector could use an in-memory repository, a JBoss Cache
instance (including those that are clustered and replicated),
- or a federated repository where content from multiple sources is unified.
- <figure id="dna-connectors-0.2">
- <title>JBoss DNA can put JCR on top of multiple kinds of systems</title>
- <graphic align="center" scale="100"
fileref="dna-connectors-0.2.png"/>
- </figure>
- Really, the federated connector gives us all kinds of possibilities, since we can use
that connector on top of lots of connectors
- to other individual sources. This simple connector architecture is fundamentally what
makes JBoss DNA so powerful and flexible.
- Along with a good library of connectors, which is what we're planning to create.
- </para>
- <para>
- For instance, we want to build a connector to <ulink
url="&JIRA-39;">other JCR repositories</ulink>, and another that
accesses
- the <ulink url="&JIRA-34;">local file system</ulink>.
We've already started on a <ulink url="&JIRA-36;">Subversion
connector</ulink>,
- which will allow JCR to access the files in a SVN repository (and perhaps push changes
into SVN through a commit).
- And of course we want to create a connector that accesses <ulink
url="&JIRA-199;">data</ulink>
- and <ulink url="&JIRA-37;">metadata</ulink> from relational
databases. For more information, check out our
- <ulink
url="&JIRA;?report=com.atlassian.jira.plugin.system.project:roadmap-panel">roadmap</ulink>.
- Of course, if we don't have a connector to suit your needs, you can <link
linkend="custom-connectors">write your own</link>.
- <figure id="dna-connectors-future">
- <title>Future JBoss DNA connectors</title>
- <graphic align="center" scale="100"
fileref="dna-connectors-future.png"/>
- </figure>
- </para>
- <para>
- </para>
- <para>
- It's even possible to put a different API layer on top of the connectors. For
example, the new <ulink url="&JSR203;">New I/O
(JSR-203)</ulink>
- API offers the opportunity to build new file system providers. This would be very
straightforward to put on top of a JCR implementation,
- but it could be made even simpler by putting it on top of a DNA connector. In both
cases, it'd be a trivial mapping from nodes that represent
- files and folders into JSR-203 files and directories, and events on those nodes could
easily be translated into JSR-203 watch events.
- Then, simply choose a DNA connector and configure it to use the source you want to
use.
- <figure id="dna-connectors-vfs">
- <title>Virtual File System with JBoss DNA</title>
- <graphic align="center" scale="100"
fileref="vfs-and-connector.png"/>
- </figure>
- </para>
- <para>Before we go further, let's define some terminology regarding
connectors.</para>
- <itemizedlist>
- <listitem>
- <para>
- A <emphasis role="strong">connector</emphasis> is the runnable
code packaged in one or more JAR files that
- contains implementations of several interfaces (described below). A Java developer
<emphasis>writes</emphasis>
- a connector to a type of source, such as a particular database management system,
LDAP directory, source code
- management system, etc. It is then packaged into one or more JAR files (including
dependent JARs) and deployed
- for use in applications that use JBoss DNA repositories.
- </para>
- </listitem>
- <listitem>
- <para>
- The description of a particular source system (e.g., the "Customer"
database, or the company LDAP system)
- is called a <emphasis role="strong">repository
source</emphasis>. JBoss DNA defines a &RepositorySource; interface
- that defines methods describing the behavior and supported features and a method for
establishing connections.
- A connector will have a class that implements this interface and that has JavaBean
properties for
- all of the connector-specific properties required to fully describe an instance of
the system. Use of JavaBean
- properties is not required, but it is highly recommended, as it enables reflective
configuration and administration.
- Applications that use JBoss DNA create an instance of the connector's
&RepositorySource; implementation and set
- the properties for the external source that the application wants to access with
that connector.
- </para>
- </listitem>
- <listitem>
- <para>
- A repository source instance is then used to establish <emphasis
role="strong">connections</emphasis> to
- that source. A connector provides an implementation of the
&RepositoryConnection; interface, which
- defines methods for interacting with the external system. In particular, the
<code>execute(...)</code> method
- takes an &ExecutionContext; instance and a &Request; object. The
&ExectuionContext; object defines the
- environment in which the processing is occurring, including information about the
JAAS &Subject; and &LoginContext;.
- The &Request; object describes the requested operations on the content, with
different concrete subclasses
- representing each type of activity. Examples of commands include (but not limited
to) getting a node, moving a node, creating a node,
- changing a node, and deleting a node. And, if the repository source is able to
participate in JTA/JTS distributed transactions, then the
- &RepositoryConnection; must implement the
<code>getXaResource()</code> method by returning
- a valid <code>javax.transaction.xa.XAResource</code> object that can be
used by the transaction monitor.
- </para>
- </listitem>
- </itemizedlist>
- <para>As an example, consider that we want JBoss DNA to give us access through
JCR to the schema information contained in a
- relational databases. We first have to develop a connector that allows us to interact
with relational databases using JDBC.
- That connector would contain a <code>JdbcRepositorySource</code> Java
class that implements &RepositorySource;,
- and that has all of the various JavaBean properties for setting the name of the driver
class, URL, username, password,
- and other properties. (Or we might have a JavaBean property that defines the JNDI
name where we can find a JDBC
- <code>DataSource</code> instance pointing to our JDBC database.)
- </para>
- <para>
- Our new connector would also have a <code>JdbcRepositoryConnection</code>
Java class that implements the
- &RepositoryConnection; interface. This class would probably wrap a JDBC database
connection,
- and would implement the <code>execute(...)</code> method such that the
nodes exposed by the connector
- describe the database schema of the database. For example, the connector might
represent each database table
- as a node with the table's name, with properties that describe the table (e.g.,
the description, whether it's a
- temporary table), and with child nodes that represent each of the columns, keys and
constraints.
- </para>
- <para>
- To use our connector in an application that uses JBoss DNA, we need to create an
instance of the
- <classname>JdbcRepositorySource</classname> for each database instance
that we want to access. If we have 3 MySQL databases,
- 9 Oracle databases, and 4 PostgreSQL databases, then we'd need to create a total
of 16 <classname>JdbcRepositorySource</classname>
- instances, each with the properties describing a single database instance. Those
sources are then available for use by
- JBoss DNA components, including the <link
linkend="jcr">JCR</link> implementation.
- </para>
- <para>
- So, we've so far learned what a repository connector is and how they're used
to establish connections to the underlying sources
- and access the content in those sources. In the <link
linkend="repository-service">next section</link>, we'll show how
these
- source instances can be configured, managed, and their connections pooled. After
that, we'll look review JBoss DNA's
- <link linkend="connector-library">existing connectors</link> and
show how to <link linkend="custom-connectors">create your own
connectors</link>.
- </para>
- </sect1>
- <sect1 id="repository-workspaces">
- <title>Workspaces</title>
- <para>The previous section talked about how connector expose their information
through the graph language of JBoss DNA.
- This is true, except that we didn't dive into too much of the detail. JBoss DNA
graphs have the notion of <emphasis>workspaces</emphasis>
- in which the content appears, and its very easy for clients using the graph to switch
between workspaces. In fact,
- workspaces differ from each other in that they provide different views of the same
information.
- </para>
- <para>Consider a source control system, like SVN or CVS. These systems provide
different views of the source code:
- a mainline development branch as well as other branches (or tags) commonly used for
releases. So, just like one source
- file might appear in the mainline branch as well as the previous two release branches,
a node in a repository source
- might appear in multiple workspaces.
- </para>
- <para>
- However, each connector can kind of decide how (or whether) it uses workspaces. For
example, there may be no overlap
- in the content between workspaces. Or a connector might only expose a single
workspace (in other words, there's only one
- "default" workspace).
- </para>
- </sect1>
- <sect1 id="repository-service">
- <title>Repository Service</title>
- <para>The JBoss DNA &RepositoryService; is the component that manages the
<emphasis>repository sources</emphasis>
- and the connections to them. &RepositorySource; instances can be
programmatically added to the service, but
- the service can actually read its configuration from a configuration repository
(which, by the way, is represented by a
- just another &RepositorySource; instance that's usually added programmatically
to the service). The service connects to
- the configuration repository, reads the content in a particular area, and
automatically sets up the &RepositorySource; instances
- per the information found in the configuration repository.
- </para>
- <para>
- The &RepositoryService; also transparently maintains for each source a pool of
reusable connections. The pooling properties
- can be controlled via the configuration repository, or adjusted programmatically.
- </para>
- <para>
- Using a repository, then, involves simply asking the &RepositoryService; for a
&RepositoryConnection;
- to the repository given the repository's name. If a source exists with that name,
the service checks out a connection from
- the source's pool. The resulting connection is actually a wrapper around the
underlying pooled connection, so the
- component that requested the connection can simply close it, and under the covers the
actual connection is simply returned
- to the pool.
- </para>
- <para>To instantiate the &RepositoryService;, we need to first have a few
other objects:
- <itemizedlist>
- <listitem>
- <para>
- A &ExecutionContext; instance, as discussed <link
linkend="execution-context">earlier</link>.
- </para>
- </listitem>
- <listitem>
- <para>
- A &RepositoryLibrary; instance that manages the list of &RepositorySource;
instances,
- properly injects the execution contexts into each repository source, and provides a
configurable pool of connections
- for each source.
- </para>
- </listitem>
- <listitem>
- <para>
- A <emphasis>configuration repository</emphasis> that contains
descriptions of all of the repository sources
- as well as any information those sources need. Because this is a regular
repository, this could be a simple
- repository with content loaded from an XML file, or it could be a shared
- central repository with information about all of the JBoss DNA repositories used
across your organization.
- </para>
- </listitem>
- </itemizedlist>
- With these components in place, we can then instantiate the &RepositoryService;
and start it (using its
- &ServiceAdministrator;). During startup, the service reads the configuration
repository and loads any
- defined &RepositorySource; instances into the repository library, using the class
loader factory
- (available in the &ExecutionContext;) to obtain.
- </para>
- <para>
- Here's sample code that shows how to set up and start the repository service. You
can see something similar
- in the example application in the <code>startRepositories()</code> method
of the
- <code>org.jboss.example.dna.repository.RepositoryClient</code> class.
- </para>
- <programlisting>
- // Create the top-level execution context with all standard components ...
- &ExecutionContext; context = new ExecutionContext();
-
- // Create the library for the RepositorySource instances ...
- &RepositoryLibrary; sources = new &RepositoryLibrary;(context);
-
- // Load into the source manager the repository source for the configuration repository
...
- &InMemoryRepositorySource; configSource = new &InMemoryRepositorySource;();
- configSource.setName("Configuration");
- sources.addSource(configSource);
-
- // Now instantiate the Repository Service ...
- &RepositoryService; service = new &RepositoryService;(sources,
configSource.getName(), context);
- service.getAdministrator().start();
- </programlisting>
-
- <para>After startup completes, the repositories are ready to be used. The client
application obtains the list of repositories
- and presents them to the user. When the user selects one, the client application
starts navigating that repository
- starting at its root node (e.g., the "/" path). As you type a command to
list the contents of the current node or to
- "change directories" to a different node, the client application obtains the
information for the node using a simple
- procedure:
- <orderedlist>
- <listitem>
- <para>Get a connection to the repository.</para>
- </listitem>
- <listitem>
- <para>Using the connection, find the current node and read its properties and
children, putting the information
- into a simple Java plain old Java object (POJO).</para>
- </listitem>
- <listitem>
- <para>Close the connection to the repository (in a finally block to ensure it
always happens).</para>
- </listitem>
- </orderedlist>
- </para>
- </sect1>
- <sect1 id="connector-library">
- <title>Out-of-the-box repository connectors</title>
- <para>
- A number of repository connectors are already available in JBoss DNA, and are
outlined in the following sections.
- Note that we do want to build <ulink
url="https://jira.jboss.org/jira/secure/IssueNavigator.jspa?reset=tr...
connectors</ulink>
- in the upcoming releases.
- </para>
- <sect2 id="dna-connector-inmemory">
- <title>In-memory connector</title>
- <para>
- The in-memory repository connector is a simple connector that creates a transient,
in-memory repository.
- This repository is used as a very simple in-memory cache or as a standalone transient
repository.
- </para>
- <para>
- The &InMemoryRepositorySource; class provides a number of JavaBean properties
that control its behavior:
- </para>
- <table frame='all'>
- <title>&InMemoryRepositorySource; properties</title>
- <tgroup cols='2' align='left' colsep='1'
rowsep='1'>
- <colspec colname='c1' colwidth="1*"/>
- <colspec colname='c2' colwidth="1*"/>
- <thead>
- <row>
- <entry>Property</entry>
- <entry>Description</entry>
- </row>
- </thead>
- <tbody>
- <row>
- <entry>name</entry>
- <entry>The name of the repository source, which is used by the
&RepositoryService; when obtaining a &RepositoryConnection; by
name.</entry>
- </row>
- <row>
- <entry>jndiName</entry>
- <entry>Optional property that, if used, specifies the name in JNDI where an
&InMemoryRepository; instance can be found.
- This is an advanced property that is infrequently used.</entry>
- </row>
- <row>
- <entry>rootNodeUuid</entry>
- <entry>Optional property that, if used, defines the UUID of the root node in
the in-memory repository. If not used,
- then a new UUID is generated.</entry>
- </row>
- <row>
- <entry>retryLimit</entry>
- <entry>Optional property that, if used, defines the number of times that any
single operation on a &RepositoryConnection; to this source should be retried
- following a communication failure. The default value is
'0'.</entry>
- </row>
- <row>
- <entry>defaultCachePolicy</entry>
- <entry>Optional property that, if used, defines the default for how long
this information provided by this source may to be
- cached by other, higher-level components. The default value of null implies that
this source does not define a specific
- duration for caching information provided by this repository
source.</entry>
- </row>
- <row>
- <entry>defaultWorkspaceName</entry>
- <entry>Optional property that is initialized to an empty string and which
defines the name for the workspace that will be used by default
- if none is specified.</entry>
- </row>
- </tbody>
- </tgroup>
- </table>
- </sect2>
- <sect2 id="dna-connector-jbosscache">
- <title>JBoss Cache connector</title>
- <para>
- The JBoss Cache repository connector allows a <ulink
url="http://www.jboss.org/jbosscache/">JBoss Cache</ulink> instance to
be
- used as a JBoss DNA (and thus JCR) repository. This provides a repository that is an
effective, scalable, and distributed cache,
- and is often paired with other repository sources to provide a local or <link
linkend="dna-connector-federation">federated</link>
- repository.
- </para>
- <para>
- The &JBossCacheSource; class provides a number of JavaBean properties that
control its behavior:
- </para>
- <table frame='all'>
- <title>&JBossCacheSource; properties</title>
- <tgroup cols='2' align='left' colsep='1'
rowsep='1'>
- <colspec colname='c1' colwidth="1*"/>
- <colspec colname='c2' colwidth="1*"/>
- <thead>
- <row>
- <entry>Property</entry>
- <entry>Description</entry>
- </row>
- </thead>
- <tbody>
- <row>
- <entry>name</entry>
- <entry>The name of the repository source, which is used by the
&RepositoryService; when obtaining a &RepositoryConnection; by
name.</entry>
- </row>
- <row>
- <entry>cacheFactoryJndiName</entry>
- <entry>Optional property that, if used, specifies the name in JNDI where an
existing JBoss Cache Factory instance can be found.
- That factory would then be used if needed to create a JBoss Cache instance. If
no value is provided, then the
- JBoss Cache <code>DefaultCacheFactory</code> class is
used.</entry>
- </row>
- <row>
- <entry>cacheConfigurationName</entry>
- <entry>Optional property that, if used, specifies the name of the
configuration that is supplied to the cache factory
- when creating a new JBoss Cache instance.</entry>
- </row>
- <row>
- <entry>cacheJndiName</entry>
- <entry>Optional property that, if used, specifies the name in JNDI where an
existing JBoss Cache instance can be found.
- This should be used if your application already has a cache that is used, or if
you need to configure the cache in
- a special way.</entry>
- </row>
- <row>
- <entry>uuidPropertyName</entry>
- <entry>Optional property that, if used, defines the property that should be
used to find the UUID value for each node
- in the cache. "<code>dna:uuid</code>" is the
default.</entry>
- </row>
- <row>
- <entry>retryLimit</entry>
- <entry>Optional property that, if used, defines the number of times that any
single operation on a &RepositoryConnection; to this source should be retried
- following a communication failure. The default value is
'0'.</entry>
- </row>
- <row>
- <entry>defaultCachePolicy</entry>
- <entry>Optional property that, if used, defines the default for how long
this information provided by this source may to be
- cached by other, higher-level components. The default value of null implies that
this source does not define a specific
- duration for caching information provided by this repository
source.</entry>
- </row>
- <row>
- <entry>nameOfDefaultWorkspace</entry>
- <entry>Optional property that is initialized to an empty string and which
defines the name for the workspace that will be used by default
- if none is specified.</entry>
- </row>
- <row>
- <entry>predefinedWorkspaceNames</entry>
- <entry>Optional property that defines the names of the workspaces that exist
and that are available for use without having to create them.</entry>
- </row>
- <row>
- <entry>creatingWorkspacesAllowed</entry>
- <entry>Optional property that is by default 'true' that defines
whether clients can create new workspaces.</entry>
- </row>
- </tbody>
- </tgroup>
- </table>
- </sect2>
- <sect2 id="dna-connector-federation">
- <title>Federating connector</title>
- <para>
- The federated repository source provides a unified repository consisting of
information that is dynamically federated from multiple other
- &RepositorySource; instances. This is a very powerful repository source that
appears to be a single repository, when in
- fact the content is stored and managed in multiple other systems. Each
&FederatedRepositorySource; is typically configured
- with the name of another &RepositorySource; that should be used as the local,
unified cache of the federated content.
- The configuration also contains the names of the other &RepositorySource;
instances that are to be federated along with
- the &Projection; definition describing where in the unified repository the
content is to appear.
- </para>
- <figure id="dna-connector-federation-image">
- <title>Federating multiple sources using the Federated Repository
Connector</title>
- <graphic align="center" scale="100"
fileref="dna-connector-federation.png"/>
- </figure>
- <para> The federation connector works by effectively building up a single
graph by querying each source and merging or
- unifying the responses. This information is cached, which improves performance,
reduces the number of (potentially
- expensive) remote calls, reduces the load on the sources, and helps mitigate
problems with source availability. As
- clients interact with the repository, this cache is consulted first. When the
requested portion of the graph (or
- "subgraph") is contained completely in the cache, it is retuned
immediately. However, if any part of the requested
- subgraph is not in the cache, each source is consulted for their contributions to
that subgraph, and any results are
- cached.</para>
- <para> This basic flow makes it possible for the federated repository to
build up a local cache of the integrated graph
- (or at least the portions that are used by clients). In fact, the federated
repository caches information in a manner
- that is similar to that of the Domain Name System (DNS). As sources are consulted
for their contributions, the source
- also specifies whether it is the authoritative source for this information (some
sources that are themselves federated
- may not be the information's authority), whether the information may be
modified, the time-to-live (TTL) value (the time
- after which the cached information should be refreshed), and the expiration time
(the time after which the cached
- information is no longer valid). In effect, the source has complete control over
how the information it contributes is
- cached and used.</para>
- <para>
- The federated repository also needs to incorporate <emphasis>negative
caching</emphasis>, which is storage of the knowledge
- that something does <emphasis>not</emphasis> exist. Sources can be
configured to contribute information
- only below certain paths (e.g., <code>/A/B/C</code>), and the
federation engine can take advantage of this by never
- consulting that source for contributions to information on other paths. However,
below that path, any negative responses
- must also be cached (with appropriate TTL and expiry parameters) to prevent the
exclusion of that source (in case the source
- has information to contribute at a later time) or the frequent checking with the
source.
- </para>
- <para>
- The federated repository uses other &RepositorySource;s that are to be federated
and a &RepositorySource; that is to be used as the
- cache of the unified contents. These are configured in another
&RepositorySource; that is treated as a configuration repository.
- The &FederatedRepositorySource; class uses JavaBean properties to define the name
of the configuration repository and
- the path to the "<code>dna:federation</code>" node in that
configuration repository containing the information about the
- cache and federated sources. This graph structure that is expected at this location
is as follows:
- </para>
- <programlisting><![CDATA[<!-- Define the federation configuration. -->
-<dna:federatedRepository
xmlns:dna="http://www.jboss.org/dna"
-
xmlns:jcr="http://www.jcp.org/jcr/1.0"
- dna:timeToCache="100000" >
- <dna:workspaces>
- <dna:workspace jcr:name="default">
- <!-- Define how the content in the 'Cache' source is to map to the
federated cache -->
- <dna:cache dna:sourceName="Cache"
dna:workspaceName="default" dna:projectionRules="/a => /" />
-
- <!-- Define how the content in the two sources maps to the federated/unified
repository.
- This example puts the 'Cars' and 'Aircraft' content underneath
'/vehicles', but the
- 'Configuration' content (which is defined by this file) will appear
under '/'. -->
- <dna:projections>
- <dna:projection jcr:name="Cars" dna:projectionRules="/Vehicles
=> /" />
- <dna:projection jcr:name="Aircraft"
dna:projectionRules="/Vehicles => /" />
- <dna:projection jcr:name="Configuration" dna:projectionRules="/
=> /" />
- </dna:projections>
- </dna:workspace>
- </dna:workspaces>
-</dna:federatedRepository>
-]]></programlisting>
- <note>
- <para>
- We're using XML to represent a graph structure, since the two map pretty well.
Each XML element represents
- a node and XML attributes represent properties on a node. The name of the node is
defined by either the
- <code>jcr:name</code> attribute (if it exists) or the name of the XML
element. And we use XML namespaces
- to define the namespaces used in the node and property names. BTW, this is exactly
how the XML graph importer
- works.
- </para>
- </note>
- <para>
- Notice that there is a cache projection and three source projections, and each
projection defines
- one or more <emphasis>projection rules</emphasis> that are of the form:
- </para>
- <programlisting>pathInFederatedRepository =>
pathInSourceRepository</programlisting>
- <para>
- So, a projection rule <code>/Vehicles => /</code> projects the
entire contents of the source so that
- it appears in the federated repository under the
"<code>/Vehicles</code>" node.
- </para>
- <para>
- The &FederatedRepositorySource; class provides a number of JavaBean properties
that control its behavior:
- </para>
- <table frame='all'>
- <title>&FederatedRepositorySource; properties</title>
- <tgroup cols='2' align='left' colsep='1'
rowsep='1'>
- <colspec colname='c1' colwidth="1*"/>
- <colspec colname='c2' colwidth="1*"/>
- <thead>
- <row>
- <entry>Property</entry>
- <entry>Description</entry>
- </row>
- </thead>
- <tbody>
- <row>
- <entry>name</entry>
- <entry>The name of the repository source, which is used by the
&RepositoryService; when obtaining a &RepositoryConnection; by
name.</entry>
- </row>
- <row>
- <entry>repositoryName</entry>
- <entry>The name for the federated repository.</entry>
- </row>
- <row>
- <entry>configurationSourceName</entry>
- <entry>The name of the &RepositorySource; that should be used as the
configuration repository, and in which is defined
- how this federated repository is to be set up and configured.
- This name is supplied to the &RepositoryConnectionFactory; that is provided
to this instance when added to the
- &RepositoryLibrary;.</entry>
- </row>
- <row>
- <entry>configurationWorkspaceName</entry>
- <entry>The name of the workspace in the configuration &RepositorySource;
with the content defining
- how this federated repository is to be set up and configured.</entry>
- </row>
- <row>
- <entry>configurationSourcePath</entry>
- <entry>The path to the node in the configuration repository below which a
"dna:federation" node exists with the
- graph structure describing how this federated repository is to be
configured.</entry>
- </row>
- <row>
- <entry>securityDomain</entry>
- <entry>Optional property that, if used, specifies the name of the JAAS
application context that should be used
- to establish the <link linkend="execution-contenxt">execution
context</link> for this repository.
- This should correspond to the JAAS login configuration located within the JAAS
login configuration file,
- and should be used only if a "<code>username</code>"
property is defined.</entry>
- </row>
- <row>
- <entry>username</entry>
- <entry>Optional property that, if used, defines the name of the JAAS subject
that should be used
- to establish the <link linkend="execution-contenxt">execution
context</link> for this repository.
- This should be used if a "<code>securityDomain</code>"
property is defined.</entry>
- </row>
- <row>
- <entry>password</entry>
- <entry>Optional property that, if used, defines the password of the JAAS
subject that should be used
- to establish the <link linkend="execution-contenxt">execution
context</link> for this repository.
- If the password is not provided but values for the
"<code>securityDomain</code>" and
"<code>username</code>" properties are,
- then authentication will use the default JAAS callback handlers.</entry>
- </row>
- <row>
- <entry>retryLimit</entry>
- <entry>Optional property that, if used, defines the number of times that any
single operation on a &RepositoryConnection; to this source should be retried
- following a communication failure. The default value is
'0'.</entry>
- </row>
- <row>
- <entry>defaultCachePolicy</entry>
- <entry>Optional property that, if used, defines the default for how long
this information provided by this source may to be
- cached by other, higher-level components. The default value of null implies that
this source does not define a specific
- duration for caching information provided by this repository
source.</entry>
- </row>
- </tbody>
- </tgroup>
- </table>
- </sect2>
- </sect1>
- <sect1 id="custom-connectors">
- <title>Writing custom connectors</title>
- <para>
- There may come a time when you want to tackle creating your own repository connector.
Maybe the connectors we provide out-of-the-box
- don't work with your source. Maybe you want to use a different cache system.
- Maybe you have a system that you want to make available through a JBoss DNA
repository. Or, maybe you're
- a contributor and want to help us round out our library with a new connector. No
matter what the reason, creating a new connector
- is pretty straightforward, as we'll see in this section.
- </para>
- <para>
- Creating a custom connector involves the following steps:
- <orderedlist>
- <listitem>
- <para>Create a Maven 2 project for your connector;</para>
- </listitem>
- <listitem>
- <para>
- Implement the &RepositorySource; interface, using JavaBean properties for each
bit of information the implementation will
- need to establish a connection to the source system.
- </para>
- <para>
- Then, implement the &RepositoryConnection; interface with a class that
represents a connection to the source. The
- <code>execute(&ExecutionContext;, &Request;)</code> method
should process any and all requests that may come down the pike,
- and the results of each request can be put directly on that request.
- </para>
- <para>
- Don't forget unit tests that verify that the connector is doing what it's
expected to do. (If you'll be committing the connector
- code to the JBoss DNA project, please ensure that the unit tests can be run by
others that may not have access to the
- source system. In this case, consider writing integration tests that can be easily
configured to use different sources
- in different environments, and try to make the failure messages clear when the
tests can't connect to the underlying source.)
- </para>
- </listitem>
- <listitem>
- <para>
- Configure JBoss DNA to use your connector. This may involve just registering the
source with the &RepositoryService;,
- or it may involve adding a source to a configuration repository used by the
<link linkend="dna-connector-federation">federated
repository</link>.
- </para>
- </listitem>
- <listitem>
- <para>
- Deploy the JAR file with your connector (as well as any dependencies), and make
them available to JBoss DNA
- in your application.
- </para>
- </listitem>
- </orderedlist>
- Let's go through each one of these steps in more detail.
- </para>
- <sect2 id="custom_connector_project">
- <title>Creating the Maven 2 project</title>
- <para>
- The first step is to create the Maven 2 project that you can use to compile your code
and build the JARs.
- Maven 2 automates a lot of the work, and since you're already <link
linkend="maven">set up to use Maven</link>,
- using Maven for your project will save you a lot of time and effort. Of course,
you don't have to use Maven 2, but then you'll
- have to get the required libraries and manage the compiling and building process
yourself.</para>
- <note>
- <para>JBoss DNA may provide in the future a Maven archetype for creating
connector projects. If you'd find this useful
- and would like to help create it, please <link
linkend="preface">join the community</link>.
- </para>
- <para>In lieu of a Maven archetype, you may find it easier to start with a
small existing connector project.
- The <emphasis
role="strong">dna-connector-filesystem</emphasis> project is small, but
it may be tough to separate
- the stuff that every connector needs from the extra code and data structures that
manage the content.
- See the subversion repository: <ulink
url="&Subversion;trunk/extensions/dna-connector-filesystem/">&Subversion;trunk/extensions/dna-connector-filesystem/</ulink>
- </para>
- </note>
- <para>
- You can create your Maven project any way you'd like. For examples, see the
- <ulink
url="http://maven.apache.org/guides/getting-started/index.html#How_d...
2 documentation</ulink>.
- Once you've done that, just add the dependencies in your project's
<code>pom.xml</code> dependencies section:
- </para>
- <programlisting role="XML"><![CDATA[
-<dependency>
- <groupId>org.jboss.dna</groupId>
- <artifactId>dna-graph</artifactId>
- <version>0.4</version>
-</dependency>
- ]]></programlisting>
- <para>
- This is the only dependency required for compiling a connector - Maven pulls in all
of the dependencies needed by
- the 'dna-graph' artifact. Of course, you'll still have to add
dependencies for any library your connector needs
- to talk to its underlying system.
- </para>
- <para>
- As for testing, you probably will want to add more dependencies, such as those listed
here:
- </para>
- <programlisting role="XML"><![CDATA[
-<dependency>
- <groupId>org.jboss.dna</groupId>
- <artifactId>dna-graph</artifactId>
- <version>0.4</version>
- <type>test-jar</type>
- <scope>test</scope>
-</dependency>
-<dependency>
- <groupId>org.jboss.dna</groupId>
- <artifactId>dna-common</artifactId>
- <version>0.4</version>
- <type>test-jar</type>
- <scope>test</scope>
-</dependency>
-<dependency>
- <groupId>junit</groupId>
- <artifactId>junit</artifactId>
- <version>4.4</version>
- <scope>test</scope>
-</dependency>
-<dependency>
- <groupId>org.hamcrest</groupId>
- <artifactId>hamcrest-library</artifactId>
- <version>1.1</version>
- <scope>test</scope>
-</dependency>
-<!-- Logging with Log4J -->
-<dependency>
- <groupId>org.slf4j</groupId>
- <artifactId>slf4j-log4j12</artifactId>
- <version>1.4.3</version>
- <scope>test</scope>
-</dependency>
-<dependency>
- <groupId>log4j</groupId>
- <artifactId>log4j</artifactId>
- <version>1.2.14</version>
- <scope>test</scope>
-</dependency>
- ]]></programlisting>
- <para>
- Testing JBoss DNA connectors does not require a JCR repository or the JBoss DNA
services. (For more detail,
- see the <link linkend="testing_custom_connectors">testing
section</link>.) However, if you want to do
- integration testing with a JCR repository and the JBoss DNA services, you'll
need additional dependencies
- (e.g., <code>dna-repository</code> and any other extensions).
- </para>
- <para>
- At this point, your project should be set up correctly, and you're ready to move
on to
- <link linkend="implementing_repository_source">writing the Java
implementation</link> for your connector.
- </para>
- </sect2>
- <sect2 id="implementing_repository_source">
- <title>Implementing a <code>RepositorySource</code></title>
- <para>
- As mentioned earlier, a <emphasis>connector</emphasis> consists of the
Java code that is used to access content
- from a system. Perhaps the most important class that makes up a connector is the
implementation of the
- &RepositorySource;. This class is analogous to JDBC's DataSource in that it
is instantiated to represent
- a single instance of a system that will be accessed, and it contains enough
information (in the form of JavaBean properties)
- so that it can create connections to the source.
- </para>
- <para>
- Why is the &RepositorySource; implementation a JavaBean? Well, this is the class
that is instantiated, usually
- reflectively, and so a no-arg constructor is required. Using JavaBean properties
makes it possible
- to reflect upon the object's class to determine the properties that can be set
(using setters) and read
- (using getters). This means that an administrative application can instantiate,
configure, and manage
- the objects that represent the actual sources, without having to know anything about
the actual implementation.
- </para>
- <para>
- So, your connector will need a public class that implements &RepositorySource;
and provides JavaBean properties
- for any kind of inputs or options required to establish a connection to and interact
with the underlying source.
- Most of the semantics of the class are defined by the &RepositorySource; and
inherited interface.
- However, there are a few characteristics that are worth mentioning here.
- </para>
- <sect3 id="connector_cache_policy">
- <title>Cache policy</title>
- <para>
- Each connector is responsible for determining whether and how long DNA is to cache
the
- content made available by the connector. This is referred to as the
<emphasis>caching policy</emphasis>,
- and consists of a <emphasis>time to live</emphasis> value representing
the number of milliseconds that
- a piece of data may be cached. After the TTL has passed, the information is no
longer used.
- </para>
- <para>
- DNA allows a connector to use a flexible and powerful caching policy. First, each
connection returns the
- <emphasis>default</emphasis> caching policy for all information returned
by that connection.
- Often this policy can be configured via properties on the &RepositorySource;
implementation.
- This is optional, meaning the connector can return <code>null</code> if
it does not wish to
- have a default caching policy.
- </para>
- <para>
- Second, the connector is able to override its default caching policy on individual
requests
- (which we'll cover in the <link
linkend="implementing_repository_connection">next section</link>).
- Again, this is optional, meaning that a null caching policy on a request implies
that the
- request has no overridden caching policy.
- </para>
- <para>
- Third, if the connector has no default caching policy and none is set on the
individual requests,
- DNA uses whatever caching policy is set up for that component using the connector.
For example, the federating
- connector allows a default caching policy to be specified, and this policy is used
should the sources
- being federated not define their own caching policy.
- </para>
- <para>
- In summary, a connector has total control over whether and for how long the
information it provides
- is cached.
- </para>
- </sect3>
- <sect3 id="repository_source_jndi">
- <title>Leveraging JNDI</title>
- <para>
- Sometimes it is necessary (or easier) for a &RepositorySource; implementation to
look up an object in JNDI.
- One example of this is the JBoss Cache connector: while the connector can
- instantiate a new JBoss Cache instance, more interesting use cases involve JBoss
Cache instances that are
- set up for clustering and replication, something that is generally difficult to
configure in a single JavaBean.
- Therefore the &JBossCacheSource; has optional JavaBean properties that define
how it is to look up a
- JBoss Cache instance in JNDI.
- </para>
- <para>
- This is a simple pattern that you may find useful in your connector. Basically, if
your source implementation
- can look up an object in JNDI, simply use a single JavaBean String property that
defines the
- full name that should be used to locate that object in JNDI. Usually it's best
to include "Jndi" in the
- JavaBean property name so that administrative users understand the purpose of the
property.
- (And some may suggest that any optional property also use the word
"optional" in the property name.)
- </para>
- </sect3>
- <sect3 id="repository_source_capabilities">
- <title>Capabilities</title>
- <para>
- Another characteristic of a &RepositorySource; implementation is that it
provides some hint as to whether
- it supports several features. This is defined on the interface as a method that
returns a
- &RepositorySourceCapabilities; object. This class currently provides methods
that say whether the connector supports
- updates, whether it supports same-name-siblings (SNS), and whether the connector
supports listeners and events.
- </para>
- <para>
- Note that these may be hard-coded values, or the connector's response may be
determined at runtime by various factors.
- For example, a connector may interrogate the underlying system to decide whether it
can support updates.
- </para>
- <para>
- The &RepositorySourceCapabilities; can be used as is (the class is immutable),
or it can be subclassed
- to provide more complex behavior. It is important, however, that the capabilities
remain constant
- throughout the lifetime of the &RepositorySource; instance.
- </para>
- <note>
- <para>
- Why a concrete class and not an interface? By using a concrete class, connectors
inherit the default
- behavior. If additional capabilities need to be added to the class in future
releases, connectors may
- not have to override the defaults. This provides some insulation against future
enhancements to the connector framework.
- </para>
- </note>
- </sect3>
- <sect3 id="repository_source_security">
- <title>Security and authentication</title>
- <para>
- As we'll see in the next section, the main method connectors have to process
requests takes an &ExecutionContext;,
- which contains the JAAS security information of the subject performing the request.
This means that the connector
- can use this to determine authentication and authorization information for each
request.
- </para>
- <para>
- Sometimes that is not sufficient. For example, it may be that the connector needs
its own authorization information
- so that it can establish a connection (even if user-level privileges still use the
&ExecutionContext; provided with
- each request). In this case, the &RepositorySource; implementation will
probably need JavaBean properties
- that represent the connector's authentication information. This may take the
form of a username and password,
- or it may be properties that are used to delegate authentication to JAAS.
- Either way, just realize that it's perfectly acceptable for the connector to
require its own security properties.
- </para>
- </sect3>
- </sect2>
- <sect2 id="implementing_repository_connection">
- <title>Implementing a
<code>RepositoryConnection</code></title>
- <para>
- One job of the &RepositorySource; implementation is to create connections to the
underlying sources.
- Connections are represented by classes that implement the &RepositoryConnection;
interface, and creating this
- class is the next step in writing a repository connector. This is what we'll
cover in this section.
- </para>
- <para>
- The &RepositoryConnection; interface is pretty straightforward:
- </para>
- <programlisting>
-/**
- * A connection to a repository source.
- * <p>
- * These connections need not support concurrent operations by multiple threads.
- * </p>
- */
-@NotThreadSafe
-public interface &RepositoryConnection; {
-
- /**
- * Get the name for this repository source. This value should be the same as that
returned
- * by the same &RepositorySource; that created this connection.
- *
- * @return the identifier; never null or empty
- */
- String getSourceName();
-
- /**
- * Return the transactional resource associated with this connection. The transaction
manager
- * will use this resource to manage the participation of this connection in a
distributed transaction.
- *
- * @return the XA resource, or null if this connection is not aware of distributed
transactions
- */
- XAResource getXAResource();
-
- /**
- * Ping the underlying system to determine if the connection is still valid and
alive.
- *
- * @param time the length of time to wait before timing out
- * @param unit the time unit to use; may not be null
- * @return true if this connection is still valid and can still be used, or false
otherwise
- * @throws InterruptedException if the thread has been interrupted during the
operation
- */
- boolean ping( long time, &TimeUnit; unit ) throws InterruptedException;
-
- /**
- * Set the listener that is to receive notifications to changes to content within
this source.
- *
- * @param listener the new listener, or null if no component is interested in the
change notifications
- */
- void setListener( &RepositorySourceListener; listener );
-
- /**
- * Get the default cache policy for this repository. If none is provided, a global
cache policy
- * will be used.
- *
- * @return the default cache policy
- */
- &CachePolicy; getDefaultCachePolicy();
-
- /**
- * Execute the supplied commands against this repository source.
- *
- * @param context the environment in which the commands are being executed; never
null
- * @param request the request to be executed; never null
- * @throws RepositorySourceException if there is a problem loading the node data
- */
- void execute( &ExecutionContext; context,
- &Request; request ) throws &RepositorySourceException;;
-
- /**
- * Close this connection to signal that it is no longer needed and that any
accumulated
- * resources are to be released.
- */
- void close();
-}</programlisting>
- <para>
- While most of these methods are straightforward, a few warrant additional
information.
- The <code>ping(...)</code> allows DNA to check the connection to see if
it is
- alive. This method can be used in a variety of situations, ranging from verifying
that a &RepositorySource;'s
- JavaBean properties are correct to ensuring that a connection is still alive before
returning the connection from
- a connection pool.
- </para>
- <para>
- DNA hasn't yet defined the event mechanism, so connectors don't have any
methods to invoke on the &RepositorySourceListener;.
- This will be defined in the next release, so feel free to manage the listeners now.
Note that by default the &RepositorySourceCapabilities; returns
- <code>false</code> for <code>supportsEvents()</code>.
- </para>
- <para>
- The most important method on this interface, though, is the
<code>execute(...)</code> method, which serves as the
- mechanism by which the component using the connector access and manipulates the
content exposed by the connector.
- The first parameter to this method is the &ExecutionContext;, which contains the
information about environment
- as well as the subject performing the request. This was discussed <link
linkend="execution-context">earlier</link>.
- </para>
- <para>
- The second parameter, however, represents a request that is to be processed by the
connector. Request objects can
- take many different forms, as there are different classes for each kind of request
(see the table below).
- Each request contains the information a connector needs to do the processing, and it
also is the place
- where the connector places the results (or the error, if one occurs).
- </para>
- <para>
- How do the requests reference a node (or nodes)? Since requests are coming from a
client, the client
- may identify a particular node using a &Location; object that is created with:
- <itemizedlist>
- <listitem>
- <para>the &Path; to the node; or</para>
- </listitem>
- <listitem>
- <para>one or more <emphasis>identification
properties</emphasis> that are likely source=specific
- and that are represented with &Property; objects; or</para>
- </listitem>
- <listitem>
- <para>a combination of both.</para>
- </listitem>
- </itemizedlist>
- So, when a client knows the path or the identification properties, they can create a
&Location;. However,
- all of the requests return &Location; objects, so often times the client simply
uses the location
- from a previous request. Since &Location; is an immutable class, it is perfectly
safe to reuse them.
- </para>
- <para>
- One more thing about locations: while the request may have an incomplete location
(e.g., a path but no
- identification properties), the connector is expected to set on the request the
<emphasis>actual</emphasis>
- location that contains the path and all identification properties. So as long as the
client
- reuses the actual locations in subsequent requests, the connectors will have the
benefit of having
- both the path and identification properties. Connectors can then be written to
leverage this
- information, although the connector should still perform as expected when requests
have incomplete locations.
- </para>
- <table frame='all'>
- <title>Types of Node Operation Requests</title>
- <tgroup cols='2' align='left' colsep='1'
rowsep='1'>
- <colspec colname='c1' colwidth="1*"/>
- <colspec colname='c2' colwidth="1*"/>
- <thead>
- <row>
- <entry>Name</entry>
- <entry>Description</entry>
- </row>
- </thead>
- <tbody>
- <row>
- <entry>ReadNodeRequest</entry>
- <entry>
- A request to read from the named workspace in the source a node's properties
and children.
- The node may be specified by path and/or by identification properties.
- The connector returns all properties and the locations for all children,
- or sets a &PathNotFoundException; error on the request if the node did not
exist in the workspace.
- If the node is found, the connector sets on the request the actual location of
the node (including the path and identification properties).
- The connector sets a &InvalidWorkspaceException; error on the request if the
named workspace does not exist.
- </entry>
- </row>
- <row>
- <entry>VerifyNodeExistsRequest</entry>
- <entry>
- A request to verify the existance of a node at the specified location in the
named workspace of the source.
- The connector returns all the actual location for the node if it exists, or
- sets a &PathNotFoundException; error on the request if the node does not
exist in the workspace.
- The connector sets a &InvalidWorkspaceException; error on the request if the
named workspace does not exist.
- </entry>
- </row>
- <row>
- <entry>ReadAllPropertiesRequest</entry>
- <entry>
- A request to read from the named workspace in the source all of the properties of
a node.
- The node may be specified by path and/or by identification properties.
- The connector returns all properties that were found on the node,
- or sets a &PathNotFoundException; error on the request if the node did not
exist in the workspace.
- If the node is found, the connector sets on the request the actual location of
the node (including the path and identification properties).
- The connector sets a &InvalidWorkspaceException; error on the request if the
named workspace does not exist.
- </entry>
- </row>
- <row>
- <entry>ReadPropertyRequest</entry>
- <entry>
- A request to read from the named workspace in the source a single property of a
node.
- The node may be specified by path and/or by identification properties,
- and the property is specified by name.
- The connector returns the property if found on the node,
- or sets a &PathNotFoundException; error on the request if the node or
property did not exist in the workspace.
- If the node is found, the connector sets on the request the actual location of
the node (including the path and identification properties).
- The connector sets a &InvalidWorkspaceException; error on the request if the
named workspace does not exist.
- </entry>
- </row>
- <row>
- <entry>ReadAllChildrenRequest</entry>
- <entry>
- A request to read from the named workspace in the source all of the children of a
node.
- The node may be specified by path and/or by identification properties.
- The connector returns an ordered list of locations for each child found on the
node,
- an empty list if the node had no children,
- or sets a &PathNotFoundException; error on the request if the node did not
exist in the workspace.
- If the node is found, the connector sets on the request the actual location of
the parent node (including the path and identification properties).
- The connector sets a &InvalidWorkspaceException; error on the request if the
named workspace does not exist.
- </entry>
- </row>
- <row>
- <entry>ReadBlockOfChildrenRequest</entry>
- <entry>
- A request to read from the named workspace in the source a block of children of a
node, starting with the n<superscript>th</superscript> children.
- This is designed to allow paging through the children, which is much more
efficient for large numbers of children.
- The node may be specified by path and/or by identification properties, and the
block
- is defined by a starting index and a count (i.e., the block size).
- The connector returns an ordered list of locations for each of the node's
children found in the block,
- or an empty list if there are no children in that range.
- The connector also sets on the request the actual location of the parent node
(including the path and identification properties)
- or sets a &PathNotFoundException; error on the request if the parent node did
not exist in the workspace.
- The connector sets a &InvalidWorkspaceException; error on the request if the
named workspace does not exist.
- </entry>
- </row>
- <row>
- <entry>ReadNextBlockOfChildrenRequest</entry>
- <entry>
- A request to read from the named workspace in the source a block of children of a
node, starting with the children that immediately follow
- a previously-returned child.
- This is designed to allow paging through the children, which is much more
efficient for large numbers of children.
- The node may be specified by path and/or by identification properties, and the
block
- is defined by the location of the node immediately preceding the block and a
count (i.e., the block size).
- The connector returns an ordered list of locations for each of the node's
children found in the block,
- or an empty list if there are no children in that range.
- The connector also sets on the request the actual location of the parent node
(including the path and identification properties)
- or sets a &PathNotFoundException; error on the request if the parent node did
not exist in the workspace.
- The connector sets a &InvalidWorkspaceException; error on the request if the
named workspace does not exist.
- </entry>
- </row>
- <row>
- <entry>ReadBranchRequest</entry>
- <entry>
- A request to read a portion of a subgraph that has as its root a particular node,
up to a maximum depth.
- This request is an efficient mechanism when a branch (or part of a branch) is to
be navigated and processed,
- and replaces some non-trivial code to read the branch iteratively using multiple
<code>ReadNodeRequest</code>s.
- The connector reads the branch to the specified maximum depth, returning the
properties and children for all
- nodes found in the branch.
- The connector also sets on the request the actual location of the branch's
root node (including the path and identification properties).
- The connector sets a &PathNotFoundException; error on the request if the node
at
- the top of the branch does not exist in the workspace.
- The connector sets a &InvalidWorkspaceException; error on the request if the
named workspace does not exist.
- </entry>
- </row>
- <row>
- <entry>CreateNodeRequest</entry>
- <entry>
- A request to create a node at the specified location and setting on the new node
the properties included in the request.
- The connector creates the node at the desired location, adjusting any
same-name-sibling indexes as required.
- (If an SNS index is provided in the new node's location, existing children
with the same name after that SNS index
- will have their SNS indexes adjusted. However, if the requested location does
not include a SNS index, the new
- node is added after all existing children, and it's SNS index is set
accordingly.)
- The connector also sets on the request the actual location of the new node
(including the path and identification properties)..
- The connector sets a &PathNotFoundException; error on the request if the
parent node does not exist in the workspace.
- The connector sets a &InvalidWorkspaceException; error on the request if the
named workspace does not exist.
- </entry>
- </row>
- <row>
- <entry>RemovePropertiesRequest</entry>
- <entry>
- A request to remove a set of properties on an existing node. The request
contains the location of the node as well as the
- names of the properties to be removed. The connector performs these changes and
sets on the request the
- actual location (including the path and identification properties) of the node.
- The connector sets a &PathNotFoundException; error on the request if the node
does not exist in the workspace.
- The connector sets a &InvalidWorkspaceException; error on the request if the
named workspace does not exist.
- </entry>
- </row>
- <row>
- <entry>UpdatePropertiesRequest</entry>
- <entry>
- A request to set or update properties on an existing node. The request contains
the location of the node as well as the
- properties to be set and those to be deleted. The connector performs these
changes and sets on the request the
- actual location (including the path and identification properties) of the node.
- The connector sets a &PathNotFoundException; error on the request if the node
does not exist in the workspace.
- The connector sets a &InvalidWorkspaceException; error on the request if the
named workspace does not exist.
- </entry>
- </row>
- <row>
- <entry>RenameNodeRequest</entry>
- <entry>
- A request to change the name of a node. The connector changes the node's
name, adjusts all SNS indexes
- accordingly, and returns the actual locations (including the path and
identification properties) of both the original
- location and the new location.
- The connector sets a &PathNotFoundException; error on the request if the node
does not exist in the workspace.
- The connector sets a &InvalidWorkspaceException; error on the request if the
named workspace does not exist.
- </entry>
- </row>
- <row>
- <entry>CopyBranchRequest</entry>
- <entry>
- A request to copy a portion of a subgraph that has as its root a particular node,
up to a maximum depth.
- The request includes the name of the workspace where the original node is located
as well as the name of the
- workspace where the copy is to be placed (these may be the same, but may be
different).
- The connector copies the branch from the original location, up to the specified
maximum depth, and places a copy
- of the node as a child of the new location.
- The connector also sets on the request the actual location (including the path
and identification properties)
- of the original location as well as the location of the new copy.
- The connector sets a &PathNotFoundException; error on the request if the node
at
- the top of the branch does not exist in the workspace.
- The connector sets a &InvalidWorkspaceException; error on the request if one
of the named workspaces does not exist.
- </entry>
- </row>
- <row>
- <entry>MoveBranchRequest</entry>
- <entry>
- A request to move a subgraph that has a particular node as its root.
- The connector moves the branch from the original location and places it as child
of the specified new location.
- The connector also sets on the request the actual location (including the path
and identification properties)
- of the original and new locations. The connector will adjust SNS indexes
accordingly.
- The connector sets a &PathNotFoundException; error on the request if the node
that is to be moved or the
- new location do not exist in the workspace.
- The connector sets a &InvalidWorkspaceException; error on the request if the
named workspace does not exist.
- </entry>
- </row>
- <row>
- <entry>DeleteBranchRequest</entry>
- <entry>
- A request to delete an entire branch specified by a single node's location.
- The connector deletes the specified node and all nodes below it, and sets the
actual location,
- including the path and identification properties, of the node that was deleted.
- The connector sets a &PathNotFoundException; error on the request if the node
being deleted does not exist in the workspace.
- The connector sets a &InvalidWorkspaceException; error on the request if the
named workspace does not exist.
- </entry>
- </row>
- <row>
- <entry>CompositeRequest</entry>
- <entry>
- A request that actually comprises multiple requests (none of which will be a
composite).
- The connector simply processes all of the requests in the composite request, but
should set on the composite
- request any error (usually the first error) that occurs during processing of the
contained requests.
- </entry>
- </row>
- </tbody>
- </tgroup>
- </table>
- <para>There are also requests that deal with workspaces:</para>
- <table frame='all'>
- <title>Types of Workspace Requests</title>
- <tgroup cols='2' align='left' colsep='1'
rowsep='1'>
- <colspec colname='c1' colwidth="1*"/>
- <colspec colname='c2' colwidth="1*"/>
- <thead>
- <row>
- <entry>Name</entry>
- <entry>Description</entry>
- </row>
- </thead>
- <tbody>
- <row>
- <entry>GetWorkspacesRequest</entry>
- <entry>
- A request to obtain the names of the existing workspaces that are accessible to
the caller.
- </entry>
- </row>
- <row>
- <entry>VerifyWorkspaceRequest</entry>
- <entry>
- A request to verify that a workspace with a particular name exists.
- The connector returns the actual location for the root node if the workspace
exists, as well as the actual name of the workspace
- (e.g., the default workspace name if a null name is supplied).
- </entry>
- </row>
- <row>
- <entry>CreateWorkspaceRequest</entry>
- <entry>
- A request to create a workspace with a particular name.
- The connector returns the actual location for the root node if the workspace
exists, as well as the actual name of the workspace
- (e.g., the default workspace name if a null name is supplied).
- The connector sets a &InvalidWorkspaceException; error on the request if the
named workspace already exists.
- </entry>
- </row>
- <row>
- <entry>DestroyWorkspaceRequest</entry>
- <entry>
- A request to destroy a workspace with a particular name.
- The connector sets a &InvalidWorkspaceException; error on the request if the
named workspace does not exist.
- </entry>
- </row>
- <row>
- <entry>CloneWorkspaceRequest</entry>
- <entry>
- A request to clone one named workspace as another new named workspace.
- The connector sets a &InvalidWorkspaceException; error on the request if the
original workspace does not exist,
- or if the new workspace already exists.
- </entry>
- </row>
- </tbody>
- </tgroup>
- </table>
- <para>
- Although there are over a dozen different kinds of requests, we do anticipate adding
more in future releases.
- For example, DNA will likely support searching repository content in sources through
an additional subclass of &Request;.
- Getting the version history for a node will likely be another kind of request added
in an upcoming release.
- </para>
- <para>
- A connector is technically free to implement the
<code>execute(...)</code> method in any way, as long as the semantics
- are maintained. But DNA provides a &RequestProcessor; class that can simplify
writing your own connector and at the
- same time help insulate your connector from new kinds of requests that may be added
in the future. The &RequestProcessor;
- is an abstract class that defines a <code>process(...)</code> method for
each concrete &Request; subclass.
- In other words, there is a <code>process(CompositeRequest)</code> method,
a <code>process(ReadNodeRequest)</code> method,
- and so on.
- </para>
- <para>
- To use this in your connector, simply create a subclass of &RequestProcessor;,
overriding all of the abstract methods and optionally
- overriding any of the other methods that have a default implementation.
- </para>
- <note>
- <para>
- The &RequestProcessor; abstract class contains default implementations for quite
a few of the <code>process(...)</code> methods,
- and these will be <emphasis>sufficient</emphasis> but probably not
efficient or optimum. If you can provide a more efficient
- implementation given your source, feel free to do so. However, if performance is
not a big issue, all of the concrete methods
- will provide the correct behavior. Keep things simple to start out - you can always
provide better implementations later.
- </para>
- </note>
- <para>
- Then, in your connector's <code>execute(&ExecutionContext;,
&Request;)</code> method, instantiate your &RequestProcessor; subclass
- and call its <code>process(&Request;) method, passing in the
<code>execute(...)</code> method's &Request; parameter.</code>
- The &RequestProcessor; will determine the appropriate method given the actual
&Request; object and will then invoke that method:
- </para>
- <programlisting>
-public void execute( final &ExecutionContext; context,
- final &Request; request ) throws RepositorySourceException {
- RequestProcessor processor = new RequestProcessor(context);
- try {
- processor.process(request);
- } finally {
- processor.close();
- }
-}</programlisting>
- <para>
- If you do this, the bulk of your connector implementation may be in the
&RequestProcessor; implementation methods.
- This not only is pretty maintainable, it also lends itself to easier testing. And
should any new request types be added
- in the future, your connector may work just fine without any changes. In fact, if
the &RequestProcessor; class
- can implement meaningful methods for those new request types, your connector may
"just work". Or, at least
- your connector will still be binary compatible, even if your connector won't
support any of the new features.
- </para>
- <para>
- Finally, how should the connector handle exceptions? As mentioned above, each
&Request; object has a slot where the connector
- can set any exception encountered during processing. This not only handles the
exception, but in the case of &CompositeRequest;s
- it also correctly associates the problem with the request. However, it is perfectly
acceptable to throw an exception
- if the connection becomes invalid (e.g., there is a communication failure) or if a
fatal error would prevent subsequent
- requests from being processed.
- </para>
- </sect2>
- <sect2 id="testing_custom_connectors">
- <title>Testing custom connectors</title>
- <para>
- Testing connectors is not really that much different than testing other classes.
Using mocks may help to isolate your
- instances so you can create more unit tests that don't require the underlying
source system.
- </para>
- <para>
- However, there may be times when you have to use the underlying source system in your
tests. If this is the case,
- we recommend using Maven integration tests, which run at a different point in the
Maven lifecycle. The benefit of
- using integration tests is that by convention they're able to rely upon external
systems. Plus, your unit tests
- don't become polluted with slow-running tests that break if the external system
is not available.
- </para>
- </sect2>
- <sect2 id="deploying_custom_connectors">
- <title>Configuring and deploying custom connectors</title>
- <para>
- After building your connector project, you need to configure the JBoss DNA components
your application is using so
- that they use your connector. In a lot of cases, this will entail instantiating your
connector's &RepositorySource; class,
- setting the various properties, and registering it with a &RepositoryLibrary;.
Or, it will entail using a configuration
- repository to use your source and letting &RepositoryService; instantiate and set
up your &RepositorySource; instance.
- Or, you can just instantiate and set it up manually, passing the instance to whatever
component needs it.
- </para>
- <para>
- And of course you have to make the JAR file containing your connector (as well as any
dependency JARs) available to
- your application's classpath.
- </para>
- </sect2>
- </sect1>
- <sect1 id="dna_graph_api">
- <title>Graph API for using connectors</title>
- <para>
- So far we've talked about repositories, repository connectors, and how connectors
respond to the different kinds of requests.
- Normally you'd code to the JCR API and use our JCR implementation. However, what
does your code look like if you want
- to use the connectors directly, without using our JCR implementation? After all, you
may be a contributor to JBoss DNA,
- or you may want to take advantage of our connectors without all the overhead of JCR.
- </para>
- <para>
- One option, of course, is to explicitly create the different requests and pass them to
the connector's <code>execute(...)</code> method.
- While this is the most efficient approach (and one taken in some key DNA components),
you probably want something that
- is much less verbose and much easier to use. This is where the DNA graph API comes
in.
- </para>
- <para>
- JBoss DNA's <emphasis>Graph API</emphasis> was designed as a
lightweight public API for working with graph information,
- and it insulates components from the underlying requests and interacting with
connectors.
- The &Graph; class is the primary class in API, and each instance represents a
single, independent
- view of the graph of content from a single connector. &Graph; instances return
snapshots of state, and those snapshots
- never change after they're retrieved. To obtain a &Graph; instance, use the
static <code>create(...)</code>
- method, supplying the name of the source, a &RepositoryConnectionFactory; from
which a &RepositoryConnection; can be obtained,
- and the &ExecutionContext;.
- </para>
- <para>
- The &Graph; class basically represents an <ulink
url="http://www.martinfowler.com/bliki/DomainSpecificLanguage.html&q...
domain specific language (DSL)</ulink>,
- designed to be easy to use in an application.
- The Graph API makes extensive use of interfaces and method chaining, so that methods
return a concise interface that has only those
- methods that make sense at that point. In fact, this should be really easy if your
IDE has code completion.
- Just remember that under the covers, a &Graph; is just building &Request;
objects, submitting them to the connector,
- and then exposing the results.
- </para>
- <sect2 id="dna_graph_api_workspaces">
- <title>Using workspaces</title>
- <para>
- Let's look at some examples of how the Graph API works. This first example shows
how to obtain the names of the available workspaces:
- </para>
- <programlisting>
-&Set;<&String;> workspaceNames = graph.getWorkspaces();
-</programlisting>
- <para>Once you know the name of the workspace, you can specify that the graph
should use it:
- </para>
- <programlisting>
-graph.useWorkspace("myWorkspace");
-</programlisting>
- <para>
- From this point forward, all requests will apply to the workspace named
"myWorkspace". At any time, you can use a different workspace,
- which will affect all subsequent requests made using the graph. Of course, creating a
new workspace is just as easy:
- </para>
- <programlisting>
-graph.createWorkspace().named("newWorkspace");
-</programlisting>
- <para>This will attempt to create a workspace named "newWorkspace",
which will fail if that workspace already exists. You may
- want to create a new workspace with a name that should be altered if the name you
supply is already used. The following code shows
- how you can do this:
- </para>
- <programlisting>
-graph.createWorkspace().namedSomethingLike("newWorkspace");
-</programlisting>
- <para>If there is no existing workspace named "newWorkspace", a new
one will be created with this name. However, if "newWorkspace" already
- exists, this call will create a workspace with a name that is some alteration of the
supplied name.
- </para>
- <para>
- You can also clone workspaces, too:
- </para>
- <programlisting>
-graph.createWorkspace().clonedFrom("original").named("something");
-</programlisting>
- <para>
- or
- </para>
- <programlisting>
-graph.createWorkspace().clonedFrom("original").namedSomethingLike("something");
-</programlisting>
- <para>
- As you can see, it's very easy to specify which workspace you want to use or to
create new workspaces. You can also find out which workspace
- the graph is currently using:
- </para>
- <programlisting>
-&String; current = graph.getCurrentWorkspaceName();
-</programlisting>
- <para>or, if you want, you can get more information about the workspace:
- </para>
- <programlisting>
-&Workspace; current = graph.getCurrentWorkspace();
-&String; name = current.getName();
-&Location; rootLocation = current.getRoot();
-</programlisting>
- </sect2>
- <sect2 id="dna_graph_api_nodes">
- <title>Working with nodes</title>
- <para>
- Now let's switch to working with nodes. This first example returns a map of
properties (keyed by property name)
- for a node at a specific &Path;:
- </para>
- <programlisting>
-&Path; path = ...
-Map<&Name;,&Property;> propertiesByName =
graph.getPropertiesByName().on(path);
-</programlisting>
- <para>
- This next example shows how the graph can be used to obtain and loop over the
properties of a node:
- </para>
- <programlisting>
-&Path; path = ...
-for ( &Property; property : graph.getProperties().on(path) ) {
- ...
-}
-</programlisting>
- <para>
- Likewise, the next example shows how the graph can be used to obtain and loop over the
children of a node:
- </para>
- <programlisting>
-&Path; path = ...
-for ( &Location; child : graph.getChildren().of(path) ) {
- &Path; childPath = child.getPath();
- ...
-}
-</programlisting>
- <para>
- Notice that the examples pass a &Path; instance to the
<code>on(...)</code> and <code>of(...)</code> methods. Many
- of the Graph API methods take a variety of parameter types, including String,
&Path;s, &Location;s, &UUID;, or &Property; parameters.
- This should make it easy to use in many different situations.
- </para>
- <para>
- Of course, changing content is more interesting and offers more interesting
possibilities. Here are a few examples:
- </para>
- <programlisting>
-&Path; path = ...
-&Location; location = ...
-&Property; idProp1 = ...
-&Property; idProp2 = ...
-&UUID; uuid = ...
-graph.move(path).into(idProp1, idProp2);
-graph.copy(path).into(location);
-graph.delete(uuid);
-graph.delete(idProp1,idProp2);
-</programlisting>
- <para>
- The methods shown above work immediately, as soon as each request is built. However,
there is another way to use
- the &Graph; object, and that is in a <emphasis>batch</emphasis> mode.
Simply create a &GraphBatch; object using the
- <code>batch()</code> method, create the requests on that batch object, and
then execute all of the commands on the
- batch by calling its <code>execute()</code> method. That
<code>execute()</code> method returns a &Results; interface
- that can be used to read the node information retrieved by the batched requests.
- </para>
- <para>
- Method chaining works really well with the batch mode, since multiple commands can be
assembled together very easily:
- </para>
- <programlisting>
-&Path; path = ...
-String path2 = ...
-&Location; location = ...
-&Property; idProp1 = ...
-&Property; idProp2 = ...
-&UUID; uuid = ...
-graph.batch().move(path).into(idProp1,
idProp2).and().copy(path2).into(location).and().delete(uuid).execute();
-&Results; results = graph.batch().read(path2).
- and().readChildren().of(idProp1,idProp2).
- and().readSugraphOfDepth(3).at(uuid2).
- execute();
-for ( &Location; child : results.getNode(path2) ) {
- ...
-}
-</programlisting>
- <para>
- Of course, this section provided just a hint of the Graph API.
- The &Graph; interface is actually quite complete and offers a full-featured
approach for reading and updating a graph.
- For more information, see the &Graph; JavaDocs.
- </para>
- </sect2>
- </sect1>
- <sect1>
- <title>Summary</title>
- <para>
- In this chapter, we covered all the aspects of JBoss DNA repositories, including the
connector framework,
- how DNA's JCR implementation works with connectors, what connectors are available
(and how to use them),
- and how to write your own connector. So now that you know how to set up and use JBoss
DNA repositories,
- the <link linkend="jcr">next chapter</link> describes how you
can leverage JBoss DNA's JCR implementation.
- </para>
- </sect1>
-</chapter>
Added: trunk/docs/reference/src/main/docbook/en-US/content/sequencers/zip.xml
===================================================================
--- trunk/docs/reference/src/main/docbook/en-US/content/sequencers/zip.xml
(rev 0)
+++ trunk/docs/reference/src/main/docbook/en-US/content/sequencers/zip.xml 2009-06-08
14:57:17 UTC (rev 995)
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ JBoss DNA (
http://www.jboss.org/dna)
+ ~
+ ~ See the COPYRIGHT.txt file distributed with this work for information
+ ~ regarding copyright ownership. Some portions may be licensed
+ ~ to Red Hat, Inc. under one or more contributor license agreements.
+ ~ See the AUTHORS.txt file in the distribution for a full listing of
+ ~ individual contributors.
+ ~
+ ~ JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
+ ~ is licensed to you under the terms of the GNU Lesser General Public License as
+ ~ published by the Free Software Foundation; either version 2.1 of
+ ~ the License, or (at your option) any later version.
+ ~
+ ~ JBoss DNA is distributed in the hope that it will be useful,
+ ~ but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ ~ or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ ~ for more details.
+ ~
+ ~ You should have received a copy of the GNU Lesser General Public License
+ ~ along with this distribution; if not, write to:
+ ~ Free Software Foundation, Inc.
+ ~ 51 Franklin Street, Fifth Floor
+ ~ Boston, MA 02110-1301 USA
+ -->
+<!DOCTYPE preface PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
"http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd" [
+<!ENTITY % CustomDTD SYSTEM "../../custom.dtd">
+%CustomDTD;
+]>
+<chapter id="zip-file-sequencer">
+ <title>ZIP File Sequencer</title>
+ <para>
+ blah blah
+ </para>
+</chapter>
\ No newline at end of file
Property changes on:
trunk/docs/reference/src/main/docbook/en-US/content/sequencers/zip.xml
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Deleted: trunk/docs/reference/src/main/docbook/en-US/content/sequencing.xml
===================================================================
--- trunk/docs/reference/src/main/docbook/en-US/content/sequencing.xml 2009-06-08 00:52:09
UTC (rev 994)
+++ trunk/docs/reference/src/main/docbook/en-US/content/sequencing.xml 2009-06-08 14:57:17
UTC (rev 995)
@@ -1,793 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ JBoss DNA (
http://www.jboss.org/dna)
- ~
- ~ See the COPYRIGHT.txt file distributed with this work for information
- ~ regarding copyright ownership. Some portions may be licensed
- ~ to Red Hat, Inc. under one or more contributor license agreements.
- ~ See the AUTHORS.txt file in the distribution for a full listing of
- ~ individual contributors.
- ~
- ~ JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
- ~ is licensed to you under the terms of the GNU Lesser General Public License as
- ~ published by the Free Software Foundation; either version 2.1 of
- ~ the License, or (at your option) any later version.
- ~
- ~ JBoss DNA is distributed in the hope that it will be useful,
- ~ but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- ~ or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
- ~ for more details.
- ~
- ~ You should have received a copy of the GNU Lesser General Public License
- ~ along with this distribution; if not, write to:
- ~ Free Software Foundation, Inc.
- ~ 51 Franklin Street, Fifth Floor
- ~ Boston, MA 02110-1301 USA
- -->
-<!DOCTYPE preface PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
"http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd" [
-<!ENTITY % CustomDTD SYSTEM "../custom.dtd">
-%CustomDTD;
-]>
-<chapter id="sequencing">
- <title>Sequencing content</title>
- <para>As we've mentioned before, JBoss DNA is able to work with existing JCR
repositories. Your client applications
- make changes to the information in those repositories, and JBoss DNA automatically
uses its sequencers to extract
- additional information from the uploaded files.</para>
- <para>
- This chapter discusses the sequencing features of JBoss DNA and the components that are
involved.
- </para>
- <sect1 id="sequencing-service">
- <title>Sequencing Service</title>
- <para>The JBoss DNA <emphasis>sequencing service</emphasis> is the
component that manages the <emphasis>sequencers</emphasis>,
- reacting to changes in JCR repositories and then running the appropriate sequencers.
- This involves processing the changes on a node, determining which (if any)
sequencers should be run on that node,
- and for each sequencer constructing the execution environment, calling the
sequencer, and saving the information
- generated by the sequencer.</para>
- <note>
- <para>Configuring JBoss DNA services is a bit more manual than is ideal. As
you'll see, JBoss DNA uses dependency
- injection to allow a great deal of flexibility in how it can be configured and
customized. But this flexibility
- makes it more difficult for you to use. We understand this, and will soon provide
a much easier way to set up
- and manage JBoss DNA. Current plans are to use the <ulink
url="http://www.jboss.org/jbossmc">JBoss Microcontainer</ulink>
- along with a configuration repository.</para>
- </note>
- <para>To set up the sequencing service, an instance is created, and dependent
components are injected into
- the object. This includes among other things:
- <itemizedlist>
- <listitem>
- <para>An <emphasis>execution context</emphasis> that defines the
context in which the service runs, including
- a factory for JCR sessions given names of the repository and workspace. This
factory must be configured,
- and is how JBoss DNA knows about your JCR repositories and how to connect to
them. More on this a bit later.</para>
- </listitem>
- <listitem>
- <para>An optional <emphasis>factory for class loaders</emphasis>
used to load sequencers. If no factory is supplied,
- the service uses the current thread's context class loader (or if that is
null, the class loader that loaded the
- sequencing service class).</para>
- </listitem>
- <listitem>
- <para>An &ExecutorService; used to execute the sequencing activites. If
none
- is supplied, a new single-threaded executor is created by calling
<code>Executors.newSingleThreadExecutor()</code>.
- (This can easily be changed by subclassing and overriding the
<code>SequencerService.createDefaultExecutorService()</code>
method.)</para>
- </listitem>
- <listitem>
- <para>Filters for sequencers and events. By default, all sequencers are
considered for "node added", "property added"
- and "property changed" events.</para>
- </listitem>
- </itemizedlist>
- </para>
- <para>As mentioned above, the &JcrExecutionContext; provides access to a
&SessionFactory; that is used
- by JBoss DNA to establish sessions to your JCR repositories. Two implementations
are available:
- <itemizedlist>
- <listitem>
- <para>The &JndiSessionFactory;> looks up JCR &Repository; instances
in JNDI using
- names that are supplied when creating sessions. This implementation also
has methods to set the
- JCR &Credentials; for a given workspace name.</para>
- </listitem>
- <listitem>
- <para>The &SimpleSessionFactory; has methods to register the JCR
&Repository; instances
- with names, as well as methods to set the JCR &Credentials; for a given
workspace name.</para>
- </listitem>
- </itemizedlist>
- You can use the &JcrExecutionContext; and use one of these &SessionFactory;
implementations or another
- implementation that you provide.</para>
- <para>Here's an example of how to instantiate and configure the
&SequencingService;:</para>
- <programlisting>
-&SimpleSessionFactory; sessionFactory = new &SimpleSessionFactory;();
-sessionFactory.registerRepository("Main Repository", this.repository);
-&Credentials; credentials = new &SimpleCredentials;("jsmith",
"secret".toCharArray());
-sessionFactory.registerCredentials("Main Repository/Workspace1", credentials);
-// Now create the JCR execution context, with a reference to the session factory
-// and the name of the repository from which sessions will be obtained ...
-ExecutionContext executionContext = new
&JcrExecutionContext;(sessionFactory,"Main Repository");
-
-// Create the sequencing service, passing in the execution context ...
-&SequencingService; sequencingService = new &SequencingService;();
-sequencingService.setExecutionContext(executionContext);
-</programlisting>
- <para>After the sequencing service is created and configured, it must be started.
The &SequencingService;
- has an <emphasis>administration object</emphasis> (that is an instance
of &ServiceAdministrator;)
- with <code>start()</code>, <code>pause()</code>, and
<code>shutdown()</code> methods. The latter method will
- close the queue for sequencing, but will allow sequencing operations already
running to complete normally.
- To wait until all sequencing operations have completed, simply call the
<code>awaitTermination</code> method
- and pass it the maximum amount of time you want to wait.</para>
- <programlisting>
-sequencingService.getAdministrator().start();
-</programlisting>
- <para>The JBoss DNA services are utilizing resources and threads that must be
released before your application is ready to shut down.
- The safe way to do this is to simply obtain the &ServiceAdministrator; for each
service (via the <code>getServiceAdministrator()</code> method)
- and call <code>shutdown()</code>. As previously mentioned, the shutdown
method will simply prevent new work from being processed
- and will not wait for existing work to be completed. If you want to wait until the
service completes all its work, you must wait
- until the service terminates. Here's an example that shows how this is
done:</para>
- <programlisting>
-// Shut down the service and wait until it's all shut down ...
-sequencingService.getAdministrator().shutdown();
-sequencingService.getAdministrator().awaitTermination(5, TimeUnit.SECONDS);
-
-// Shut down the observation service ...
-observationService.getAdministrator().shutdown();
-observationService.getAdministrator().awaitTermination(5, TimeUnit.SECONDS);
-</programlisting>
- </sect1>
- <sect1 id="sequencer-configuration">
- <title>Sequencer Configurations</title>
- <para>The sequencing service must also be configured with the sequencers that it
will use. This is done using the
- <code>addSequencer(SequencerConfig)</code> method and passing a
&SequencerConfig; instance that
- you create. Here's the code that defines 3 sequencer configurations: 1 that
places image metadata into
- "<code><![CDATA[/images/<filename>]]></code>",
another that places MP3 metadata into
"<code><![CDATA[/mp3s/<filename>]]></code>",
- and a third that places a structure that represents the classes, methods, and
attributes found within Java source into
-
"<code><![CDATA[/java/<filename>]]></code>".</para>
- <programlisting>
-String name = "Image Sequencer";
-String desc = "Sequences image files to extract the characteristics of the
image";
-String classname = "org.jboss.dna.sequencer.images.ImageMetadataSequencer";
-String[] classpath = null; // Use the current classpath
-String[] pathExpressions =
{"//(*.(jpg|jpeg|gif|bmp|pcx|png)[*])/jcr:content[@jcr:data] =>
/images/$1"};
-&SequencerConfig; imageSequencerConfig = new &SequencerConfig;(name, desc,
classname,
- classpath, pathExpressions);
-sequencingService.addSequencer(imageSequencerConfig);
-
-name = "MP3 Sequencer";
-desc = "Sequences MP3 files to extract the ID3 tags from the audio file";
-classname = "org.jboss.dna.sequencer.mp3.Mp3MetadataSequencer";
-pathExpressions = {"//(*.mp3[*])/jcr:content[@jcr:data] => /mp3s/$1"};
-&SequencerConfig; mp3SequencerConfig = new &SequencerConfig;(name, desc,
classname,
- classpath, pathExpressions);
-sequencingService.addSequencer(mp3SequencerConfig);
-
-name = "Java Sequencer";
-desc = "Sequences java files to extract the characteristics of the Java
source";
-classname = "org.jboss.dna.sequencer.java.JavaMetadataSequencer";
-pathExpressions = {"//(*.java[*])/jcr:content[@jcr:data] => /java/$1"};
-&SequencerConfig; javaSequencerConfig = new &SequencerConfig;(name, desc,
classname,
- classpath, pathExpressions);
-this.sequencingService.addSequencer(javaSequencerConfig);
-</programlisting>
- <para>Each configuration defines several things, including the name,
description, and sequencer implementation class.
- The configuration also defines the classpath information, which can be passed to the
&ClassLoaderFactory; to get
- a Java &ClassLoader; with which the sequencer class can be loaded. (If no
classpath information is provided, as is done
- in the code above, the application class loader is used.) The configuration also
specifies the path expressions that
- identify the nodes that should be sequenced with the sequencer and where to store
the output generated by the sequencer.
- Path expressions are pretty straightforward but are quite powerful, so before we go
any further with the example,
- let's dive into path expressions in more detail.</para>
- <sect2 id="path_expressions">
- <title>Path Expressions</title>
- <para>Path expressions consist of two parts: a selection criteria (or an input
path) and an output path:</para>
- <programlisting><![CDATA[ inputPath => outputPath
]]></programlisting>
- <para>The <emphasis>inputPath</emphasis> part defines an expression
for the path of a node that is to be sequenced.
- Input paths consist of '<code>/</code>' separated segments,
where each segment represents a pattern for a single node's
- name (including the same-name-sibling indexes) and
'<code>@</code>' signifies a property name.</para>
- <para>Let's first look at some simple examples:</para>
- <table frame='all'>
- <title>Simple Input Path Examples</title>
- <tgroup cols='2' align='left' colsep='1'
rowsep='1'>
- <colspec colname='c1' colwidth="1*"/>
- <colspec colname='c2' colwidth="1*"/>
- <thead>
- <row>
- <entry>Input Path</entry>
- <entry>Description</entry>
- </row>
- </thead>
- <tbody>
- <row><entry>/a/b</entry><entry>Match node
"<code>b</code>" that is a child of the top level node
"<code>a</code>". Neither node
- may have any same-name-sibilings.</entry></row>
- <row><entry>/a/*</entry><entry>Match any child node of the
top level node "<code>a</code>".</entry></row>
- <row><entry>/a/*.txt</entry><entry>Match any child node of
the top level node "<code>a</code>" that also has a name ending in
"<code>.txt</code>".</entry></row>
- <row><entry>/a/*.txt</entry><entry>Match any child node of
the top level node "<code>a</code>" that also has a name ending in
"<code>.txt</code>".</entry></row>
- <row><entry>/a/b@c</entry><entry>Match the property
"<code>c</code>" of node
"<code>/a/b</code>".</entry></row>
- <row><entry>/a/b[2]</entry><entry>The second child named
"<code>b</code>" below the top level node
"<code>a</code>".</entry></row>
- <row><entry>/a/b[2,3,4]</entry><entry>The second, third or
fourth child named "<code>b</code>" below the top level node
"<code>a</code>".</entry></row>
- <row><entry>/a/b[*]</entry><entry>Any (and every) child
named "<code>b</code>" below the top level node
"<code>a</code>".</entry></row>
- <row><entry>//a/b</entry><entry>Any node named
"<code>b</code>" that exists below a node named
"<code>a</code>", regardless
- of where node "<code>a</code>" occurs. Again, neither
node may have any same-name-sibilings.</entry></row>
- </tbody>
- </tgroup>
- </table>
- <para>With these simple examples, you can probably discern the most important
rules. First, the '<code>*</code>' is a wildcard character
- that matches any character or sequence of characters in a node's name (or index
if appearing in between square brackets), and
- can be used in conjunction with other characters (e.g.,
"<code>*.txt</code>").</para>
- <para>Second, square brackets (i.e., '<code>[</code>' and
'<code>]</code>') are used to match a node's same-name-sibiling
index.
- You can put a single non-negative number or a comma-separated list of non-negative
numbers. Use '0' to match a node that has no
- same-name-sibilings, or any positive number to match the specific
same-name-sibling.</para>
- <para>Third, combining two delimiters (e.g.,
"<code>//</code>") matches any sequence of nodes, regardless of what
their names are
- or how many nodes. Often used with other patterns to identify nodes at any level
matching other patterns.
- Three or more sequential slash characters are treated as two.</para>
- <para>Many input paths can be created using just these simple rules. However,
input paths can be more complicated. Here are some
- more examples:</para>
- <table frame='all'>
- <title>More Complex Input Path Examples</title>
- <tgroup cols='2' align='left' colsep='1'
rowsep='1'>
- <colspec colname='c1' colwidth="1*"/>
- <colspec colname='c2' colwidth="1*"/>
- <thead>
- <row>
- <entry>Input Path</entry>
- <entry>Description</entry>
- </row>
- </thead>
- <tbody>
- <row><entry>/a/(b|c|d)</entry><entry>Match children of the
top level node "<code>a</code>" that are named
"<code>a</code>",
- "<code>b</code>" or
"<code>c</code>". None of the nodes may have same-name-sibling
indexes.</entry></row>
- <row><entry>/a/b[c/d]</entry><entry>Match node
"<code>b</code>" child of the top level node
"<code>a</code>", when node
- "<code>b</code>" has a child named
"<code>c</code>", and "<code>c</code>" has a
child named "<code>d</code>".
- Node "<code>b</code>" is the selected node, while nodes
"<code>b</code>" and "<code>b</code>" are used
as criteria but are not
- selected.</entry></row>
- <row><entry>/a(/(b|c|d|)/e)[f/g/@something]</entry><entry>Match
node "<code>/a/b/e</code>",
"<code>/a/c/e</code>", "<code>/a/d/e</code>",
- or "<code>/a/e</code>" when they also have a child
"<code>f</code>" that itself has a child
"<code>g</code>" with property
- "<code>something</code>". None of the nodes may have
same-name-sibling indexes.</entry></row>
- </tbody>
- </tgroup>
- </table>
- <para>These examples show a few more advanced rules. Parentheses (i.e.,
'<code>(</code>' and '<code>)</code>') can be
used
- to define a set of options for names, as shown in the first and third rules.
Whatever part of the selected node's path
- appears between the parentheses is captured for use within the output path. Thus,
the first input path in the previous table
- would match node "<code>/a/b</code>", and "b" would
be captured and could be used within the output path using
"<code>$1</code>",
- where the number used in the output path identifies the parentheses.</para>
- <para>Square brackets can also be used to specify criteria on a node's
properties or children. Whatever appears in between the square
- brackets does not appear in the selected node.</para>
- <para>Let's go back to the previous code fragment and look at the first
path expression:</para>
- <programlisting><![CDATA[
//(*.(jpg|jpeg|gif|bmp|pcx|png)[*])/jcr:content[@jcr:data] => /images/$1
]]></programlisting>
- <para>This matches a node named
"<code>jcr:content</code>" with property
"<code>jcr:data</code>" but no siblings with the same name,
- and that is a child of a node whose name ends with
"<code>.jpg</code>", "<code>.jpeg</code>",
"<code>.gif</code>", "<code>.bmp</code>",
"<code>.pcx</code>",
- or "<code>.png</code>" that may have any same-name-sibling
index. These nodes can appear at any level in the repository.
- Note how the input path capture the filename (the segment containing the file
extension), including any same-name-sibling index.
- This filename is then used in the output path, which is where the sequenced content
is placed.</para>
- </sect2>
- </sect1>
- <sect1 id="sequencers">
- <title>JBoss DNA Sequencers</title>
- <para>
- JBoss DNA includes a number of sequencers "out of the box". These
sequencers can be used within your application to sequence
- a variety of common file formats. To use them, the only thing you have to do is
define the appropriate sequencer configurations
- and include the appropriate JAR files.
- </para>
- <sect2 id="dna-sequencer-images">
- <title>Image sequencer</title>
- <para>
- A sequencer that extracts metadata from JPEG, GIF, BMP, PCX, PNG, IFF, RAS, PBM, PGM,
PPM and PSD image files.
- This sequencer extracts the file format, image resolution, number of bits per pixel
and optionally number of images, comments
- and physical resolution, and then writes this information into the repository using
the following structure:
- </para>
- <itemizedlist>
- <listitem>
- <para>
- <emphasis role="strong">image:metadata</emphasis> node of
type <code>image:metadata</code>
- </para>
- </listitem>
- <listitem>
- <itemizedlist>
- <listitem>
- <para><emphasis role="strong">jcr:mimeType</emphasis>
- optional string property for the mime type of the image</para>
- </listitem>
- <listitem>
- <para><emphasis role="strong">jcr:encoding</emphasis>
- optional string property for the encoding of the image</para>
- </listitem>
- <listitem>
- <para><emphasis
role="strong">image:formatName</emphasis> - string property for the
name of the format</para>
- </listitem>
- <listitem>
- <para><emphasis role="strong">image:width</emphasis> -
optional integer property for the image's width in pixels</para>
- </listitem>
- <listitem>
- <para><emphasis role="strong">image:height</emphasis>
- optional integer property for the image's height in pixles</para>
- </listitem>
- <listitem>
- <para><emphasis
role="strong">image:bitsPerPixel</emphasis> - optional integer property
for the number of bits per pixel</para>
- </listitem>
- <listitem>
- <para><emphasis
role="strong">image:progressive</emphasis> - optional boolean property
specifying whether the image is stored in a progressive
- (i.e., interlaced) form</para>
- </listitem>
- <listitem>
- <para><emphasis
role="strong">image:numberOfImages</emphasis> - optional integer
property for the number of images stored in the file; defaults
- to 1</para>
- </listitem>
- <listitem>
- <para><emphasis
role="strong">image:physicalWidthDpi</emphasis> - optional integer
property for the physical width of the image in dots per inch</para>
- </listitem>
- <listitem>
- <para><emphasis
role="strong">image:physicalHeightDpi</emphasis> - optional integer
property for the physical height of the image in dots per
- inch</para>
- </listitem>
- <listitem>
- <para><emphasis
role="strong">image:physicalWidthInches</emphasis> - optional double
property for the physical width of the image in inches</para>
- </listitem>
- <listitem>
- <para><emphasis
role="strong">image:physicalHeightInches</emphasis> - optional double
property for the physical height of the image in inches</para>
- </listitem>
- </itemizedlist>
- </listitem>
- </itemizedlist>
- <para>
- This structure could be extended in the future to add EXIF and IPTC metadata as child
nodes. For example, EXIF metadata is
- structured as tags in directories, where the directories form something like
namespaces, and which are used by different camera
- vendors to store custom metadata. This structure could be mapped with each directory
(e.g. "EXIF" or "Nikon Makernote" or
- "IPTC") as the name of a child node, with the EXIF tags values stored as
either properties or child nodes.
- </para>
- <para>
- To use this sequencer, simply include the
<code>dna-sequencer-images</code> JAR
- in your application and configure the Sequencing Service to use this sequencer using
something similar to:
- </para>
- <programlisting>
-String name = "Image Sequencer";
-String desc = "Sequences image files to extract the characteristics of the
image";
-String classname = "org.jboss.dna.sequencer.images.ImageMetadataSequencer";
-String[] classpath = null; // Use the current classpath
-String[] pathExpressions =
{"//(*.(jpg|jpeg|gif|bmp|pcx|png|iff|ras|pbm|pgm|ppm|psd)[*])/jcr:content[@jcr:data]
=> /images/$1"};
-&SequencerConfig; sequencerConfig = new &SequencerConfig;(name, desc, classname,
- classpath, pathExpressions);
-sequencingService.addSequencer(sequencerConfig);
-</programlisting>
- </sect2>
- <sect2 id="dna-sequencer-msoffice">
- <title>Microsoft Office document sequencer</title>
- <para>
- This sequencer is included in JBoss DNA and processes Microsoft Office documents,
including Word documents, Excel spreadsheets,
- and PowerPoint presentations. With documents, the sequencer extracts the internal
structure based on Heading styles.
- With presentations, the sequencer extracts the slides, titles, text and slide
thumbnails.
- With spreadsheets, the sequencer extracts the names of the sheets. And, the
sequencer extracts for all the files the
- general file information, including the name of the author, title, keywords, subject,
comments, and various dates.
- </para>
- <para>
- To use this sequencer, simply include the
<code>dna-sequencer-msoffice</code> JAR and all of the
- <ulink
url="http://poi.apache.org/">POI</ulink> JARs
- in your application and configure the Sequencing Service to use this sequencer using
something similar to:
- </para>
- <programlisting>
-String name = "Microsoft Office Document Sequencer";
-String desc = "Sequences MS Office documents, including spreadsheets and
presentations";
-String classname =
"org.jboss.dna.sequencer.msoffice.MSOfficeMetadataSequencer";
-String[] classpath = null; // Use the current classpath
-String[] pathExpressions = {"//(*.(doc|docx|ppt|pps|xls)[*])/jcr:content[@jcr:data]
=> /msoffice/$1"};
-&SequencerConfig; sequencerConfig = new &SequencerConfig;(name, desc, classname,
- classpath, pathExpressions);
-sequencingService.addSequencer(sequencerConfig);
-</programlisting>
- </sect2>
- <sect2 id="dna-sequencer-zip">
- <title>ZIP archive sequencer</title>
- <para>
- The ZIP file sequencer is included in JBoss DNA and extracts the files and folders
contained in the ZIP archive file,
- extracting the files and folders into the repository using JCR's
<code>nt:file</code> and <code>nt:folder</code> node types.
- </para>
- <para>
- To use this sequencer, simply include the <code>dna-sequencer-zip</code>
JAR
- in your application and configure the Sequencing Service to use this sequencer using
something similar to:
- </para>
- <programlisting>
-String name = "ZIP Sequencer";
-String desc = "Sequences ZIP archives to extract the files and folders";
-String classname = "org.jboss.dna.sequencer.zip.ZipSequencer";
-String[] pathExpressions = {"//(*.zip[*])/jcr:content[@jcr:data] =>
/zips/$1"};
-&SequencerConfig; sequencerConfig = new &SequencerConfig;(name, desc, classname,
- classpath, pathExpressions);
-this.sequencingService.addSequencer(sequencerConfig);
-</programlisting>
- </sect2>
- <sect2 id="dna-sequencer-java">
- <title>Java source sequencer</title>
- <para>
- One of the sequencers that included in JBoss DNA is the <emphasis
role="strong">dna-sequencer-java</emphasis> subproject.
- This sequencer parses Java source code added to the repository and extracts the basic
structure of the classes and enumerations
- defined in the code.
- This structure includes: the package structures, class declarations, class and member
attribute declarations,
- class and member method declarations with signature (but not implementation logic),
enumerations with each enumeration literal value,
- annotations, and JavaDoc information for all of the above.
- After extracting this information from the source code, the sequencer then writes
this structure into the repository,
- where it can be further processed, analyzed, searched, navigated, or referenced.
- </para>
- <para>
- To use this sequencer, simply include the <code>dna-sequencer-java</code>
JAR (plus all of the JARs that it is dependent upon)
- in your application and configure the Sequencing Service to use this sequencer using
something similar to:
- </para>
- <programlisting>
-String name = "Java Sequencer";
-String desc = "Sequences java files to extract the characteristics of the Java
source";
-String classname = "org.jboss.dna.sequencer.java.JavaMetadataSequencer";
-String[] classpath = null; // Use the current classpath
-String[] pathExpressions = {"//(*.java[*])/jcr:content[@jcr:data] =>
/java/$1"};
-&SequencerConfig; sequencerConfig = new &SequencerConfig;(name, desc, classname,
- classpath, pathExpressions);
-this.sequencingService.addSequencer(sequencerConfig);
-</programlisting>
- </sect2>
- <sect2 id="dna-sequencer-mp3">
- <title>MP3 audio file sequencer</title>
- <para>
- Another sequencer that is included in JBoss DNA is the <emphasis
role="strong">dna-sequencer-mp3</emphasis> sequencer project.
- This sequencer processes MP3 audio files added to a repository and extracts the
<ulink
url="http://www.id3.org/">ID3</ulink>
- metadata for the file, including the track's title, author, album name, year,
and comment.
- After extracting this information from the audio files, the sequencer then writes
this structure into the repository,
- where it can be further processed, analyzed, searched, navigated, or referenced.
- </para>
- <para>
- To use this sequencer, simply include the <code>dna-sequencer-mp3</code>
JAR and the <ulink
url="http://www.jthink.net/jaudiotagger/">JAudioTagger</u...
- library in your application and configure the Sequencing Service to use this
sequencer using something similar to:
- </para>
- <programlisting>
-String name = "MP3 Sequencer";
-String desc = "Sequences MP3 files to extract the ID3 tags of the audio file";
-String classname = "org.jboss.dna.sequencer.mp3.Mp3MetadataSequencer";
-String[] pathExpressions = {"//(*.mp3[*])/jcr:content[@jcr:data] =>
/mp3s/$1"};
-&SequencerConfig; sequencerConfig = new &SequencerConfig;(name, desc, classname,
- classpath, pathExpressions);
-this.sequencingService.addSequencer(sequencerConfig);
-</programlisting>
- </sect2>
- <sect2 id="dna-sequencer-cnd">
- <title>JCR Compact Node Definition (CND) file sequencer</title>
- <para>
- This sequencer processes JCR Compact Node Definition (CND) files
- to extract the node definitions with their property definitions, and inserts these
into the repository using JCR standard notation.
- </para>
- </sect2>
- <sect2 id="dna-sequencer-jbpm-jpdl">
- <title>jBPM JPDL file sequencer</title>
- <para>
- This sequencer is incomplete and is not currently usable. The purpose is to
sequence jBPM JPDL files to identify metadata
- about processes, their actors, and their dependencies.
- </para>
- </sect2>
- <sect2 id="dna-sequencer-xml">
- <title>XML file sequencer</title>
- <para>
- This sequencer stores the structure and data of an XML file into the repository.
- </para>
- </sect2>
-
- </sect1>
- <sect1 id="custom-sequencers">
- <title>Creating custom sequencers</title>
- <para>The current release of JBoss DNA comes with six sequencers. However,
it's very easy to create your own
- sequencers and to then configure JBoss DNA to use them in your own application.
- </para>
- <para>
- Creating a custom sequencer involves the following steps:
- <orderedlist>
- <listitem>
- <para>Create a Maven 2 project for your sequencer;</para>
- </listitem>
- <listitem>
- <para>Implement the &StreamSequencer; interface with your own
implementation, and create unit tests to verify
- the functionality and expected behavior;</para>
- </listitem>
- <listitem>
- <para>Add the sequencer configuration to the JBoss DNA
&SequencingService; in your application
- as described in the <link linkend="using_dna">previous
chapter</link>; and</para>
- </listitem>
- <listitem>
- <para>Deploy the JAR file with your implementation (as well as any
dependencies), and make them available to JBoss DNA
- in your application.</para>
- </listitem>
- </orderedlist>
- It's that simple.
- </para>
- <sect2 id="custom_sequencer_project">
- <title>Creating the Maven 2 project</title>
- <para>The first step is to create the Maven 2 project that you can use to
compile your code and build the JARs.
- Maven 2 automates a lot of the work, and since you're already <link
linkend="maven">set up to use Maven</link>,
- using Maven for your project will save you a lot of time and effort. Of course, you
don't have to use Maven 2, but then you'll
- have to get the required libraries and manage the compiling and building process
yourself.</para>
- <note>
- <para>JBoss DNA may provide in the future a Maven archetype for creating
sequencer projects. If you'd find this useful
- and would like to help create it, please <link
linkend="preface">join the community</link>.
- </para>
- <para>In lieu of a Maven archetype, you may find it easier to start with a
small existing sequencer project.
- The <emphasis role="strong">dna-sequencer-images</emphasis>
project is a small, self-contained sequencer implementation that
- has only the minimal dependencies.
- See the subversion repository: <ulink
url="&Subversion;trunk/extensions/dna-sequencer-images/">&Subversion;trunk/extensions/dna-sequencer-images/</ulink>
- </para>
- </note>
- <para>You can create your Maven project any way you'd like. For examples,
see the <ulink
url="http://maven.apache.org/guides/getting-started/index.html#How_d...
2 documentation</ulink>.
- Once you've done that, just add the dependencies in your project's
<code>pom.xml</code> dependencies section:</para>
- <programlisting role="XML"><![CDATA[
-<dependency>
- <groupId>org.jboss.dna</groupId>
- <artifactId>dna-common</artifactId>
- <version>0.3</version>
-</dependency>
-<dependency>
- <groupId>org.jboss.dna</groupId>
- <artifactId>dna-graph</artifactId>
- <version>0.3</version>
-</dependency>
- ]]></programlisting>
- <para>These are minimum dependencies required for compiling a sequencer. Of
course, you'll have to add
- other dependencies that your sequencer needs.</para>
- <para>As for testing, you probably will want to add more dependencies, such as
those listed here:</para>
- <programlisting role="XML"><![CDATA[
-<dependency>
- <groupId>org.jboss.dna</groupId>
- <artifactId>dna-graph</artifactId>
- <version>0.5</version>
- <type>test-jar</type>
- <scope>test</scope>
-</dependency>
-<dependency>
- <groupId>junit</groupId>
- <artifactId>junit</artifactId>
- <version>4.4</version>
- <scope>test</scope>
-</dependency>
-<dependency>
- <groupId>org.hamcrest</groupId>
- <artifactId>hamcrest-library</artifactId>
- <version>1.1</version>
- <scope>test</scope>
-</dependency>
-<!-- Logging with Log4J -->
-<dependency>
- <groupId>org.slf4j</groupId>
- <artifactId>slf4j-log4j12</artifactId>
- <version>1.4.3</version>
- <scope>test</scope>
-</dependency>
-<dependency>
- <groupId>log4j</groupId>
- <artifactId>log4j</artifactId>
- <version>1.2.14</version>
- <scope>test</scope>
-</dependency>
- ]]></programlisting>
- <para>Testing JBoss DNA sequencers does not require a JCR repository or the
JBoss DNA services. (For more detail,
- see the <link linkend="testing_custom_sequencers">testing
section</link>.) However, if you want to do
- integration testing with a JCR repository and the JBoss DNA services, you'll
need additional dependencies for these libraries.</para>
- <programlisting role="XML"><![CDATA[
-<!-- JBoss DNA JCR Repository -->
-<dependency>
- <groupId>org.jboss.dna</groupId>
- <artifactId>dna-jcr</artifactId>
- <version>0.5</version>
- <scope>test</scope>
-</dependency>
-<!-- Java Content Repository API -->
-<dependency>
- <groupId>javax.jcr</groupId>
- <artifactId>jcr</artifactId>
- <version>1.0.1</version>
- <scope>test</scope>
-</dependency>
- ]]></programlisting>
- <para>At this point, your project should be set up correctly, and you're
ready to move on to
- <link linkend="custom_sequencer_implementation">writing the Java
implementation</link> for your sequencer.</para>
- </sect2>
- <sect2 id="custom_sequencer_implementation">
- <title>Implementing the StreamSequencer interface</title>
- <para>After creating the project and setting up the dependencies, the next
step is to create a Java class that implements
- the &StreamSequencer; interface. This interface is very straightforward and
involves a single method:</para>
- <programlisting>
-public interface &StreamSequencer; {
-
- /**
- * Sequence the data found in the supplied stream, placing the output
- * information into the supplied map.
- *
- * @param stream the stream with the data to be sequenced; never null
- * @param output the output from the sequencing operation; never null
- * @param context the context for the sequencing operation; never null
- */
- void sequence( &InputStream; stream, &SequencerOutput; output,
&SequencerContext; context );
-}
-</programlisting>
- <para>The job of a stream sequencer is to process the data in the supplied
stream, and place into the &SequencerOutput;
- any information that is to go into the JCR repository. JBoss DNA figures out when
your sequencer should be called
- (of course, using the sequencing configuration you'll add in a bit), and then
makes sure the generated information
- is saved in the correct place in the repository.
- </para>
- <para>The &SequencerContext; provides information about
- the current sequencing operation, including the location and properties of the node
being sequenced, the MIME type
- of the node being sequenced, and a location to record problems that aren't
severe enough to warrant throwing an exception.
- </para>
- <para>The &SequencerOutput; class is fairly easy to use. There are
basically two methods you need to call.
- One method sets the property values, while the other sets references to other nodes
in the repository. Use these
- methods to describe the properties of the nodes you want to create, using relative
paths for the nodes and
- valid JCR property names for properties and references. JBoss DNA will ensure that
nodes are created or updated
- whenever they're needed.</para>
- <programlisting>
-public interface &SequencerOutput; {
-
- /**
- * Set the supplied property on the supplied node. The allowable
- * values are any of the following:
- * - primitives (which will be autoboxed)
- * - String instances
- * - String arrays
- * - byte arrays
- * - InputStream instances
- * - Calendar instances
- *
- * @param nodePath the path to the node containing the property;
- * may not be null
- * @param property the name of the property to be set
- * @param values the value(s) for the property; may be empty if
- * any existing property is to be removed
- */
- void setProperty( String nodePath, String property, Object... values );
-
- /**
- * Set the supplied reference on the supplied node.
- *
- * @param nodePath the path to the node containing the property;
- * may not be null
- * @param property the name of the property to be set
- * @param paths the paths to the referenced property, which may be
- * absolute paths or relative to the sequencer output node;
- * may be empty if any existing property is to be removed
- */
- void setReference( String nodePath, String property, String... paths );
-}
-</programlisting>
- <para>JBoss DNA will create nodes of type
<code>nt:unstructured</code> unless you specify the value for the
- <code>jcr:primaryType</code> property. You can also specify the
values for the <code>jcr:mixinTypes</code> property
- if you want to add mixins to any node.</para>
- <para>For a complete example of a sequencer, let's look at the
&ImageMetadataSequencer;
- implementation:</para>
- <programlisting>
-public class &ImageMetadataSequencer; implements &StreamSequencer; {
-
- public static final String METADATA_NODE = "image:metadata";
- public static final String IMAGE_PRIMARY_TYPE = "jcr:primaryType";
- public static final String IMAGE_MIXINS = "jcr:mixinTypes";
- public static final String IMAGE_MIME_TYPE = "jcr:mimeType";
- public static final String IMAGE_ENCODING = "jcr:encoding";
- public static final String IMAGE_FORMAT_NAME = "image:formatName";
- public static final String IMAGE_WIDTH = "image:width";
- public static final String IMAGE_HEIGHT = "image:height";
- public static final String IMAGE_BITS_PER_PIXEL = "image:bitsPerPixel";
- public static final String IMAGE_PROGRESSIVE = "image:progressive";
- public static final String IMAGE_NUMBER_OF_IMAGES =
"image:numberOfImages";
- public static final String IMAGE_PHYSICAL_WIDTH_DPI =
"image:physicalWidthDpi";
- public static final String IMAGE_PHYSICAL_HEIGHT_DPI =
"image:physicalHeightDpi";
- public static final String IMAGE_PHYSICAL_WIDTH_INCHES =
"image:physicalWidthInches";
- public static final String IMAGE_PHYSICAL_HEIGHT_INCHES =
"image:physicalHeightInches";
-
- /**
- * {@inheritDoc}
- */
- public void sequence( &InputStream; stream, &SequencerOutput; output,
- &SequencerContext; context ) {
- &ImageMetadata; metadata = new &ImageMetadata;();
- metadata.setInput(stream);
- metadata.setDetermineImageNumber(true);
- metadata.setCollectComments(true);
-
- // Process the image stream and extract the metadata ...
- if (!metadata.check()) {
- metadata = null;
- }
- // Generate the output graph if we found useful metadata ...
- if (metadata != null) {
- // Place the image metadata into the output map ...
- output.setProperty(METADATA_NODE, IMAGE_PRIMARY_TYPE,
"image:metadata");
- // output.psetProperty(METADATA_NODE, IMAGE_MIXINS, "");
- output.setProperty(METADATA_NODE, IMAGE_MIME_TYPE, metadata.getMimeType());
- // output.setProperty(METADATA_NODE, IMAGE_ENCODING, "");
- output.setProperty(METADATA_NODE, IMAGE_FORMAT_NAME,
metadata.getFormatName());
- output.setProperty(METADATA_NODE, IMAGE_WIDTH, metadata.getWidth());
- output.setProperty(METADATA_NODE, IMAGE_HEIGHT, metadata.getHeight());
- output.setProperty(METADATA_NODE, IMAGE_BITS_PER_PIXEL,
metadata.getBitsPerPixel());
- output.setProperty(METADATA_NODE, IMAGE_PROGRESSIVE,
metadata.isProgressive());
- output.setProperty(METADATA_NODE, IMAGE_NUMBER_OF_IMAGES,
metadata.getNumberOfImages());
- output.setProperty(METADATA_NODE, IMAGE_PHYSICAL_WIDTH_DPI,
metadata.getPhysicalWidthDpi());
- output.setProperty(METADATA_NODE, IMAGE_PHYSICAL_HEIGHT_DPI,
metadata.getPhysicalHeightDpi());
- output.setProperty(METADATA_NODE, IMAGE_PHYSICAL_WIDTH_INCHES,
metadata.getPhysicalWidthInch());
- output.setProperty(METADATA_NODE, IMAGE_PHYSICAL_HEIGHT_INCHES,
metadata.getPhysicalHeightInch());
- }
- }
-}
-</programlisting>
- <para>
- Notice how the image metadata is extracted and the output graph is generated. A
single node is created with the name <code>image:metadata</code>
- and with the <code>image:metadata</code> node type. No mixins are
defined for the node, but several properties are set on the node
- using the values obtained from the image metadata. After this method returns, the
constructed graph will be saved to the repository
- in all of the places defined by its configuration. (This is why only relative paths
are used in the sequencer.)
- </para>
- </sect2>
- <sect2 id="testing_custom_sequencers">
- <title>Testing custom sequencers</title>
- <para>The sequencing framework was designed to make testing sequencers much
easier. In particular, the
- &StreamSequencer; interface does not make use of the JCR API. So instead of
requiring a fully-configured
- JCR repository and JBoss DNA system, unit tests for a sequencer can focus on testing
that the content is
- processed correctly and the desired output graph is generated.</para>
- <note>
- <para>For a complete example of a sequencer unit test, see the
<code>ImageMetadataSequencerTest</code> unit test
- in the <code>org.jboss.dna.sequencer.images</code> package of the
<code>dna-sequencers-image</code> project.
- </para>
- </note>
- <para>The following code fragment shows one way of testing a sequencer, using
JUnit 4.4 assertions and
- some of the classes made available by JBoss DNA. Of course,
- this example code does not do any error handling and does not make all the
assertions a real test would.</para>
- <programlisting>
-&StreamSequencer; sequencer = new &ImageMetadataSequencer;();
-&MockSequencerOutput; output = new &MockSequencerOutput;();
-&MockSequencerContext; context = new &MockSequencerContext;();
-&InputStream; stream = null;
-try {
- stream =
this.getClass().getClassLoader().getResource("caution.gif").openStream();
- sequencer.sequence(stream,output,context); // writes to 'output'
- assertThat(output.getPropertyValues("image:metadata",
"jcr:primaryType"),
- is(new Object[] {"image:metadata"}));
- assertThat(output.getPropertyValues("image:metadata",
"jcr:mimeType"),
- is(new Object[] {"image/gif"}));
- // ... make more assertions here
- assertThat(output.hasReferences(), is(false));
-} finally {
- stream.close();
-}
-</programlisting>
- <para>It's also useful to test that a sequencer produces no output for
something it should not understand:</para>
- <programlisting>
-&Sequencer; sequencer = new &ImageMetadataSequencer;();
-&MockSequencerOutput; output = new &MockSequencerOutput;();
-&MockSequencerContext; context = new &MockSequencerContext;();
-&InputStream; stream = null;
-try {
- stream =
this.getClass().getClassLoader().getResource("caution.pict").openStream();
- sequencer.sequence(stream,output,context); // writes to 'output'
- assertThat(output.hasProperties(), is(false));
- assertThat(output.hasReferences(), is(false));
-} finally {
- stream.close();
-}
-</programlisting>
- <para>These are just two simple tests that show ways of testing a sequencer.
Some tests may get quite involved,
- especially if a lot of output data is produced.
- </para>
- <para>It may also be useful to create some integration tests
- that <link linkend="using_dna">configure JBoss DNA</link> to
use a custom sequencer, and to then upload
- content using the JCR API, verifying that the custom sequencer did run. However,
remember that JBoss DNA
- runs sequencers asynchronously in the background, and you must synchronize your
tests to ensure that the
- sequencers have a chance to run before checking the results. (One way of doing this
(although, granted, not always reliable) is to wait for a second
- after uploading your content, shutdown the &SequencingService; and await its
termination,
- and then check that the sequencer output has been saved to the JCR repository. For
an example of this technique,
- see the <code>SequencingClientTest</code> unit test in the example
application.)
- </para>
- </sect2>
- <sect2 id="deploying_custom_sequencers">
- <title>Deploying custom sequencers</title>
- <para>The first step of deploying a sequencer consists of adding/changing the
sequencer configuration (e.g., &SequencerConfig;)
- in the &SequencingService;. This was covered in the <link
linkend="sequencing_service">previous chapter</link>.
- </para>
- <para>
- The second step is to make the sequencer implementation available to JBoss DNA. At
this time, the JAR containing
- your new sequencer, as well as any JARs that your sequencer depends on, should be
placed on your application classpath.</para>
- <note>
- <para>A future goal of JBoss DNA is to allow sequencers, connectors, and
other extensions to be easily deployed into
- a runtime repository. This process will not only be much simpler, but it will
also provide JBoss DNA
- with the information necessary to update configurations and create the
appropriate class loaders for each extension.
- Having separate class loaders for each extension helps prevent the pollution of
the common classpath,
- facilitates an isolated runtime environment to eliminate any dependency
conflicts, and may potentially
- enable hot redeployment of newer extension versions.
- </para>
- </note>
- </sect2>
- </sect1>
- <sect1>
- <title>Summary</title>
- <para>
- In this chapter, we described how JBoss DNA sequences files as they're uploaded
into a repository.
- And one of the things we mentioned was that each sequencer is handed (with other
inputs) the MIME type of the file it is to process.
- How does DNA know what the MIME type is?
- JBoss DNA uses <emphasis>MIME type detectors</emphasis>, and this is the
topic of the <link linkend="mimetypes">next chapter</link>.
- </para>
- </sect1>
-</chapter>
Deleted: trunk/docs/reference/src/main/docbook/en-US/content/testing.xml
===================================================================
--- trunk/docs/reference/src/main/docbook/en-US/content/testing.xml 2009-06-08 00:52:09
UTC (rev 994)
+++ trunk/docs/reference/src/main/docbook/en-US/content/testing.xml 2009-06-08 14:57:17
UTC (rev 995)
@@ -1,152 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- ~ JBoss DNA (
http://www.jboss.org/dna)
- ~
- ~ See the COPYRIGHT.txt file distributed with this work for information
- ~ regarding copyright ownership. Some portions may be licensed
- ~ to Red Hat, Inc. under one or more contributor license agreements.
- ~ See the AUTHORS.txt file in the distribution for a full listing of
- ~ individual contributors.
- ~
- ~ JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
- ~ is licensed to you under the terms of the GNU Lesser General Public License as
- ~ published by the Free Software Foundation; either version 2.1 of
- ~ the License, or (at your option) any later version.
- ~
- ~ JBoss DNA is distributed in the hope that it will be useful,
- ~ but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- ~ or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
- ~ for more details.
- ~
- ~ You should have received a copy of the GNU Lesser General Public License
- ~ along with this distribution; if not, write to:
- ~ Free Software Foundation, Inc.
- ~ 51 Franklin Street, Fifth Floor
- ~ Boston, MA 02110-1301 USA
- -->
-<!DOCTYPE preface PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
"http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd" [
-<!ENTITY % CustomDTD SYSTEM "../custom.dtd">
-%CustomDTD;
-]>
-<chapter id="testing">
- <title>Testing</title>
- <para>
- The JBoss DNA project uses automated testing to verify that the software is doing what
it's supposed to
- and not doing what it shouldn't do. These automated tests are run continuously and
also act as regression tests,
- ensuring that we known if any problems we find and fix reappear later. All of our
tests are executed as part of
- our <link linkend="maven">Maven</link> build process, and the
entire build process (including the tests)
- is automatically run using <link linkend="hudson">Hudson</link>
continuous integration system.
- </para>
- <sect1 id="unit-tests">
- <title>Unit tests</title>
- <para>
- <emphasis role="strong">Unit tests</emphasis> verify the
behavior of a single class (or small set of classes) in isolation
- from other classes.
- We use the JUnit 4.4 testing framework, which has significant improvements over
earlier versions and makes
- it very easy to quickly write unit tests with little extra code. We also frequently
use the Mockito library
- to help create mock implementations of other classes that are not under test but are
used in the tests.
- </para>
- <para>
- Unit tests should generally run quickly and should not require large assemblies of
components. Additionally,
- they may rely upon the file resources included in the project, but these tests should
require no external resources
- (like databases or servers). Note that our unit tests are run during the
"test" phase of the standard
- <ulink
url="http://maven.apache.org/guides/introduction/introduction-to-the...
lifecycle</ulink>.
- This means that they are executed against the raw .class files created during
complication.
- </para>
- <para>
- Developers are expected to run all of the JBoss DNA unit tests in their local
environment before committing changes to SVN.
- So, if you're a developer and you've made changes to your local copy of the
source, you can run those tests that are
- related to your changes using your IDE or with Maven (or any other mechanism). But
before you commit your changes,
- you are expected to run a full Maven build using <code>mvn clean
install</code> (in the "trunk/" directory).
- Please do <emphasis>not</emphasis> rely upon continuous integration to run
all of the tests for you - the CI
- system is there to catch the occasional mistakes and to also run the <link
linkend="integration-tests">integration tests</link>.
- </para>
- </sect1>
- <sect1 id="integration-tests">
- <title>Integration tests</title>
- <para>
- While <link linkend="unit-tests">unit tests</link> test
individual classes in (relative) isolation, the purpose of
- <emphasis role="strong">integration tests</emphasis> are to
verify that assemblies of classes and components are
- behaving correctly. These assemblies are often the same ones that end users will
actually use. In fact,
- integration tests are executed during the "integration-test" phase of the
standard
- <ulink
url="http://maven.apache.org/guides/introduction/introduction-to-the...
lifecycle</ulink>,
- meaning they are executed against the packaged JARs and artifacts of the project.
- </para>
- <para>
- Integration tests also use the JUnit 4.4 framework, so they are again easy to write
and follow the same pattern
- as unit tests. However, because they're working with larger assemblies of
components, they often will take longer
- to set up, longer to run, and longer to tear down. They also may require initializing
"external resources", like
- databases or servers.
- </para>
- <para>
- Note, that while external resources may be required, care should be taken to minimize
these dependencies and to
- ensure that most (if not all) integration tests may be run by anyone who downloads the
source code. This means
- that these external resources should be available and set up within the tests. For
example, use in-memory databases
- where possible. Or, if a database is required, use an open-source database (e.g.,
MySQL or PostgreSQL). And when
- these external resources are not available, it should be obvious from the test class
names and/or test method names
- that it involved an external resource (e.g.,
"<code>MySqlConnectorIntegrationTest.shouldFindNodeStoredInDatabase()</code>").
- </para>
- </sect1>
- <sect1 id="writing-tests">
- <title>Writing tests</title>
- <para>
- As mentioned in <link linkend="methodology">the
introduction</link>, the JBoss DNA project doesn't follow any one methodology
- or process. Instead, we simply have a goal that as much code as possible is tested to
ensure it behaves as expected.
- Do we expect 100% of the code is covered by automated tests? No, but we do want to
test as much as we can.
- Maybe a simple JavaBean class doesn't need many tests, but any class with
non-trivial logic should be tested.
- </para>
- <para>
- We do encourage writing tests either before or while you write the code. Again,
we're not blindly following a methodology.
- Instead, there's a very practical reason: writing the tests early on helps you
write classes that are testable.
- If you wait until after the class (or classes) are done, you'll probably find that
it's not easy to test all
- of the logic (especially the complicated logic).
- </para>
- <para>
- Another suggestion is to write tests so that they specify and verify the behavior that
is expected from a class or component.
- One challenge developers often have is knowing what they should even test and what the
tests should look like.
- This is where <emphasis role="strong"><ulink
url="http://behaviour-driven.org/">Behavior-driven development
(BDD)</ulink></emphasis>
- helps out. If you think about what a class' behaviors are supposed to be (e.g.,
requirements), simply capture those
- requirements as test methods (with no implementations). For example, a test class for
sequencer
- implementation might have a test
method <code>shouldNotThrowAnErrorWhenTheSuppliedStreamIsNull() { }</code>.
Then, after you enumerate
- all the requirements you can think of, go back and start implementing the test
methods.
- </para>
- <para>
- If you look at the existing test cases, you'll find that the names of the unit and
integration tests in JBoss DNA
- follow a naming style, where the test method names are readable sentences. Actually,
we try to name the test methods
- <emphasis>and</emphasis> the test classes such that they form a
concisely-worded requirement. For example,
- </para>
- <programlisting>InMemorySequencerTest.shouldNotThrowAnErrorWhenTheSuppliedStreamIsNull()</programlisting>
- <para>
- is easily translated into a readable requirement:
- </para>
- <programlisting>InMemorySequencer should not throw an error when the supplied
stream is null.</programlisting>
- <para>
- In fact, at some point in the future, we'd like to process the source to
automatically generate a list of the behavior specifications
- that are asserted by the tests.
- </para>
- <para>
- But for now, we write tests - a lot of them. And by following a few simple
conventions and practices, we're able
- to do it quickly and in a way that makes it easy to understand what the code is
supposed to do (or not do).
- </para>
- </sect1>
- <sect1 id="tck-tests">
- <title>Technology Compatibility Kit (TCK) tests</title>
- <para>
- Many Java specifications provide TCK test suites that can be used to check or verify
that an implementation
- correctly implements the API or SPI defined by the specification. These TCK tests
vary by technology, but
- <ulink url="&JSR170;">JSR-170</ulink> does provide TCK tests
that ensure that a JCR repository implementation exhibits the correct and expected
- behavior.
- </para>
- <para>
- JBoss DNA has not yet passed enough of the TCK tests to publish the results. We
still have to implement
- queries, which is a required feature of Level 1 repositories. However, suffice to
say that JBoss DNA has passed
- many of the individual tests that make up the Level 1 and Level 2 tests, and it is a
major objective of the next
- release to pass the remaining Level 1 and Level 2 tests (along with some other
optional features).
- </para>
- <para>
- JBoss DNA also frequently runs the JCR unit tests from the Apache Jackrabbit project.
(Those these tests are not
- the official TCK, they apparently are used within the official TCK.) These unit tests
are set up in the
- <code>dna-jcr-tck</code> project.
- </para>
- </sect1>
-</chapter>
Modified: trunk/docs/reference/src/main/docbook/en-US/master.xml
===================================================================
--- trunk/docs/reference/src/main/docbook/en-US/master.xml 2009-06-08 00:52:09 UTC (rev
994)
+++ trunk/docs/reference/src/main/docbook/en-US/master.xml 2009-06-08 14:57:17 UTC (rev
995)
@@ -52,14 +52,32 @@
</bookinfo>
<xi:include
xmlns:xi="http://www.w3.org/2001/XInclude"
href="content/preface.xml"/>
<xi:include
xmlns:xi="http://www.w3.org/2001/XInclude"
href="content/introduction.xml"/>
- <xi:include
xmlns:xi="http://www.w3.org/2001/XInclude"
href="content/development_tools.xml"/>
- <xi:include
xmlns:xi="http://www.w3.org/2001/XInclude"
href="content/classloaders.xml"/>
- <xi:include
xmlns:xi="http://www.w3.org/2001/XInclude"
href="content/environment.xml"/>
- <xi:include
xmlns:xi="http://www.w3.org/2001/XInclude"
href="content/repositories.xml"/>
- <xi:include
xmlns:xi="http://www.w3.org/2001/XInclude"
href="content/jcr.xml"/>
- <xi:include
xmlns:xi="http://www.w3.org/2001/XInclude"
href="content/sequencing.xml"/>
- <xi:include
xmlns:xi="http://www.w3.org/2001/XInclude"
href="content/mimetypes.xml"/>
- <xi:include
xmlns:xi="http://www.w3.org/2001/XInclude"
href="content/configuration.xml"/>
- <xi:include
xmlns:xi="http://www.w3.org/2001/XInclude"
href="content/testing.xml"/>
+ <part>
+ <title>Developers and Contributors</title>
+ <xi:include
xmlns:xi="http://www.w3.org/2001/XInclude"
href="content/developers/tools.xml"/>
+ <xi:include
xmlns:xi="http://www.w3.org/2001/XInclude"
href="content/developers/testing.xml"/>
+ </part>
+ <part>
+ <title>JBoss DNA Core</title>
+ <xi:include
xmlns:xi="http://www.w3.org/2001/XInclude"
href="content/core/classloaders.xml"/>
+ <xi:include
xmlns:xi="http://www.w3.org/2001/XInclude"
href="content/core/environment.xml"/>
+ <xi:include
xmlns:xi="http://www.w3.org/2001/XInclude"
href="content/core/repositories.xml"/>
+ <xi:include
xmlns:xi="http://www.w3.org/2001/XInclude"
href="content/core/sequencing.xml"/>
+ <xi:include
xmlns:xi="http://www.w3.org/2001/XInclude"
href="content/core/mimetypes.xml"/>
+ <xi:include
xmlns:xi="http://www.w3.org/2001/XInclude"
href="content/core/configuration.xml"/>
+ </part>
+ <part>
+ <title>JBoss DNA JCR</title>
+ <xi:include
xmlns:xi="http://www.w3.org/2001/XInclude"
href="content/jcr/jcr.xml"/>
+ <xi:include
xmlns:xi="http://www.w3.org/2001/XInclude"
href="content/jcr/rest_service.xml"/>
+ </part>
+ <part>
+ <title>Provided Connectors</title>
+ <xi:include
xmlns:xi="http://www.w3.org/2001/XInclude"
href="content/connectors/in_memory.xml"/>
+ </part>
+ <part>
+ <title>Provided Sequencers</title>
+ <xi:include
xmlns:xi="http://www.w3.org/2001/XInclude"
href="content/sequencers/zip.xml"/>
+ </part>
<xi:include
xmlns:xi="http://www.w3.org/2001/XInclude"
href="content/future.xml"/>
</book>