DNA SVN: r1006 - trunk/docs/reference/src/main/docbook/en-US/content/core.
by dna-commits@lists.jboss.org
Author: rhauch
Date: 2009-06-09 02:11:57 -0400 (Tue, 09 Jun 2009)
New Revision: 1006
Modified:
trunk/docs/reference/src/main/docbook/en-US/content/core/connector.xml
Log:
Clarified how the connector should be broadcasting events
Modified: trunk/docs/reference/src/main/docbook/en-US/content/core/connector.xml
===================================================================
--- trunk/docs/reference/src/main/docbook/en-US/content/core/connector.xml 2009-06-09 06:11:41 UTC (rev 1005)
+++ trunk/docs/reference/src/main/docbook/en-US/content/core/connector.xml 2009-06-09 06:11:57 UTC (rev 1006)
@@ -579,6 +579,12 @@
</para>
</note>
<para>
+ Also, make sure your &RequestProcessor; is properly <link linkend="connector_events">broadcasting the changes</link> made during execution.
+ The &RequestProcessor; class has a <code>recordChange(&ChangeRequest;)</code> that can be called from each of the <code>process(...)</code>
+ methods that take a &ChangeRequest;. The &RequestProcessor; enqueues these requests, and when the &RequestProcessor; is
+ closed, the default implementation is to send a &Change; to the &Observer; supplied into the constructor.
+ </para>
+ <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:
@@ -586,11 +592,13 @@
<programlisting>
public void execute( final &ExecutionContext; context,
final &Request; request ) throws RepositorySourceException {
- RequestProcessor processor = new RequestProcessor(context);
+ String sourceName = // from the &RepositorySource;
+ &Observer; observer = // from the &RepositoryContext;
+ &RequestProcessor; processor = new CustomRequestProcessor(sourceName,context,observer);
try {
processor.process(request);
} finally {
- processor.close();
+ processor.close(); // sends the accumulated &ChangeRequest;s as a &Changes; to the &Observer;
}
}</programlisting>
<para>
16 years, 6 months
DNA SVN: r1005 - in trunk/docs/reference/src/main/docbook/en-US: content/core and 1 other directory.
by dna-commits@lists.jboss.org
Author: rhauch
Date: 2009-06-09 02:11:41 -0400 (Tue, 09 Jun 2009)
New Revision: 1005
Modified:
trunk/docs/reference/src/main/docbook/en-US/content/core/connector.xml
trunk/docs/reference/src/main/docbook/en-US/content/core/graph.xml
trunk/docs/reference/src/main/docbook/en-US/custom.dtd
Log:
Added to the graph chapter new sections on requests, request processors, and observation.
Modified: trunk/docs/reference/src/main/docbook/en-US/content/core/connector.xml
===================================================================
--- trunk/docs/reference/src/main/docbook/en-US/content/core/connector.xml 2009-06-09 04:32:58 UTC (rev 1004)
+++ trunk/docs/reference/src/main/docbook/en-US/content/core/connector.xml 2009-06-09 06:11:41 UTC (rev 1005)
@@ -51,20 +51,20 @@
in one spot, using one powerful standard API.
</para>
<sect1 id="connectors">
- <title>Repository connectors</title>
+ <title>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:
+ <emphasis>connector</emphasis> system. Essentially, JBoss DNA's JCR implementation uses a single
+ connector to access all content:
<figure id="dnajcr-and-connector">
- <title>JBoss DNA's JCR implementation delegates to a repository connector</title>
+ <title>JBoss DNA's JCR implementation delegates to a 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),
+ That single 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>
@@ -128,9 +128,9 @@
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
+ takes an &ExecutionContext; instance and a &Request; object. The &ExecutionContext; object defines the
+ environment in which the processing is occurring,
+ while 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
@@ -161,123 +161,15 @@
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>.
+ So, we've so far learned what a connector is and how they're used to establish connections to the underlying sources
+ and access the content in those sources. Next we'll show how connectors expose the notion of workspaces, and describe
+ how to <link linkend="custom-connectors">create your own connectors</link>.
</para>
</sect1>
- <sect1 id="repository-workspaces">
- <title>Workspaces</title>
- <para>The <link linkend="graph-api-workspaces">previous chapter</link> 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>
+ <title>Out-of-the-box connectors</title>
<para>
- A number of repository connectors are already available in JBoss DNA, and are outlined in detail
+ A number of connectors are already available in JBoss DNA, and are outlined in detail
<link linkend="provied-connectors-part">later in the document</link>.
Note that we do want to build <ulink url="https://jira.jboss.org/jira/secure/IssueNavigator.jspa?reset=true&mod...">more connectors</ulink>
in the upcoming releases.
@@ -286,7 +178,7 @@
<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
+ There may come a time when you want to tackle creating your own 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
@@ -356,7 +248,7 @@
<dependency>
<groupId>org.jboss.dna</groupId>
<artifactId>dna-graph</artifactId>
- <version>0.4</version>
+ <version>0.5</version>
</dependency>
]]></programlisting>
<para>
@@ -371,17 +263,18 @@
<dependency>
<groupId>org.jboss.dna</groupId>
<artifactId>dna-graph</artifactId>
- <version>0.4</version>
+ <version>0.5</version>
<type>test-jar</type>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.jboss.dna</groupId>
<artifactId>dna-common</artifactId>
- <version>0.4</version>
+ <version>0.5</version>
<type>test-jar</type>
<scope>test</scope>
</dependency>
+<!-- Unit testing -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
@@ -441,6 +334,37 @@
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="repository-workspaces">
+ <title>Workspaces</title>
+ <para>The <link linkend="graph-api-workspaces">previous chapter</link> 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>
+ </sect3>
+ <sect3 id="connector_events">
+ <title>Broadcasting events</title>
+ <para>
+ When your &RepositorySource; instance is put into the library within a running JBoss DNA system,
+ the <code>initialize(&RepositoryContext;)</code> method will be called on the instance.
+ The supplied &RepositoryContext; object represents the context in which the &RepositorySource;
+ is running, and provides access to an &ExecutionContext;, a &RepositoryConnectionFactory; that can be used
+ to obtain connections to other sources, and an &Observer; of your source that should be called with
+ events describing the &Changes; being made within the source, either as a result of &ChangeRequest; operations being
+ performed on this source, or as a result of operations being performed on the content from outside
+ the source.
+ </para>
+ </sect3>
<sect3 id="connector_cache_policy">
<title>Cache policy</title>
<para>
@@ -538,7 +462,7 @@
<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.
+ class is the next step in writing a connector. This is what we'll cover in this section.
</para>
<para>
The &RepositoryConnection; interface is pretty straightforward:
@@ -580,13 +504,6 @@
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.
*
@@ -601,8 +518,7 @@
* @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;;
+ void execute( &ExecutionContext; context, &Request; request ) throws &RepositorySourceException;;
/**
* Close this connection to signal that it is no longer needed and that any accumulated
@@ -618,6 +534,7 @@
a connection pool.
</para>
<para>
+ If the connector is able to publish events, then
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>.
@@ -629,293 +546,12 @@
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).
+ 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
+ <link linkend="graph-requests">previous chapter</link> for details).
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;.
@@ -923,7 +559,8 @@
</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
+ are maintained. But as discussed in the <link linkend="graph-request-processors">previous chapter</link>, JBoss 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,
@@ -984,28 +621,16 @@
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>
<title>Summary</title>
<para>
- In this chapter, we covered all the aspects of JBoss DNA repositories, including the connector framework,
+ In this chapter, we covered all the aspects of JBoss DNA connectors, including the connector API,
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.
+ the <link linkend="sequencing-framework">next chapter</link> describes the sequencing framework and how
+ to build your own custom sequencers. After that, we'll get into <link linkend="configuration">how to configure
+ JBoss DNA</link> to use the connectors and sequencers.
</para>
</sect1>
</chapter>
Modified: trunk/docs/reference/src/main/docbook/en-US/content/core/graph.xml
===================================================================
--- trunk/docs/reference/src/main/docbook/en-US/content/core/graph.xml 2009-06-09 04:32:58 UTC (rev 1004)
+++ trunk/docs/reference/src/main/docbook/en-US/content/core/graph.xml 2009-06-09 06:11:41 UTC (rev 1005)
@@ -962,6 +962,441 @@
</para>
</sect2>
</sect1>
+ <sect1 id="graph-requests">
+ <title>Requests</title>
+ <para>
+ JBoss DNA &Graph; objects operate upon the underlying graph content, but we haven't really talked about how that works.
+ Recall that the &Graph; objects don't maintain any stateful representation of the content, but instead submit requests
+ to the underlying graph and return representations of the requested portions of the content.
+ This section focuses on what those requests look like, since they'll actually become very important when
+ working with <link linkend="connector-framework">connectors</link> in the next chapter.
+ </para>
+ <para>
+ A graph &Request; is an encapsulation of a command that is to be executed by the underlying graph owner (typically
+ a connector). Request objects can take many different forms, as there are different classes for each kind of request.
+ Each request contains the information needed to complete the processing, and it also is the place
+ where the results (or error) are recorded.
+ </para>
+ <para>
+ The &Graph; object creates the &Request; objects using &Location; objects to identify the node (or nodes) that are the
+ subject of the request. The &Graph; can either submit the request immediately, or it can batch multiple requests
+ together into "units of work". The submitted requests are then processed by the underlying system (e.g., connector)
+ and returned back to the &Graph; object, which then extracts and returns the results.
+ </para>
+ <para>
+ There are actually quite a few different types of &Request; classes:
+ </para>
+ <table frame='all'>
+ <title>Types of Read 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>
+ </tbody>
+ </tgroup>
+ </table>
+ <para>
+ &ChangeRequest; is a subclass of &Request; that provides a base class for all the requests that request a change
+ be made to the content. As we'll see later, these &ChangeRequest; objects also get reused by the
+ <link linkend="graph-observation">observation</link> system.
+ </para>
+ <table frame='all'>
+ <title>Types of Change 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>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 Read 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>
+ </tbody>
+ </tgroup>
+ </table>
+ <para>And there are also requests that deal with changing workspaces (and thus extend &ChangeRequest;):</para>
+ <table frame='all'>
+ <title>Types of Workspace Change 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>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>
+ This section covered the different kinds of &Request; classes. The next section provides a easy way to encapsulate how
+ a component should responds to these requests, and after that we'll see how these &Request; objects are also used
+ in the <link linkend="graph-observation">observation</link> framework.
+ </para>
+ </sect1>
+ <sect1 id="graph-request-processors">
+ <title>Request processors</title>
+ <para>
+ JBoss DNA connectors are typically the components that receive these &Request; objects. We'll dive deep into connectors
+ in the <link linkend="connector-framework">next chapter</link>, but before we do there is one more component related to
+ &Request;s that should be discussed.
+ </para>
+ <para>
+ The &RequestProcessor; class 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. This makes it easy to implement behavior that responds to the different kinds of &Request; classes:
+ simply subclass the &RequestProcessor;, override 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>
+ </sect1>
+ <sect1 id="graph-observation">
+ <title>Observation</title>
+ <para>
+ The JBoss DNA graph model also incorporates an observation framework that allows components to register and
+ be notified when changes occur within the content owned by a graph.
+ </para>
+ <para>
+ Many event frameworks define the listeners and sources as interfaces. While this is often useful, it requires
+ the implementations properly address the thread-safe semantics of managing and calling the listeners.
+ The JBoss DNA observation framework uses abstract or concrete classes to minimize the effort required for implementing
+ &ChangeObserver; or &Observable;. These abstract classes provide implementations for a number of
+ utility methods (such as the <code>unregister()</code> method on &ChangeObserver;) that
+ also save effort and code.
+ </para>
+ <para>
+ However, one of the more important reasons for providing classes is that &ChangeObserver; uses
+ weak references to track the &Observable; instances, and the &ChangeObservers;
+ class uses weak references for the listeners. This means that an observer does not prevent &Observable; instances
+ from being garbage collected, nor do observers prevent &Observable; instances from being garbage collected.
+ These abstract class provide all this functionality for free.
+ </para>
+ <sect2 id="graph-observable">
+ <title>Observable</title>
+ <para>
+ Any component that can have changes and be observed can implement the &Observable; interface. This interface
+ allows &Observers; to register (or be registered) to receive notifications of the changes. However, a concrete and thread-safe
+ implementation of this interface, called &ChangeObservers;, is available and should be used where possible, since it
+ automatically manages the registered &ChangeObserver; instances and properly implements the register and unregister mechanisms.
+ </para>
+ </sect2>
+ <sect2 id="graph-observers">
+ <title>Observers</title>
+ <para>
+ Components that are to recieve notifications of changes are called <emphasis>observers</emphasis>. To create an observer, simply extend
+ the &ChangeObserver; abstract class and provide an implementation of the <code>notify(&Changes;)</code> method.
+ Then, register the observer with an &Observable; using its <code>register(&ChangeObserver;)</code> method.
+ The observer's <code>notify(&Changes;)</code> method will then be called with the changes that have
+ been made to the &Observable;.
+ </para>
+ <para>
+ When an observer is no longer needed, it should be unregistered from all &Observable; instances with which
+ it was registered. The &ChangeObserver; class automatically tracks which &Observable; instances it is
+ registered with, and calling the observer's <code>unregister()</code> will unregister the observer from
+ all of these &Observable;s. Alternatively, an observer can be unregistered from a single &Observable; using the
+ &Observable;'s <code>unregister(&ChangeObserver;)</code> method.
+ </para>
+ </sect2>
+ <sect2 id="graph-changes">
+ <title>Changes</title>
+ <para>
+ The &Changes; class represents the set of individual changes that have been made during a single, atomic
+ operation. Each &Changes; instance has information about the source of the changes, the timestamp at which
+ the changes occurred, and the individual changes that were made. These individual changes take the form of
+ &ChangeRequest; objects, which we'll see more of in the next chapter. Each request is
+ frozen, meaning it is immutable and will not change. Also none of the change requests will be marked as cancelled.
+ </para>
+ <para>
+ Using the actual &ChangeRequest; objects as the "events" has a number of advantages.
+ First, the existing &ChangeRequest; subclasses already contain the information to accurately and completely
+ describe the operation. Reusing these classes means we don't need a duplicate class structure or come up with a generic
+ event class.
+ </para>
+ <para>
+ Second, the requests have all the state required for an event, plus they often will have more. For example,
+ the &DeleteBranchRequest; has the actual location of the branch that was deleted (and in this way is not much different than
+ a more generic event), but the &CreateNodeRequest; has the actual location of the created node along with the properties
+ of that node. Additionally, the &RemovePropertyRequest; has the actual location of the node along with the name of the property
+ that was removed. In many cases, these requests have all the information a more general event class might have but
+ then hopefully enough information for many observers to use directly without having to read the graph to decide what
+ actually changed.
+ </para>
+ <para>
+ Third, the requests that make up a &Changes; instance can actually be replayed. Consider the case of a cache
+ that is backed by a &RepositorySource;, which might use an observer to keep the cache in sync.
+ As the cache is notified of &Changes;, the cache can simply replay the changes against its source.
+ </para>
+ </sect2>
+ <para>
+ As we'll see in the <link linkend="connector-framework">next chapter</link>, each connector is responsible for propagating
+ the &ChangeRequest; objects to the connector's &Observer;. But that's not the only use of &Observers;.
+ We'll also see later how the <link linkend="sequencing-framework">sequencing system</link> uses &Observers; to monitor
+ for changes in the graph content to determine which, if any, sequencers should be run. And, the
+ <link linkend="jcr">JCR implementation</link> also uses the observation framework to propagate those changes
+ to JCR clients.
+ </para>
+ </sect1>
<sect1>
<title>Summary</title>
<para>
Modified: trunk/docs/reference/src/main/docbook/en-US/custom.dtd
===================================================================
--- trunk/docs/reference/src/main/docbook/en-US/custom.dtd 2009-06-09 04:32:58 UTC (rev 1004)
+++ trunk/docs/reference/src/main/docbook/en-US/custom.dtd 2009-06-09 06:11:41 UTC (rev 1005)
@@ -116,10 +116,11 @@
<!ENTITY PropertyType "<ulink url='&API;graph/property/PropertyType.html'><classname>PropertyType</classname></ulink>">
<!ENTITY PathNotFoundException "<ulink url='&API;graph/property/PathNotFoundException.html'><classname>PathNotFoundException</classname></ulink>">
<!ENTITY RepositorySource "<ulink url='&API;graph/connector/RepositorySource.html'><interface>RepositorySource</interface></ulink>">
+<!ENTITY RepositoryContext "<ulink url='&API;graph/connector/RepositoryContext.html'><interface>RepositoryContext</interface></ulink>">
<!ENTITY RepositoryConnection "<ulink url='&API;graph/connector/RepositoryConnection.html'><interface>RepositoryConnection</interface></ulink>">
<!ENTITY RepositoryConnectionFactory "<ulink url='&API;graph/connector/RepositoryConnectionFactory.html'><interface>RepositoryConnectionFactory</interface></ulink>">
-<!ENTITY RepositorySourceListener "<ulink url='&API;graph/connector/RepositorySourceListener.html'><interface>RepositorySourceListener</interface></ulink>">
<!ENTITY RepositorySourceCapabilities "<ulink url='&API;graph/connector/RepositorySourceCapabilities.html'><classname>RepositorySourceCapabilities</classname></ulink>">
+<!ENTITY RepositorySourceException "<ulink url='&API;graph/connector/RepositorySourceException.html'><classname>RepositorySourceException</classname></ulink>">
<!ENTITY InMemoryRepository "<ulink url='&API;graph/connector/inmemory/InMemoryRepository.html'><classname>InMemoryRepository</classname></ulink>">
<!ENTITY InMemoryRepositorySource "<ulink url='&API;graph/connector/inmemory/InMemoryRepositorySource.html'><classname>InMemoryRepositorySource</classname></ulink>">
<!ENTITY FederatedRepository "<ulink url='&API;graph/connector/federation/FederatedRepository.html'><classname>FederatedRepository</classname></ulink>">
@@ -127,9 +128,13 @@
<!ENTITY Projection "<ulink url='&API;graph/connector/federation/Projection.html'><classname>Projection</classname></ulink>">
<!ENTITY CachePolicy "<ulink url='&API;graph/cache/CachePolicy.html'><interface>CachePolicy</interface></ulink>">
<!ENTITY Request "<ulink url='&API;graph/request/Request.html'><classname>Request</classname></ulink>">
+<!ENTITY ChangeRequest "<ulink url='&API;graph/request/ChangeRequest.html'><classname>ChangeRequest</classname></ulink>">
<!ENTITY CompositeRequest "<ulink url='&API;graph/request/CompositeRequest.html'><classname>CompositeRequest</classname></ulink>">
<!ENTITY ReadNodeRequest "<ulink url='&API;graph/request/ReadNodeRequest.html'><classname>ReadNodeRequest</classname></ulink>">
+<!ENTITY RemovePropertyRequest "<ulink url='&API;graph/request/RemovePropertyRequest.html'><classname>RemovePropertyRequest</classname></ulink>">
+<!ENTITY CreateNodeRequest "<ulink url='&API;graph/request/CreateNodeRequest.html'><classname>CreateNodeRequest</classname></ulink>">
<!ENTITY CopyBranchRequest "<ulink url='&API;graph/request/CopyBranchRequest.html'><classname>CopyBranchRequest</classname></ulink>">
+<!ENTITY DeleteBranchRequest "<ulink url='&API;graph/request/DeleteBranchRequest.html'><classname>DeleteBranchRequest</classname></ulink>">
<!ENTITY InvalidRequestException "<ulink url='&API;graph/request/InvalidRequestException.html'><classname>InvalidRequestException</classname></ulink>">
<!ENTITY InvalidWorkspaceException "<ulink url='&API;graph/request/InvalidWorkspaceException.html'><classname>InvalidWorkspaceException</classname></ulink>">
<!ENTITY UnsupportedRequestException "<ulink url='&API;graph/request/UnsupportedRequestException.html'><classname>UnsupportedRequestException</classname></ulink>">
@@ -140,6 +145,12 @@
<!ENTITY MimeTypeDetector "<ulink url='&API;graph/mimetype/MimeTypeDetector.html'><interface>MimeTypeDetector</interface></ulink>">
<!ENTITY MockSequencerOutput "<ulink url='&API;graph/sequencer/MockSequencerOutput.html'><interface>MockSequencerOutput</interface></ulink>">
<!ENTITY MockSequencerContext "<ulink url='&API;graph/sequencer/MockSequencerContext.html'><interface>MockSequencerContext</interface></ulink>">
+<!ENTITY Observer "<ulink url='&API;graph/observer/Observer.html'><interface>Observer</interface></ulink>">
+<!ENTITY Observable "<ulink url='&API;graph/observer/Observable.html'><interface>Observable</interface></ulink>">
+<!ENTITY ChangeObserver "<ulink url='&API;graph/observer/ChangeObserver.html'><classname>ChangeObserver</classname></ulink>">
+<!ENTITY NetChangeObserver "<ulink url='&API;graph/observer/NetChangeObserver.html'><classname>NetChangeObserver</classname></ulink>">
+<!ENTITY ChangeObservers "<ulink url='&API;graph/observer/ChangeObservers.html'><classname>ChangeObservers</classname></ulink>">
+<!ENTITY Changes "<ulink url='&API;graph/observer/Changes.html'><classname>Changes</classname></ulink>">
<!-- Types in dna-repository -->
16 years, 6 months
DNA SVN: r1004 - in trunk/docs/reference/src/main/docbook/en-US: content/connectors and 2 other directories.
by dna-commits@lists.jboss.org
Author: rhauch
Date: 2009-06-09 00:32:58 -0400 (Tue, 09 Jun 2009)
New Revision: 1004
Modified:
trunk/docs/reference/src/main/docbook/en-US/content/connectors/federation.xml
trunk/docs/reference/src/main/docbook/en-US/content/connectors/in_memory.xml
trunk/docs/reference/src/main/docbook/en-US/content/connectors/jboss_cache.xml
trunk/docs/reference/src/main/docbook/en-US/content/core/connector.xml
trunk/docs/reference/src/main/docbook/en-US/content/core/graph.xml
trunk/docs/reference/src/main/docbook/en-US/content/core/sequencing.xml
trunk/docs/reference/src/main/docbook/en-US/content/sequencers/compact_node_types.xml
trunk/docs/reference/src/main/docbook/en-US/content/sequencers/image.xml
trunk/docs/reference/src/main/docbook/en-US/content/sequencers/java_source.xml
trunk/docs/reference/src/main/docbook/en-US/content/sequencers/microsoft_office.xml
trunk/docs/reference/src/main/docbook/en-US/content/sequencers/mp3.xml
trunk/docs/reference/src/main/docbook/en-US/content/sequencers/xml.xml
trunk/docs/reference/src/main/docbook/en-US/content/sequencers/zip.xml
trunk/docs/reference/src/main/docbook/en-US/custom.dtd
Log:
Completed (pending editing/reviews) the chapter on the graph model.
Modified: trunk/docs/reference/src/main/docbook/en-US/content/connectors/federation.xml
===================================================================
--- trunk/docs/reference/src/main/docbook/en-US/content/connectors/federation.xml 2009-06-09 03:30:56 UTC (rev 1003)
+++ trunk/docs/reference/src/main/docbook/en-US/content/connectors/federation.xml 2009-06-09 04:32:58 UTC (rev 1004)
@@ -30,8 +30,160 @@
]>
<chapter id="federation-connector">
<title>Federation Connector</title>
- <para>
- blah blah
- </para>
+ <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>
</chapter>
Modified: 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 2009-06-09 03:30:56 UTC (rev 1003)
+++ trunk/docs/reference/src/main/docbook/en-US/content/connectors/in_memory.xml 2009-06-09 04:32:58 UTC (rev 1004)
@@ -31,7 +31,56 @@
<chapter id="in-memory-connector">
<title>In-Memory Connector</title>
<para>
- blah blah
+ 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>
</chapter>
Modified: trunk/docs/reference/src/main/docbook/en-US/content/connectors/jboss_cache.xml
===================================================================
--- trunk/docs/reference/src/main/docbook/en-US/content/connectors/jboss_cache.xml 2009-06-09 03:30:56 UTC (rev 1003)
+++ trunk/docs/reference/src/main/docbook/en-US/content/connectors/jboss_cache.xml 2009-06-09 04:32:58 UTC (rev 1004)
@@ -31,7 +31,78 @@
<chapter id="jboss-cache-connector">
<title>JBoss Cache Connector</title>
<para>
- blah blah
+ 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="federation-connector">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>
</chapter>
Modified: trunk/docs/reference/src/main/docbook/en-US/content/core/connector.xml
===================================================================
--- trunk/docs/reference/src/main/docbook/en-US/content/core/connector.xml 2009-06-09 03:30:56 UTC (rev 1003)
+++ trunk/docs/reference/src/main/docbook/en-US/content/core/connector.xml 2009-06-09 04:32:58 UTC (rev 1004)
@@ -169,7 +169,7 @@
</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.
+ <para>The <link linkend="graph-api-workspaces">previous chapter</link> 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.
@@ -277,300 +277,11 @@
<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.
+ A number of repository connectors are already available in JBoss DNA, and are outlined in detail
+ <link linkend="provied-connectors-part">later in the document</link>.
Note that we do want to build <ulink url="https://jira.jboss.org/jira/secure/IssueNavigator.jspa?reset=true&mod...">more 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>
@@ -1288,174 +999,6 @@
</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">internal 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>
Modified: trunk/docs/reference/src/main/docbook/en-US/content/core/graph.xml
===================================================================
--- trunk/docs/reference/src/main/docbook/en-US/content/core/graph.xml 2009-06-09 03:30:56 UTC (rev 1003)
+++ trunk/docs/reference/src/main/docbook/en-US/content/core/graph.xml 2009-06-09 04:32:58 UTC (rev 1004)
@@ -715,17 +715,252 @@
as required.
</para>
</sect1>
+ <sect1 id="graph-location">
+ <title>Locations</title>
+ <para>
+ In addition to &Path; objects, nodes can be identified by one or more <emphasis>identification properties</emphasis>.
+ These really are just &Property; instances with names that have a special meaning
+ (usually to <link linkend="connector-framework">connectors</link>).
+ JBoss DNA also defines a &Location; class that encapsulates:
+ <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 and/or the identification properties, they can create a &Location; object
+ and then use that to identify the node. &Location; is a class that can be instantiated through factory
+ methods on the class:
+ </para>
+ <programlisting>
+public abstract class &Location; implements &Iterable;<&Property;>, Comparable<&Location;> {
+
+ public static &Location; create( &Path; path ) { ... }
+ public static &Location; create( &UUID; uuid ) { ... }
+ public static &Location; create( &Path; path, &UUID; uuid ) { ... }
+ public static &Location; create( &Path; path, &Property; idProperty ) { ... }
+ public static &Location; create( &Path; path, &Property; firstIdProperty,
+ &Property;... remainingIdProperties ) { ... }
+ public static &Location; create( &Path; path, &Iterable;<&Property; idProperties ) { ... }
+ public static &Location; create( &Property; idProperty ) { ... }
+ public static &Location; create( &Property; firstIdProperty,
+ &Property;... remainingIdProperties ) { ... }
+ public static &Location; create( &Iterable;<&Property;> idProperties ) { ... }
+ public static &Location; create( &List;<&Property;> idProperties ) { ... }
+ ...
+} </programlisting>
+ <para>
+ Like many of the other classes and interfaces, &Location; is immutable and cannot be changed once created.
+ However, there are methods on the class to create a copy of the &Location; object with a different &Path;,
+ a different &UUID;, or different identification properties:
+ </para>
+ <programlisting>
+public abstract class &Location; implements &Iterable;<&Property;>, Comparable<&Location;> {
+ ...
+ public &Location; with( &Property; newIdProperty );
+ public &Location; with( &Path; newPath );
+ public &Location; with( &UUID; uuid );
+ ...
+} </programlisting>
+ <para>
+ One more thing about locations: we'll see later in the next chapter how they are used to make requests
+ to the <link linkend="connector-framework">connectors</link>. When creating the requests, clients usually have an
+ incomplete location (e.g., a path but no identification properties). When processing the requests, connectors
+ provide an <emphasis>actual</emphasis> location that contains the path <emphasis>and</emphasis> all identification properties.
+ If actual &Location; objects are then reused in subsequent requests by the client, the connectors will have the benefit of having
+ both the path and identification properties and may be able to more efficiently locate the identified node.
+ </para>
+ </sect1>
<sect1 id="graph-api">
<title>Graph API</title>
<para>
-
+ JBoss DNA's <emphasis>Graph API</emphasis> was designed as a lightweight public API for working with graph information.
+ The &Graph; class is the primary class in API, and each instance represents a single, independent
+ view of a single graph. &Graph; instances don't maintain state, so every request (or batch of requests) operate against
+ the underlying graph and the return <emphasis>immutable snapshots</emphasis> of the requested state at the time
+ the request was made.
</para>
- </sect1>
- <sect1 id="graph-requests">
- <title>Request Model</title>
<para>
-
+ There are several ways to obtain a &Graph; instance, as we'll see in later chapters. For the time being, the important
+ thing to understand is what a &Graph; instance represents and how it interacts with the underlying content to return
+ representations of portions of that underlying graph content.
</para>
+ <para>
+ The &Graph; class basically represents an <ulink url="http://www.martinfowler.com/bliki/DomainSpecificLanguage.html">internal 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>
+ <para>
+ The next few subsections describe how to use a &Graph; instance.
+ </para>
+ <sect2 id="graph-api-workspaces">
+ <title>Using workspaces</title>
+ <para>
+ JBoss DNA graphs have the notion of <emphasis>workspaces</emphasis> that provide different views of the content. Some graphs may have
+ one workspace, while others may have multiple workspaces. Some graphs will allow a client to create new workspaces or destroy
+ existing workspaces, while other graphs will not allow adding or removing workspaces. Some graphs may have workspaces may show the same (or very
+ similar) content, while other graphs may have workspaces that each contain completely independent content.
+ </para>
+ <para>
+ The &Graph; object is always bound to a workspace, which initially is the <emphasis>default workspace</emphasis>. To find out
+ what the name of the default workspace is, simply ask for the current workspace after creating the &Graph;:
+ </para>
+ <programlisting>
+&Workspace; current = graph.getCurrentWorkspace();
+</programlisting>
+ <para>
+ To obtain the list of workspaces available in a graph, simply ask for them:
+ </para>
+ <programlisting>
+&Set;<&String;> workspaceNames = graph.getWorkspaces();
+</programlisting>
+ <para>Once you know the name of a particular 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. To go back to the default workspace, simply supply a null name:
+ </para>
+ <programlisting>
+graph.useWorkspace(null);
+</programlisting>
+ <para>
+ 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="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>
Modified: trunk/docs/reference/src/main/docbook/en-US/content/core/sequencing.xml
===================================================================
--- trunk/docs/reference/src/main/docbook/en-US/content/core/sequencing.xml 2009-06-09 03:30:56 UTC (rev 1003)
+++ trunk/docs/reference/src/main/docbook/en-US/content/core/sequencing.xml 2009-06-09 04:32:58 UTC (rev 1004)
@@ -29,7 +29,7 @@
%CustomDTD;
]>
<chapter id="sequencing_framework">
- <title>Sequencing content</title>
+ <title>Sequencing framework</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>
@@ -253,207 +253,14 @@
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>
+ <sect1 id="sequencer-library">
+ <title>Out-of-the-box 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.
+ A number of sequencers are already available in JBoss DNA, and are outlined in detail
+ <link linkend="provied-sequencers-part">later in the document</link>.
+ Note that we do want to build <ulink url="https://jira.jboss.org/jira/secure/IssueNavigator.jspa?reset=true&mod...">more sequencers</ulink>
+ in the upcoming releases.
</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</ulink>
- 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>
Modified: trunk/docs/reference/src/main/docbook/en-US/content/sequencers/compact_node_types.xml
===================================================================
--- trunk/docs/reference/src/main/docbook/en-US/content/sequencers/compact_node_types.xml 2009-06-09 03:30:56 UTC (rev 1003)
+++ trunk/docs/reference/src/main/docbook/en-US/content/sequencers/compact_node_types.xml 2009-06-09 04:32:58 UTC (rev 1004)
@@ -31,6 +31,7 @@
<chapter id="cnd-sequencer">
<title>Compact Node Type (CND) Sequencer</title>
<para>
- blah blah
+ 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>
</chapter>
\ No newline at end of file
Modified: trunk/docs/reference/src/main/docbook/en-US/content/sequencers/image.xml
===================================================================
--- trunk/docs/reference/src/main/docbook/en-US/content/sequencers/image.xml 2009-06-09 03:30:56 UTC (rev 1003)
+++ trunk/docs/reference/src/main/docbook/en-US/content/sequencers/image.xml 2009-06-09 04:32:58 UTC (rev 1004)
@@ -30,7 +30,79 @@
]>
<chapter id="image-sequencer">
<title>Image Sequencer</title>
- <para>
- blah blah
- </para>
+ <para>
+ The &ImageMetadataSequencer; sequencer 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>
</chapter>
\ No newline at end of file
Modified: trunk/docs/reference/src/main/docbook/en-US/content/sequencers/java_source.xml
===================================================================
--- trunk/docs/reference/src/main/docbook/en-US/content/sequencers/java_source.xml 2009-06-09 03:30:56 UTC (rev 1003)
+++ trunk/docs/reference/src/main/docbook/en-US/content/sequencers/java_source.xml 2009-06-09 04:32:58 UTC (rev 1004)
@@ -30,7 +30,28 @@
]>
<chapter id="java-source-sequencer">
<title>Java Source File Sequencer</title>
- <para>
- blah blah
- </para>
+ <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>
</chapter>
\ No newline at end of file
Modified: trunk/docs/reference/src/main/docbook/en-US/content/sequencers/microsoft_office.xml
===================================================================
--- trunk/docs/reference/src/main/docbook/en-US/content/sequencers/microsoft_office.xml 2009-06-09 03:30:56 UTC (rev 1003)
+++ trunk/docs/reference/src/main/docbook/en-US/content/sequencers/microsoft_office.xml 2009-06-09 04:32:58 UTC (rev 1004)
@@ -30,7 +30,26 @@
]>
<chapter id="msoffice-sequencer">
<title><trademark class='registered'>Microsoft Office</trademark> Document Sequencer</title>
- <para>
- blah blah
- </para>
+ <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>
</chapter>
\ No newline at end of file
Modified: trunk/docs/reference/src/main/docbook/en-US/content/sequencers/mp3.xml
===================================================================
--- trunk/docs/reference/src/main/docbook/en-US/content/sequencers/mp3.xml 2009-06-09 03:30:56 UTC (rev 1003)
+++ trunk/docs/reference/src/main/docbook/en-US/content/sequencers/mp3.xml 2009-06-09 04:32:58 UTC (rev 1004)
@@ -30,7 +30,24 @@
]>
<chapter id="mp3-sequencer">
<title>MP3 Sequencer</title>
- <para>
- blah blah
- </para>
+ <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</ulink>
+ 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>
</chapter>
\ No newline at end of file
Modified: trunk/docs/reference/src/main/docbook/en-US/content/sequencers/xml.xml
===================================================================
--- trunk/docs/reference/src/main/docbook/en-US/content/sequencers/xml.xml 2009-06-09 03:30:56 UTC (rev 1003)
+++ trunk/docs/reference/src/main/docbook/en-US/content/sequencers/xml.xml 2009-06-09 04:32:58 UTC (rev 1004)
@@ -31,6 +31,6 @@
<chapter id="xml-sequencer">
<title>XML Document Sequencer</title>
<para>
- blah blah
+ This sequencer stores the structure and data of an XML file into the repository.
</para>
</chapter>
\ No newline at end of file
Modified: trunk/docs/reference/src/main/docbook/en-US/content/sequencers/zip.xml
===================================================================
--- trunk/docs/reference/src/main/docbook/en-US/content/sequencers/zip.xml 2009-06-09 03:30:56 UTC (rev 1003)
+++ trunk/docs/reference/src/main/docbook/en-US/content/sequencers/zip.xml 2009-06-09 04:32:58 UTC (rev 1004)
@@ -30,7 +30,21 @@
]>
<chapter id="zip-file-sequencer">
<title>ZIP File Sequencer</title>
- <para>
- blah blah
- </para>
+ <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>
</chapter>
\ No newline at end of file
Modified: trunk/docs/reference/src/main/docbook/en-US/custom.dtd
===================================================================
--- trunk/docs/reference/src/main/docbook/en-US/custom.dtd 2009-06-09 03:30:56 UTC (rev 1003)
+++ trunk/docs/reference/src/main/docbook/en-US/custom.dtd 2009-06-09 04:32:58 UTC (rev 1004)
@@ -53,6 +53,9 @@
<!ENTITY Serializable "<ulink url='&Java;java/io/Serializable.html'><interface>Serializable</interface></ulink>">
<!ENTITY Iterator "<ulink url='&Java;java/util/Iterator.html'><interface>Iterator</interface></ulink>">
<!ENTITY Iteratable "<ulink url='&Java;java/util/Iteratable.html'><interface>Iteratable</interface></ulink>">
+<!ENTITY Set "<ulink url='&Java;java/util/Set.html'><interface>Set</interface></ulink>">
+<!ENTITY Map "<ulink url='&Java;java/util/Map.html'><interface>Map</interface></ulink>">
+<!ENTITY List "<ulink url='&Java;java/util/List.html'><interface>List</interface></ulink>">
<!ENTITY BigDecimal "<ulink url='&Java;java/math/BigDecimal.html'><classname>BigDecimal</classname></ulink>">
<!ENTITY Calendar "<ulink url='&Java;java/util/Calendar.html'><classname>Calendar</classname></ulink>">
<!ENTITY Date "<ulink url='&Java;java/util/Date.html'><classname>Date</classname></ulink>">
16 years, 6 months
DNA SVN: r1003 - in trunk: docs/reference/src/main/docbook/en-US and 1 other directories.
by dna-commits@lists.jboss.org
Author: rhauch
Date: 2009-06-08 23:30:56 -0400 (Mon, 08 Jun 2009)
New Revision: 1003
Added:
trunk/dna-graph/src/main/java/org/jboss/dna/graph/property/Readable.java
Modified:
trunk/dna-graph/src/main/java/org/jboss/dna/graph/property/Name.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/property/Path.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/property/Property.java
trunk/docs/reference/src/main/docbook/en-US/content/core/graph.xml
trunk/docs/reference/src/main/docbook/en-US/custom.dtd
Log:
Added the Readable interface, which not only made the documentation more clear but also added value to the API. Also elaborated a lot of the graph API section in the Reference Guide.
Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/property/Name.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/property/Name.java 2009-06-09 03:30:34 UTC (rev 1002)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/property/Name.java 2009-06-09 03:30:56 UTC (rev 1003)
@@ -25,8 +25,6 @@
import java.io.Serializable;
import net.jcip.annotations.Immutable;
-import org.jboss.dna.common.text.TextEncoder;
-import org.jboss.dna.graph.property.Path.Segment;
/**
* A qualified name consisting of a namespace and a local name.
@@ -34,7 +32,7 @@
* @author Randall Hauch
*/
@Immutable
-public interface Name extends Comparable<Name>, Serializable {
+public interface Name extends Comparable<Name>, Serializable, Readable {
/**
* Get the local name part of this qualified name.
@@ -49,69 +47,4 @@
* @return the URI; never null but possibly empty
*/
String getNamespaceUri();
-
- /**
- * Get the string form of the name. The {@link Path#DEFAULT_ENCODER default encoder} is used to encode characters in the local
- * name and namespace.
- *
- * @return the encoded string
- * @see #getString(TextEncoder)
- */
- public String getString();
-
- /**
- * Get the encoded string form of the name, using the supplied encoder to encode characters in the local name and namespace.
- *
- * @param encoder the encoder to use, or null if the {@link Path#DEFAULT_ENCODER default encoder} should be used
- * @return the encoded string
- * @see #getString()
- */
- public String getString( TextEncoder encoder );
-
- /**
- * Get the string form of the name, using the supplied namespace registry to convert the {@link #getNamespaceUri() namespace
- * URI} to a prefix. The {@link Path#DEFAULT_ENCODER default encoder} is used to encode characters in each of the path
- * segments.
- *
- * @param namespaceRegistry the namespace registry that should be used to obtain the prefix for the
- * {@link Name#getNamespaceUri() namespace URI}
- * @return the encoded string
- * @throws IllegalArgumentException if the namespace registry is null
- * @see #getString(NamespaceRegistry,TextEncoder)
- */
- public String getString( NamespaceRegistry namespaceRegistry );
-
- /**
- * Get the encoded string form of the name, using the supplied namespace registry to convert the {@link #getNamespaceUri()
- * namespace URI} to a prefix.
- *
- * @param namespaceRegistry the namespace registry that should be used to obtain the prefix for the
- * {@link Name#getNamespaceUri() namespace URI}
- * @param encoder the encoder to use, or null if the {@link Path#DEFAULT_ENCODER default encoder} should be used
- * @return the encoded string
- * @throws IllegalArgumentException if the namespace registry is null
- * @see #getString(NamespaceRegistry)
- */
- public String getString( NamespaceRegistry namespaceRegistry,
- TextEncoder encoder );
-
- /**
- * Get the encoded string form of the name, using the supplied namespace registry to convert the names' namespace URIs to
- * prefixes and the supplied encoder to encode characters in each of the path segments, and using the second delimiter to
- * encode (or convert) the delimiter used between the namespace prefix and the local part.
- *
- * @param namespaceRegistry the namespace registry that should be used to obtain the prefix for the
- * {@link Name#getNamespaceUri() namespace URIs} in the segment {@link Segment#getName() names}
- * @param encoder the encoder to use for encoding the {@link Name#getLocalName() local part} and
- * {@link Name#getNamespaceUri() namespace prefix}, or null if the {@link Path#DEFAULT_ENCODER default encoder} should
- * be used
- * @param delimiterEncoder the encoder to use for encoding the delimiter between the {@link Name#getLocalName() local part}
- * and {@link Name#getNamespaceUri() namespace prefix}, or null if the standard delimiter should be used
- * @return the encoded string
- * @see #getString(NamespaceRegistry)
- * @see #getString(NamespaceRegistry, TextEncoder)
- */
- public String getString( NamespaceRegistry namespaceRegistry,
- TextEncoder encoder,
- TextEncoder delimiterEncoder );
}
Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/property/Path.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/property/Path.java 2009-06-09 03:30:34 UTC (rev 1002)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/property/Path.java 2009-06-09 03:30:56 UTC (rev 1003)
@@ -74,7 +74,7 @@
* @author John Verhaeg
*/
@Immutable
-public interface Path extends Comparable<Path>, Iterable<Path.Segment>, Serializable {
+public interface Path extends Comparable<Path>, Iterable<Path.Segment>, Serializable, Readable {
/**
* The text encoder that does nothing.
@@ -149,7 +149,7 @@
* @author Randall Hauch
*/
@Immutable
- public static interface Segment extends Cloneable, Comparable<Segment>, Serializable {
+ public static interface Segment extends Cloneable, Comparable<Segment>, Serializable, Readable {
/**
* Get the name component of this segment.
@@ -194,73 +194,6 @@
* @see #getString(TextEncoder)
*/
public String getUnencodedString();
-
- /**
- * Get the string form of the segment. The {@link #DEFAULT_ENCODER default encoder} is used to encode characters in each
- * of the path segments.
- *
- * @return the encoded string
- * @see #getString(TextEncoder)
- */
- public String getString();
-
- /**
- * Get the encoded string form of the segment, using the supplied encoder to encode characters in each of the path
- * segments.
- *
- * @param encoder the encoder to use, or null if the {@link #DEFAULT_ENCODER default encoder} should be used
- * @return the encoded string
- * @see #getString()
- */
- public String getString( TextEncoder encoder );
-
- /**
- * Get the string form of the segment, using the supplied namespace registry to convert the name's namespace URI to a
- * prefix. The {@link #DEFAULT_ENCODER default encoder} is used to encode characters in each of the path segments.
- *
- * @param namespaceRegistry the namespace registry that should be used to obtain the prefix for the
- * {@link Name#getNamespaceUri() namespace URI} in the segment's {@link #getName() name}
- * @return the encoded string
- * @throws IllegalArgumentException if the namespace registry is null
- * @see #getString(NamespaceRegistry,TextEncoder)
- */
- public String getString( NamespaceRegistry namespaceRegistry );
-
- /**
- * Get the encoded string form of the segment, using the supplied namespace registry to convert the name's namespace URI
- * to a prefix and the supplied encoder to encode characters in each of the path segments.
- *
- * @param namespaceRegistry the namespace registry that should be used to obtain the prefix for the
- * {@link Name#getNamespaceUri() namespace URI} in the segment's {@link #getName() name}
- * @param encoder the encoder to use, or null if the {@link #DEFAULT_ENCODER default encoder} should be used
- * @return the encoded string
- * @throws IllegalArgumentException if the namespace registry is null
- * @see #getString(NamespaceRegistry)
- */
- public String getString( NamespaceRegistry namespaceRegistry,
- TextEncoder encoder );
-
- /**
- * Get the encoded string form of the segment, using the supplied namespace registry to convert the names' namespace URIs
- * to prefixes and the supplied encoder to encode characters in each of the path segments. The second encoder is used to
- * encode (or convert) the delimiter between the {@link Name#getNamespaceUri() namespace prefix} and the
- * {@link Name#getLocalName() local part}.
- *
- * @param namespaceRegistry the namespace registry that should be used to obtain the prefix for the
- * {@link Name#getNamespaceUri() namespace URIs} in the segment {@link Segment#getName() names}
- * @param encoder the encoder to use for encoding the {@link Name#getLocalName() local part} and
- * {@link Name#getNamespaceUri() namespace prefix} in the segment's {@link #getName() name}, or null if the
- * {@link #DEFAULT_ENCODER default encoder} should be used
- * @param delimiterEncoder the encoder to use for encoding the delimiter between the {@link Name#getLocalName() local
- * part} and {@link Name#getNamespaceUri() namespace prefix} of each {@link Path#getSegmentsList() segment}, or
- * null if the standard delimiters should be used
- * @return the encoded string
- * @see #getString(NamespaceRegistry)
- * @see #getString(NamespaceRegistry, TextEncoder)
- */
- public String getString( NamespaceRegistry namespaceRegistry,
- TextEncoder encoder,
- TextEncoder delimiterEncoder );
}
/**
@@ -516,74 +449,4 @@
*/
public List<Segment> getSegmentsList();
- /**
- * Get the string form of the path. The {@link #DEFAULT_ENCODER default encoder} is used to encode characters in each of the
- * path segments.
- *
- * @return the encoded string
- * @see #getString(TextEncoder)
- */
- public String getString();
-
- /**
- * Get the encoded string form of the path, using the supplied encoder to encode characters in each of the path segments.
- *
- * @param encoder the encoder to use, or null if the {@link #DEFAULT_ENCODER default encoder} should be used
- * @return the encoded string
- * @see #getString()
- */
- public String getString( TextEncoder encoder );
-
- /**
- * Get the string form of the path, using the supplied namespace registry to convert the names' namespace URIs to prefixes.
- * The {@link #DEFAULT_ENCODER default encoder} is used to encode characters in each of the path segments. The second encoder
- * is used to encode (or convert) the delimiter between the {@link Name#getNamespaceUri() namespace prefix} and the
- * {@link Name#getLocalName() local part}.
- *
- * @param namespaceRegistry the namespace registry that should be used to obtain the prefix for the
- * {@link Name#getNamespaceUri() namespace URIs} in the segment {@link Segment#getName() names}
- * @return the encoded string
- * @throws IllegalArgumentException if the namespace registry is null
- * @see #getString(NamespaceRegistry,TextEncoder)
- * @see #getString(NamespaceRegistry, TextEncoder, TextEncoder)
- */
- public String getString( NamespaceRegistry namespaceRegistry );
-
- /**
- * Get the encoded string form of the path, using the supplied namespace registry to convert the names' namespace URIs to
- * prefixes and the supplied encoder to encode characters in each of the path segments.
- *
- * @param namespaceRegistry the namespace registry that should be used to obtain the prefix for the
- * {@link Name#getNamespaceUri() namespace URIs} in the segment {@link Segment#getName() names}, or null if the
- * namespace registry should not be used
- * @param encoder the encoder to use for encoding the {@link Name#getLocalName() local part} and
- * {@link Name#getNamespaceUri() namespace prefix} of each {@link Path#getSegmentsList() segment}, or null if the
- * {@link #DEFAULT_ENCODER default encoder} should be used
- * @return the encoded string
- * @see #getString(NamespaceRegistry)
- * @see #getString(NamespaceRegistry, TextEncoder, TextEncoder)
- */
- public String getString( NamespaceRegistry namespaceRegistry,
- TextEncoder encoder );
-
- /**
- * Get the encoded string form of the path, using the supplied namespace registry to convert the names' namespace URIs to
- * prefixes and the supplied encoder to encode characters in each of the path segments.
- *
- * @param namespaceRegistry the namespace registry that should be used to obtain the prefix for the
- * {@link Name#getNamespaceUri() namespace URIs} in the segment {@link Segment#getName() names}
- * @param encoder the encoder to use for encoding the {@link Name#getLocalName() local part} and
- * {@link Name#getNamespaceUri() namespace prefix} of each {@link Path#getSegmentsList() segment}, or null if the
- * {@link #DEFAULT_ENCODER default encoder} should be used
- * @param delimiterEncoder the encoder to use for encoding the delimiter between the {@link Name#getLocalName() local part}
- * and {@link Name#getNamespaceUri() namespace prefix} of each {@link Path#getSegmentsList() segment}, and for encoding
- * the path delimiter, or null if the standard delimiters should be used
- * @return the encoded string
- * @see #getString(NamespaceRegistry)
- * @see #getString(NamespaceRegistry, TextEncoder)
- */
- public String getString( NamespaceRegistry namespaceRegistry,
- TextEncoder encoder,
- TextEncoder delimiterEncoder );
-
}
Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/property/Property.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/property/Property.java 2009-06-09 03:30:34 UTC (rev 1002)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/property/Property.java 2009-06-09 03:30:56 UTC (rev 1003)
@@ -25,7 +25,6 @@
import java.util.Iterator;
import net.jcip.annotations.Immutable;
-import org.jboss.dna.common.text.TextEncoder;
/**
* Representation of a property consisting of a name and value(s). Note that this property is immutable, meaning that the property
@@ -59,7 +58,7 @@
* @author Randall Hauch
*/
@Immutable
-public interface Property extends Iterable<Object>, Comparable<Property> {
+public interface Property extends Iterable<Object>, Comparable<Property>, Readable {
/**
* Get the name of the property.
@@ -155,67 +154,4 @@
* @see ValueFactory#create(Object[])
*/
Object[] getValuesAsArray();
-
- /**
- * Get the string form of the property, using the default encoder.
- *
- * @return the encoded string
- * @see #getString(TextEncoder)
- */
- public String getString();
-
- /**
- * Get the encoded string form of the property, using the supplied encoder to encode characters in the property's name and
- * values.
- *
- * @param encoder the encoder to use, or null if the default encoder should be used
- * @return the encoded string
- * @see #getString()
- */
- public String getString( TextEncoder encoder );
-
- /**
- * Get the string form of the property, using the supplied namespace registry to convert the property's name and values.
- *
- * @param namespaceRegistry the namespace registry that should be used to obtain the prefix for the
- * {@link Name#getNamespaceUri() namespace URIs} in the property {@link #getName() name}
- * @return the string
- * @throws IllegalArgumentException if the namespace registry is null
- * @see #getString(NamespaceRegistry,TextEncoder)
- * @see #getString(NamespaceRegistry, TextEncoder, TextEncoder)
- */
- public String getString( NamespaceRegistry namespaceRegistry );
-
- /**
- * Get the encoded string form of the property, using the supplied namespace registry to convert the property's namespace URIs
- * to prefixes and the supplied encoder to encode characters in the property's name and values.
- *
- * @param namespaceRegistry the namespace registry that should be used to obtain the prefix for the
- * {@link Name#getNamespaceUri() namespace URIs} in the property {@link #getName() name}, or null if the namespace
- * registry should not be used
- * @param encoder the encoder to use for encoding the name and values, or null if the default encoder should be used
- * @return the encoded string
- * @see #getString(NamespaceRegistry)
- * @see #getString(NamespaceRegistry, TextEncoder, TextEncoder)
- */
- public String getString( NamespaceRegistry namespaceRegistry,
- TextEncoder encoder );
-
- /**
- * Get the encoded string form of the property, using the supplied namespace registry to convert the property's namespace URIs
- * to prefixes and the supplied encoder to encode characters in the property's name and values.
- *
- * @param namespaceRegistry the namespace registry that should be used to obtain the prefix for the
- * {@link Name#getNamespaceUri() namespace URIs} in the property {@link #getName() name}, or null if the namespace
- * registry should not be used
- * @param encoder the encoder to use for encoding the name and values, or null if the default encoder should be used
- * @param delimiterEncoder the encoder to use for encoding delimiters used in paths and names, or null if the standard
- * delimiters should be used
- * @return the encoded string
- * @see #getString(NamespaceRegistry)
- * @see #getString(NamespaceRegistry, TextEncoder)
- */
- public String getString( NamespaceRegistry namespaceRegistry,
- TextEncoder encoder,
- TextEncoder delimiterEncoder );
}
Copied: trunk/dna-graph/src/main/java/org/jboss/dna/graph/property/Readable.java (from rev 1002, trunk/dna-graph/src/main/java/org/jboss/dna/graph/property/Name.java)
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/property/Readable.java (rev 0)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/property/Readable.java 2009-06-09 03:30:56 UTC (rev 1003)
@@ -0,0 +1,95 @@
+/*
+ * 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 software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.dna.graph.property;
+
+import net.jcip.annotations.Immutable;
+import org.jboss.dna.common.text.TextEncoder;
+import org.jboss.dna.graph.property.Path.Segment;
+
+/**
+ * An interface defining methods to obtain a "readable" string representation.
+ */
+@Immutable
+public interface Readable {
+
+ /**
+ * Get the string form of the object. A {@link Path#DEFAULT_ENCODER default encoder} is used to encode characters.
+ *
+ * @return the encoded string
+ * @see #getString(TextEncoder)
+ */
+ public String getString();
+
+ /**
+ * Get the encoded string form of the object, using the supplied encoder to encode characters.
+ *
+ * @param encoder the encoder to use, or null if the {@link Path#DEFAULT_ENCODER default encoder} should be used
+ * @return the encoded string
+ * @see #getString()
+ */
+ public String getString( TextEncoder encoder );
+
+ /**
+ * Get the string form of the object, using the supplied namespace registry to convert any namespace URIs to prefixes. A
+ * {@link Path#DEFAULT_ENCODER default encoder} is used to encode characters.
+ *
+ * @param namespaceRegistry the namespace registry that should be used to obtain the prefix for any namespace URIs
+ * @return the encoded string
+ * @throws IllegalArgumentException if the namespace registry is null
+ * @see #getString(NamespaceRegistry,TextEncoder)
+ */
+ public String getString( NamespaceRegistry namespaceRegistry );
+
+ /**
+ * Get the encoded string form of the object, using the supplied namespace registry to convert the any namespace URIs to
+ * prefixes.
+ *
+ * @param namespaceRegistry the namespace registry that should be used to obtain the prefix for the namespace URIs
+ * @param encoder the encoder to use, or null if the {@link Path#DEFAULT_ENCODER default encoder} should be used
+ * @return the encoded string
+ * @throws IllegalArgumentException if the namespace registry is null
+ * @see #getString(NamespaceRegistry)
+ */
+ public String getString( NamespaceRegistry namespaceRegistry,
+ TextEncoder encoder );
+
+ /**
+ * Get the encoded string form of the object, using the supplied namespace registry to convert the names' namespace URIs to
+ * prefixes and the supplied encoder to encode characters, and using the second delimiter to encode (or convert) the delimiter
+ * used between the namespace prefix and the local part of any names.
+ *
+ * @param namespaceRegistry the namespace registry that should be used to obtain the prefix for the namespace URIs in the
+ * segment {@link Segment#getName() names}
+ * @param encoder the encoder to use for encoding the local part and namespace prefix of any names, or null if the
+ * {@link Path#DEFAULT_ENCODER default encoder} should be used
+ * @param delimiterEncoder the encoder to use for encoding the delimiter between the local part and namespace prefix of any
+ * names, or null if the standard delimiter should be used
+ * @return the encoded string
+ * @see #getString(NamespaceRegistry)
+ * @see #getString(NamespaceRegistry, TextEncoder)
+ */
+ public String getString( NamespaceRegistry namespaceRegistry,
+ TextEncoder encoder,
+ TextEncoder delimiterEncoder );
+}
Modified: trunk/docs/reference/src/main/docbook/en-US/content/core/graph.xml
===================================================================
--- trunk/docs/reference/src/main/docbook/en-US/content/core/graph.xml 2009-06-09 03:30:34 UTC (rev 1002)
+++ trunk/docs/reference/src/main/docbook/en-US/content/core/graph.xml 2009-06-09 03:30:56 UTC (rev 1003)
@@ -73,7 +73,21 @@
&Name; is also a interface rather than a concrete class:
</para>
<programlisting>
-</programlisting>
+@Immutable
+public interface &Name; extends Comparable<&Name;>, &Serializable;, &Readable; {
+
+ /**
+ * Get the local name part of this qualified name.
+ * @return the local name; never null
+ */
+ String getLocalName();
+
+ /**
+ * Get the URI for the namespace used in this qualified name.
+ * @return the URI; never null but possibly empty
+ */
+ String getNamespaceUri();
+}</programlisting>
<para>
This means that you need to use a factory to create &Name; instances.
</para>
@@ -91,32 +105,22 @@
via the <code>getValueFactories()</code> method. But before we see that, let's first discuss how names are represented as strings.
</para>
<para>
- We mentioned earlier that names are qualified with a namespace, and namespaces are identified by URIs.
- Also, the local part of the names can contain any character. So what do the string representation of names look like?
- </para>
- <para>
- The &Name; interface defines a number of <code>getString(...)</code> methods that take different parameters.
- The <code>getString()</code> method (which is called by the <code>toString()</code> method) will simply output
- the namespace URI followed by a ':' and an local name (encoded with a default &TextEncoder; implementation, which
- will be discussed in the <link linkend="graph-encoders">next section</link>). If you want to use the
- namespace prefix, then simply use one of the <code>getString(...)</code> methods that takes a &NamespaceRegistry;
- parameter.
- </para>
- <para>
We'll see how names are used later one, but one more point to make: &Name; is both serializable and comparable,
and all implementations should support <code>equals(...)</code> and <code>hashCode()</code> so that &Name; can
- be used as a key in a hash-based map.
+ be used as a key in a hash-based map. &Name; also extends the &Readable; interface, which we'll learn
+ more about later in this chapter.
</para>
</sect1>
<sect1 id="graph-paths">
<title>Paths</title>
<para>
Another important concept in JBoss DNA's graph model is that of a <emphasis>path</emphasis>, which provides a way
- of locating a node within a hierarchy. JBoss DNA's &Path; object is actually comprised of an ordered sequence
- of &PathSegment; objects:
+ of locating a node within a hierarchy. JBoss DNA's &Path; object is an immutable ordered sequence
+ of &PathSegment; objects. A small portion of the interface is shown here:
</para>
<programlisting>
-public interface &Path; extends Comparable<Path>, Iterable<&PathSegment;>, Serializable {
+@Immutable
+public interface &Path; extends Comparable<Path>, Iterable<&PathSegment;>, &Serializable;, &Readable; {
/**
* Return the number of segments in this path.
@@ -130,101 +134,586 @@
*/
public boolean isRoot();
- /**
- * {@inheritDoc}
- */
- public Iterator<&PathSegment;> iterator();
+ /**
+ * {@inheritDoc}
+ */
+ public Iterator<&PathSegment;> iterator();
- /**
- * Obtain a copy of the segments in this path. None of the segments are encoded.
- * @return the array of segments as a copy
- */
- public &PathSegment;[] getSegmentsArray();
+ /**
+ * Obtain a copy of the segments in this path. None of the segments are encoded.
+ * @return the array of segments as a copy
+ */
+ public &PathSegment;[] getSegmentsArray();
- /**
- * Get an unmodifiable list of the path segments.
- * @return the unmodifiable list of path segments; never null
- */
- public List<&PathSegment;> getSegmentsList();
- /**
- * Get the last segment in this path.
- * @return the last segment, or null if the path is empty
- */
- public &PathSegment; getLastSegment();
+ /**
+ * Get an unmodifiable list of the path segments.
+ * @return the unmodifiable list of path segments; never null
+ */
+ public List<&PathSegment;> getSegmentsList();
+ /**
+ * Get the last segment in this path.
+ * @return the last segment, or null if the path is empty
+ */
+ public &PathSegment; getLastSegment();
- /**
- * Get the segment at the supplied index.
- * @param index the index
- * @return the segment
- * @throws IndexOutOfBoundsException if the index is out of bounds
- */
- public &PathSegment; getSegment( int index );
+ /**
+ * Get the segment at the supplied index.
+ * @param index the index
+ * @return the segment
+ * @throws IndexOutOfBoundsException if the index is out of bounds
+ */
+ public &PathSegment; getSegment( int index );
- /**
- * Return an iterator that walks the paths from the root path down to this path. This method
- * always returns at least one path (the root returns an iterator containing itself).
- * @return the path iterator; never null
- */
- public Iterator<&Path;> pathsFromRoot();
+ /**
+ * Return an iterator that walks the paths from the root path down to this path. This method
+ * always returns at least one path (the root returns an iterator containing itself).
+ * @return the path iterator; never null
+ */
+ public Iterator<&Path;> pathsFromRoot();
- /**
- * Return a new path consisting of the segments starting at <code>beginIndex</code> index (inclusive).
- * This is equivalent to calling <code>path.subpath(beginIndex,path.size()-1)</code>.
- * @param beginIndex the beginning index, inclusive.
- * @return the specified subpath
- * @exception IndexOutOfBoundsException if the <code>beginIndex</code> is negative or larger
- * than the length of this <code>Path</code> object
- */
- public &Path; subpath( int beginIndex );
+ /**
+ * Return a new path consisting of the segments starting at <code>beginIndex</code> index (inclusive).
+ * This is equivalent to calling <code>path.subpath(beginIndex,path.size()-1)</code>.
+ * @param beginIndex the beginning index, inclusive.
+ * @return the specified subpath
+ * @exception IndexOutOfBoundsException if the <code>beginIndex</code> is negative or larger
+ * than the length of this <code>Path</code> object
+ */
+ public &Path; subpath( int beginIndex );
- /**
- * Return a new path consisting of the segments between the <code>beginIndex</code> index (inclusive)
- * and the <code>endIndex</code> index (exclusive).
- * @param beginIndex the beginning index, inclusive.
- * @param endIndex the ending index, exclusive.
- * @return the specified subpath
- * @exception IndexOutOfBoundsException if the <code>beginIndex</code> is negative, or
- * <code>endIndex</code> is larger than the length of this <code>Path</code>
- * object, or <code>beginIndex</code> is larger than <code>endIndex</code>.
- */
- public &Path; subpath( int beginIndex, int endIndex );
+ /**
+ * Return a new path consisting of the segments between the <code>beginIndex</code> index (inclusive)
+ * and the <code>endIndex</code> index (exclusive).
+ * @param beginIndex the beginning index, inclusive.
+ * @param endIndex the ending index, exclusive.
+ * @return the specified subpath
+ * @exception IndexOutOfBoundsException if the <code>beginIndex</code> is negative, or
+ * <code>endIndex</code> is larger than the length of this <code>Path</code>
+ * object, or <code>beginIndex</code> is larger than <code>endIndex</code>.
+ */
+ public &Path; subpath( int beginIndex, int endIndex );
...
-
} </programlisting>
<para>
- where each &PathSegment; is comprised of a &Name; and <emphasis>same-name-sibling index</emphasis>:
+ There are actually quite a few methods (not shown above) for obtaining related paths: the path of the parent, the path of an ancestor,
+ resolving a path relative to this path, normalizing a path (by removing "." and ".." segments), finding the lowest
+ common ancestor shared with another path, etc. There are also a number of methods that compare the path with others,
+ including determining whether a path is above, equal to, or below this path.
</para>
+ <para>
+ Each &PathSegment; is an immutable pair of a &Name; and <emphasis>same-name-sibling (SNS) index</emphasis>. When two sibling nodes
+ have the same name, then the first sibling will have SNS index of "1" and the second will be given a SNS index of "2".
+ (This mirrors the same-name-sibling index behavior of <ulink url="&JSR170;">JCR paths</ulink>.)
+ </para>
<programlisting>
+@Immutable
+public static interface &PathSegment; extends Cloneable, Comparable<&PathSegment;>, &Serializable;, &Readable; {
+
+ /**
+ * Get the name component of this segment.
+ * @return the segment's name
+ */
+ public &Name; getName();
+
+ /**
+ * Get the index for this segment, which will be 1 by default.
+ * @return the index
+ */
+ public int getIndex();
+
+ /**
+ * Return whether this segment has an index that is not "1"
+ * @return true if this segment has an index, or false otherwise.
+ */
+ public boolean hasIndex();
+
+ /**
+ * Return whether this segment is a self-reference (or ".").
+ * @return true if the segment is a self-reference, or false otherwise.
+ */
+ public boolean isSelfReference();
+
+ /**
+ * Return whether this segment is a reference to a parent (or "..")
+ * @return true if the segment is a parent-reference, or false otherwise.
+ */
+ public boolean isParentReference();
+}
</programlisting>
<para>
-
+ Like &Name;, the only way to create a &Path; or a &PathSegment; is to use the &PathFactory;, which is available
+ within the &ExecutionContext; via the <code>getValueFactories()</code> method.
</para>
-
</sect1>
<sect1 id="graph-properties">
<title>Properties</title>
<para>
-
+ The JBoss DNA graph model allows nodes to hold multiple properties, where each property is identified
+ by a unique &Name; and may have one or more values. Like many of the other classes used in the graph model,
+ &Property; is an immutable object that, once constructed, can never be changed and therefore provides
+ a consistent snapshot of the state of a property as it existed at the time it was read.
</para>
+ <para>
+ JBoss DNA properties can hold a wide range of value objects, including normal Java strings, names, paths,
+ URIs, booleans, longs, doubles, decimals, binary content, dates, UUIDs, references to other nodes,
+ or any other serializable object. All but three these are the standard Java classes: dates are
+ represented by an immutable &DateTime; class; binary content is represented by an immutable &Binary;
+ interface patterned after the proposed interface of the same name in <ulink url="&JSR283;">JSR-283</ulink>;
+ and &Reference; is an immutable interface patterned after the corresponding interface is
+ <ulink url="&JSR170;">JSR-170</ulink> and <ulink url="&JSR283;">JSR-283</ulink>.
+ </para>
+ <para>
+ The &Property; interface defines methods for obtaining the name and property values:
+ </para>
+ <programlisting>
+@Immutable
+public interface &Property; extends Iterable<Object>, Comparable<&Property;>, &Readable; {
+
+ /**
+ * Get the name of the property.
+ *
+ * @return the property name; never null
+ */
+ &Name; getName();
+
+ /**
+ * Get the number of actual values in this property.
+ * @return the number of actual values in this property; always non-negative
+ */
+ int size();
+
+ /**
+ * Determine whether the property currently has multiple values.
+ * @return true if the property has multiple values, or false otherwise.
+ */
+ boolean isMultiple();
+
+ /**
+ * Determine whether the property currently has a single value.
+ * @return true if the property has a single value, or false otherwise.
+ */
+ boolean isSingle();
+
+ /**
+ * Determine whether this property has no actual values. This method may return <code>true</code>
+ * regardless of whether the property has a single value or multiple values.
+ * This method is a convenience method that is equivalent to <code>size() == 0</code>.
+ * @return true if this property has no values, or false otherwise
+ */
+ boolean isEmpty();
+
+ /**
+ * Obtain the property's first value in its natural form. This is equivalent to calling
+ * <code>isEmpty() ? null : iterator().next()</code>
+ * @return the first value, or null if the property is {@link #isEmpty() empty}
+ */
+ Object getFirstValue();
+
+ /**
+ * Obtain the property's values in their natural form. This is equivalent to calling <code>iterator()</code>.
+ * A valid iterator is returned if the property has single valued or multi-valued.
+ * The resulting iterator is immutable, and all property values are immutable.
+ * @return an iterator over the values; never null
+ */
+ Iterator<?> getValues();
+
+ /**
+ * Obtain the property's values as an array of objects in their natural form.
+ * A valid iterator is returned if the property has single valued or multi-valued, or a
+ * null value is returned if the property is {@link #isEmpty() empty}.
+ * The resulting array is a copy, guaranteeing immutability for the property.
+ * @return the array of values
+ */
+ Object[] getValuesAsArray();
+}
+ </programlisting>
+ <para>
+ Creating &Property; instances is done by using the &PropertyFactory; object owned by the &ExecutionContext;.
+ This factory defines methods for creating properties with a &Name; and various representation of values,
+ including variable-length arguments, arrays, &Iterator;, and &Iterable;.
+ </para>
+ <para>
+ When it comes to using the property values, JBoss DNA takes a non-traditional approach.
+ Many other graph models (including JCR) mark each property with a data type and then require
+ all property values adhere to this data type. When the property values are obtained, they
+ are guaranteed to be of the correct type. However, many times the property's data type may
+ not match the data type expected by the caller, and so a conversion may be required
+ and thus has to be coded.
+ </para>
+ <para>
+ The JBoss DNA graph model uses a different tact. Because callers almost always have to convert the
+ values to the types they can handle, JBoss DNA skips the steps of associating the &Property; with a data type
+ and ensuring the values match. Instead, JBoss DNA simply provides a very easy mechanism to convert
+ the property values to the type desired by the caller. In fact, the conversion mechanism
+ is exactly the same as the factories that create the values in the first place.
+ </para>
</sect1>
<sect1 id="graph-value-factories">
<title>Values and value factories</title>
<para>
-
+ JBoss DNA properties can hold a variety of types of value objects: strings, names, paths,
+ URIs, booleans, longs, doubles, decimals, binary content, dates, UUIDs, references to other nodes,
+ or any other serializable object. To assist in the creation of these values and conversion
+ into other types, JBoss DNA defines a &ValueFactory; interface. This interface is parameterized
+ with the type of value that is being created, but defines methods for creating those values
+ from all of the other known value types:
</para>
+ <programlisting>
+public interface ValueFactory<T> {
+
+ /**
+ * Get the &PropertyType; of values created by this factory.
+ * @return the value type; never null
+ */
+ &PropertyType; getPropertyType();
+
+ /*
+ * Methods to create a value by converting from another value type.
+ * If the supplied value is the same type as returned by this factory,
+ * these methods simply return the supplied value.
+ * All of these methods throw a &ValueFormatException; if the supplied value
+ * could not be converted to this type.
+ */
+ T create( String value ) throws ValueFormatException;
+ T create( String value, TextDecoder decoder ) throws ValueFormatException;
+ T create( int value ) throws ValueFormatException;
+ T create( long value ) throws ValueFormatException;
+ T create( boolean value ) throws ValueFormatException;
+ T create( float value ) throws ValueFormatException;
+ T create( double value ) throws ValueFormatException;
+ T create( &BigDecimal; value ) throws ValueFormatException;
+ T create( &Calendar; value ) throws ValueFormatException;
+ T create( &Date; value ) throws ValueFormatException;
+ T create( &DateTime; value ) throws ValueFormatException;
+ T create( &Name; value ) throws ValueFormatException;
+ T create( &Path; value ) throws ValueFormatException;
+ T create( &Reference; value ) throws ValueFormatException;
+ T create( &URI; value ) throws ValueFormatException;
+ T create( &UUID; value ) throws ValueFormatException;
+ T create( byte[] value ) throws ValueFormatException;
+ T create( &Binary; value ) throws ValueFormatException, IoException;
+ T create( &InputStream; stream, long approximateLength ) throws ValueFormatException, IoException;
+ T create( &Reader; reader, long approximateLength ) throws ValueFormatException, IoException;
+ T create( Object value ) throws ValueFormatException, IoException;
+
+ /*
+ * Methods to create an array of values by converting from another array of values.
+ * If the supplied values are the same type as returned by this factory,
+ * these methods simply return the supplied array.
+ * All of these methods throw a &ValueFormatException; if the supplied values
+ * could not be converted to this type.
+ */
+ T[] create( String[] values ) throws ValueFormatException;
+ T[] create( String[] values, TextDecoder decoder ) throws ValueFormatException;
+ T[] create( int[] values ) throws ValueFormatException;
+ T[] create( long[] values ) throws ValueFormatException;
+ T[] create( boolean[] values ) throws ValueFormatException;
+ T[] create( float[] values ) throws ValueFormatException;
+ T[] create( double[] values ) throws ValueFormatException;
+ T[] create( &BigDecimal;[] values ) throws ValueFormatException;
+ T[] create( &Calendar;[] values ) throws ValueFormatException;
+ T[] create( &Date;[] values ) throws ValueFormatException;
+ T[] create( &DateTime;[] values ) throws ValueFormatException;
+ T[] create( &Name;[] values ) throws ValueFormatException;
+ T[] create( &Path;[] values ) throws ValueFormatException;
+ T[] create( &Reference;[] values ) throws ValueFormatException;
+ T[] create( &URI;[] values ) throws ValueFormatException;
+ T[] create( &UUID;[] values ) throws ValueFormatException;
+ T[] create( byte[][] values ) throws ValueFormatException;
+ T[] create( &Binary;[] values ) throws ValueFormatException, IoException;
+ T[] create( Object[] values ) throws ValueFormatException, IoException;
+
+ /**
+ * Create an iterator over the values (of an unknown type). The factory converts any
+ * values as required. This is useful when wanting to iterate over the values of a property,
+ * where the resulting iterator exposes the desired type.
+ * @param values the values
+ * @return the iterator of type <code>T</code> over the values, or null if the supplied parameter is null
+ * @throws &ValueFormatException; if the conversion from an iterator of objects could not be performed
+ * @throws IoException If an unexpected problem occurs during the conversion.
+ */
+ Iterator<T> create( &Iterator;<?> values ) throws ValueFormatException, IoException;
+ Iterable<T> create( &Iterable;<?> valueIterable ) throws ValueFormatException, IoException;
+}
+ </programlisting>
+ <para>
+ This makes it very easy to convert one or more values (of any type, including mixtures) into
+ corresponding value(s) that are of the desired type. For example, converting the first value
+ of a property (regardless of type) to a String is simple:
+ </para>
+ <programlisting>
+&ValueFactory;<String> stringFactory = ...
+&Property; property = ...
+String value = stringFactory.create( property.getFirstValue() );
+ </programlisting>
+ <para>
+ Likewise, iterating over the values in a property and converting them is just as easy:
+ </para>
+ <programlisting>
+&ValueFactory;<String> stringFactory = ...
+&Property; property = ...
+for ( String value : stringFactory.create(property) ) {
+ // do something with the values
+}
+ </programlisting>
+ <para>
+ What we've glossed over so far, however, is how to obtain the correct &ValueFactory; for the desired type.
+ If you remember back to the previous chapter, &ExecutionContext; has a <code>getValueFactories()</code> method
+ that return a &ValueFactories; interface:
+ </para>
+ <programlisting>
+ </programlisting>
+ <para>
+ This interface exposes a &ValueFactory; for each of the types, and even has methods to obtain a &ValueFactory;
+ given the &PropertyType; enumeration. So, the previous examples could be expanded a bit:
+ </para>
+ <programlisting>
+&ValueFactory;<String> stringFactory = context.getValueFactories().getStringFactory();
+&Property; property = ...
+String value = stringFactory.create( property.getFirstValue() );
+ </programlisting>
+ <para>
+ and
+ </para>
+ <programlisting>
+&ValueFactory;<String> stringFactory = context.getValueFactories().getStringFactory();
+&Property; property = ...
+for ( String value : stringFactory.create(property) ) {
+ // do something with the values
+}
+ </programlisting>
+ <para>
+ You might have noticed that several of the &ValueFactories; methods return subinterfaces of &ValueFactory;. These
+ add type-specific methods that are more commonly needed in certain cases. For example, here is the &NameFactory; interface:
+ </para>
+ <programlisting>
+public interface &NameFactory; extends &ValueFactory;<&Name;> {
+
+ &Name; create( String namespaceUri, String localName );
+ &Name; create( String namespaceUri, String localName, TextDecoder decoder );
+
+ &NamespaceRegistry; getNamespaceRegistry();
+}
+ </programlisting>
+ <para>and here is the &DateTimeFactory; interface, which adds methods for creating &DateTime; values for the current
+ time as well as for specific instants in time:</para>
+ <programlisting>
+public interface &DateTimeFactory; extends &ValueFactory;<&DateTime;> {
+
+ /**
+ * Create a date-time instance for the current time in the local time zone.
+ */
+ &DateTime; create();
+
+ /**
+ * Create a date-time instance for the current time in UTC.
+ */
+ &DateTime; createUtc();
+
+ &DateTime; create( &DateTime; original, long offsetInMillis );
+ &DateTime; create( int year, int monthOfYear, int dayOfMonth,
+ int hourOfDay, int minuteOfHour, int secondOfMinute, int millisecondsOfSecond );
+ &DateTime; create( int year, int monthOfYear, int dayOfMonth,
+ int hourOfDay, int minuteOfHour, int secondOfMinute, int millisecondsOfSecond,
+ int timeZoneOffsetHours );
+ &DateTime; create( int year, int monthOfYear, int dayOfMonth,
+ int hourOfDay, int minuteOfHour, int secondOfMinute, int millisecondsOfSecond,
+ int timeZoneOffsetHours, String timeZoneId );
+}
+ </programlisting>
+ <para>
+ The &PathFactory; interface defines methods for creating relative and absolute &Path; objects using combinations of other &Path; objects
+ and &Name;s and &PathSegment;s,
+ and introduces methods for creating &PathSegment; objects:
+ </para>
+ <programlisting>
+public interface &PathFactory; extends &ValueFactory;<&Path;> {
+
+ &Path; createRootPath();
+ &Path; createAbsolutePath( &Name;... segmentNames );
+ &Path; createAbsolutePath( &PathSegment;... segments );
+ &Path; createAbsolutePath( &Iterable;<&PathSegment;> segments );
+
+ &Path; createRelativePath();
+ &Path; createRelativePath( &Name;... segmentNames );
+ &Path; createRelativePath( &PathSegment;... segments );
+ &Path; createRelativePath( &Iterable;<&PathSegment;> segments );
+
+ &Path; create( Path parentPath, &Path; childPath );
+ &Path; create( Path parentPath, &Name; segmentName, int index );
+ &Path; create( Path parentPath, String segmentName, int index );
+ &Path; create( Path parentPath, &Name;... segmentNames );
+ &Path; create( Path parentPath, &PathSegment;... segments );
+ &Path; create( Path parentPath, &Iterable;<&PathSegment;> segments );
+ &Path; create( Path parentPath, String subpath );
+
+ &PathSegment; createSegment( String segmentName );
+ &PathSegment; createSegment( String segmentName, &TextDecoder; decoder );
+ &PathSegment; createSegment( String segmentName, int index );
+ &PathSegment; createSegment( &Name; segmentName );
+ &PathSegment; createSegment( &Name; segmentName, int index );
+}
+</programlisting>
+ <para>
+ And finally, the &BinaryFactory; defines methods for creating &Binary; objects from a variety of binary formats,
+ as well as a method that looks for a cached &Binary; instance given the supplied secure hash:
+ </para>
+ <programlisting>
+public interface &BinaryFactory; extends &ValueFactory;<&Binary;> {
+
+ /**
+ * Create a value from the binary content given by the supplied input, the approximate length,
+ * and the SHA-1 secure hash of the content. If the secure hash is null, then a secure hash is
+ * computed from the content. If the secure hash is not null, it is assumed to be the hash for
+ * the content and may not be checked.
+ */
+ &Binary; create( &InputStream; stream, long approximateLength, byte[] secureHash )
+ throws ValueFormatException, IoException;
+ &Binary; create( &Reader; reader, long approximateLength, byte[] secureHash )
+ throws ValueFormatException, IoException;
+
+ /**
+ * Create a binary value from the given file.
+ */
+ &Binary; create( &File; file ) throws ValueFormatException, IoException;
+
+ /**
+ * Find an existing binary value given the supplied secure hash. If no such binary value exists,
+ * null is returned. This method can be used when the caller knows the secure hash (e.g., from
+ * a previously-held &Binary; object), and would like to reuse an existing binary value
+ * (if possible) rather than recreate the binary value by processing the stream contents. This is
+ * especially true when the size of the binary is quite large.
+ *
+ * @param secureHash the secure hash of the binary content, which was probably obtained from a
+ * previously-held &Binary; object; a null or empty value is allowed, but will always
+ * result in returning null
+ * @return the existing Binary value that has the same secure hash, or null if there is no
+ * such value available at this time
+ */
+ &Binary; find( byte[] secureHash );
+}
+ </programlisting>
+ <para>
+ JBoss DNA provides efficient implementations of all of these interfaces: the &ValueFactory; interfaces and subinterfaces;
+ the &Path;, &PathSegment;, &Name;, &Binary;, &DateTime;, and &Reference; interfaces; and the &ValueFactories; interface
+ return by the &ExecutionContext;. In fact, some of these interfaces have multiple implementations that are optimized for
+ specific but frequently-occurring conditions.
+ </para>
</sect1>
- <sect1 id="graph-encoders">
- <title>Encoders and decoders</title>
+ <sect1 id="graph-readable">
+ <title>Readable, TextEncoder, and TextDecoder</title>
<para>
- String representations of &Name; and &Path; objects mentioned earlier that names are qualified with a namespace, and namespaces are identified by URIs.
- Also, the local part of the names can contain any character. So what do the string representation of names look like?
+ As shown above, the &Name;, &PathSegment;, &Path;, and &Property; interfaces all extend the &Readable; interface,
+ which defines a number of <code>getString(...)</code> methods that can produce a (readable) string representation of
+ of that object. Recall that all of these objects contain names with namespace URIs and local names (consisting of any
+ characters), and so obtaining a readable string representation will require converting the URIs to prefixes,
+ escaping certain characters in the local names, and formatting the prefix and escaped local name appropriately.
+ The different <code>getString(...)</code> methods of the &Readable; interface accept various combinations
+ of &NamespaceRegistry; and &TextEncoder; parameters:
</para>
+ <programlisting>
+@Immutable
+public interface Readable {
+
+ /**
+ * Get the string form of the object. A default encoder is used to encode characters.
+ * @return the encoded string
+ */
+ public String getString();
+
+ /**
+ * Get the encoded string form of the object, using the supplied encoder to encode characters.
+ * @param encoder the encoder to use, or null if the default encoder should be used
+ * @return the encoded string
+ */
+ public String getString( &TextEncoder; encoder );
+
+ /**
+ * Get the string form of the object, using the supplied namespace registry to convert any
+ * namespace URIs to prefixes. A default encoder is used to encode characters.
+ * @param namespaceRegistry the namespace registry that should be used to obtain the prefix
+ * for any namespace URIs
+ * @return the encoded string
+ * @throws IllegalArgumentException if the namespace registry is null
+ */
+ public String getString( &NamespaceRegistry; namespaceRegistry );
+
+ /**
+ * Get the encoded string form of the object, using the supplied namespace registry to convert
+ * the any namespace URIs to prefixes.
+ * @param namespaceRegistry the namespace registry that should be used to obtain the prefix for
+ * the namespace URIs
+ * @param encoder the encoder to use, or null if the default encoder should be used
+ * @return the encoded string
+ * @throws IllegalArgumentException if the namespace registry is null
+ */
+ public String getString( &NamespaceRegistry; namespaceRegistry,
+ &TextEncoder; encoder );
+
+ /**
+ * Get the encoded string form of the object, using the supplied namespace registry to convert
+ * the names' namespace URIs to prefixes and the supplied encoder to encode characters, and using
+ * the second delimiter to encode (or convert) the delimiter used between the namespace prefix
+ * and the local part of any names.
+ * @param namespaceRegistry the namespace registry that should be used to obtain the prefix
+ * for the namespace URIs in the names
+ * @param encoder the encoder to use for encoding the local part and namespace prefix of any names,
+ * or null if the default encoder should be used
+ * @param delimiterEncoder the encoder to use for encoding the delimiter between the local part
+ * and namespace prefix of any names, or null if the standard delimiter should be used
+ * @return the encoded string
+ */
+ public String getString( &NamespaceRegistry; namespaceRegistry,
+ &TextEncoder; encoder, &TextEncoder; delimiterEncoder );
+}
+ </programlisting>
<para>
- The key is understanding that the namespaces will
+ We've seen the &NamespaceRegistry; in the <link linkend="execution-context">previous chapter</link>,
+ but we've haven't yet talked about the &TextEncoder; interface. A &TextEncoder; merely does what
+ you'd expect: it encodes the characters in a string using some implementation-specific algorithm.
+ JBoss DNA provides a number of &TextEncoder; implementations, including:
+ <itemizedlist>
+ <listitem>
+ <para>The &Jsr283Encoder; escapes characters that are not allowed in JCR names,
+ per the <ulink url="&JSR283;">JSR-283</ulink> specification. Specifically,
+ these are the '*', '/', ':', '[', ']', and '|' characters, which are escaped by replacing
+ them with the Unicode characters U+F02A, U+F02F, U+F03A, U+F05B, U+F05D, and U+F07C, respectively.
+ </para>
+ </listitem>
+ <listitem>
+ <para>The &NoOpEncoder; does no conversion.
+ </para>
+ </listitem>
+ <listitem>
+ <para>The &UrlEncoder; converts text to be used within the different parts of a URL, as defined by Section 2.3 of
+ <ulink url="http://www.ietf.org/rfc/rfc2396.txt">RFC 2396</ulink>. Note that this class does not
+ encode a complete URL (since <code>java.net.URLEncoder</code> and <code>java.net.URLDecoder</code>
+ should be used for such purposes).
+ </para>
+ </listitem>
+ <listitem>
+ <para>The &XmlNameEncoder; converts any UTF-16 unicode character that is not a valid XML name character according to the
+ <ulink url="http://www.w3.org/TR/REC-xml/#sec-common-syn">World Wide Web Consortium (W3C) Extensible Markup Language (XML) 1.0
+ (Fourth Edition) Recommendation</ulink>, escaping such characters as <code>_xHHHH_</code>, where <code>HHHH</code> stands for the
+ four-digit hexadecimal UTF-16 unicode value for the character in the most significant bit first order.
+ For example, the name "Customer_ID" is encoded as "Customer_x0020_ID".
+ </para>
+ </listitem>
+ <listitem>
+ <para>The &XmlValueEncoder; escapes characters that are not allowed in XML values. Specifically,
+ these are the '&', '<', '>', '"', and ''', which are all escaped to
+ "&amp;", '&lt;', '&gt;', '&quot;', and '&#039;'.
+ </para>
+ </listitem>
+ </itemizedlist>
+ All of these classes also implement the &TextDecoder; interface, which defines a method that
+ <emphasis>decodes</emphasis> an encoded string using the opposite transformation.
</para>
-
-
+ <para>
+ Of course, you can provide alternative implementations, and supply them to the appropriate <code>getString(...)</code> methods
+ as required.
+ </para>
</sect1>
<sect1 id="graph-api">
<title>Graph API</title>
Modified: trunk/docs/reference/src/main/docbook/en-US/custom.dtd
===================================================================
--- trunk/docs/reference/src/main/docbook/en-US/custom.dtd 2009-06-09 03:30:34 UTC (rev 1002)
+++ trunk/docs/reference/src/main/docbook/en-US/custom.dtd 2009-06-09 03:30:56 UTC (rev 1003)
@@ -28,7 +28,7 @@
<!ENTITY GettingStarted "<ulink url='&DocHome;docs/&versionNumber;/manuals/gettingstarted/html/index.html'>Getting Started</ulink>">
<!ENTITY ReferenceGuide "<ulink url='&DocHome;docs/&versionNumber;/manuals/reference/html/index.html'>Getting Started</ulink>">
-<!ENTITY CND "<ulink url='http://jackrabbit.apache.org/node-type-notation.html'>Compact Node Definition</ulink>">
+<!ENTITY CND "<ulink url='http://jackrabbit.apache.org/node-type-notation.html'>Compact Node Definition</ulink>">
<!-- Types in JRE -->
@@ -37,10 +37,11 @@
<!ENTITY URL "<ulink url='&Java;java/net/URL.html'><classname>URL</classname></ulink>">
<!ENTITY URI "<ulink url='&Java;java/net/URL.html'><classname>URI</classname></ulink>">
<!ENTITY InputStream "<ulink url='&Java;java/io/InputStream.html'><interface>InputStream</interface></ulink>">
+<!ENTITY Reader "<ulink url='&Java;java/io/Reader.html'><interface>Reader</interface></ulink>">
<!ENTITY IOException "<ulink url='&Java;java/io/IOException.html'><classname>IOException</classname></ulink>">
<!ENTITY ClassLoader "<ulink url='&Java;java/lang/ClassLoader.html'><interface>ClassLoader</interface></ulink>">
<!ENTITY AccessControlContext "<ulink url='&Java;java/security/AccessController.html'><classname>AccessControlContext</classname></ulink>">
-<!ENTITY PrivilegedAction "<ulink url='&Java;java/security/PrivilegedAction.html'><classname>PrivilegedAction</classname></ulink>">
+<!ENTITY PrivilegedAction "<ulink url='&Java;java/security/PrivilegedAction.html'><classname>PrivilegedAction</classname></ulink>">
<!ENTITY LoginContext "<ulink url='&Java;javax/security/auth/login/LoginContext.html'><classname>LoginContext</classname></ulink>">
<!ENTITY Subject "<ulink url='&Java;javax/security/auth/Subject.html'><classname>Subject</classname></ulink>">
<!ENTITY CallbackHandler "<ulink url='&Java;javax/security/auth/callback/CallbackHandler.html'><interface>CallbackHandler</interface></ulink>">
@@ -48,7 +49,13 @@
<!ENTITY TimeUnit "<ulink url='&Java;java/util/concurrent/TimeUnit.html'><interface>TimeUnit</interface></ulink>">
<!ENTITY UUID "<ulink url='&Java;java/util/UUID.html'><classname>UUID</classname></ulink>">
<!ENTITY DataSource "<ulink url='&Java;javax/sql/DataSource.html'><classname>DataSource</classname></ulink>">
-<!ENTITY HttpServletRequest "<ulink url='&Java;javax/servlet/http/HttpServletRequest.html'><classname>HttpServletRequest</classname></ulink>">
+<!ENTITY HttpServletRequest "<ulink url='&Java;javax/servlet/http/HttpServletRequest.html'><classname>HttpServletRequest</classname></ulink>">
+<!ENTITY Serializable "<ulink url='&Java;java/io/Serializable.html'><interface>Serializable</interface></ulink>">
+<!ENTITY Iterator "<ulink url='&Java;java/util/Iterator.html'><interface>Iterator</interface></ulink>">
+<!ENTITY Iteratable "<ulink url='&Java;java/util/Iteratable.html'><interface>Iteratable</interface></ulink>">
+<!ENTITY BigDecimal "<ulink url='&Java;java/math/BigDecimal.html'><classname>BigDecimal</classname></ulink>">
+<!ENTITY Calendar "<ulink url='&Java;java/util/Calendar.html'><classname>Calendar</classname></ulink>">
+<!ENTITY Date "<ulink url='&Java;java/util/Date.html'><classname>Date</classname></ulink>">
<!-- Types in JCR API -->
@@ -57,14 +64,21 @@
<!ENTITY Session "<interface>Session</interface>">
<!ENTITY Credentials "<interface>Credentials</interface>">
<!ENTITY SimpleCredentials "<interface>SimpleCredentials</interface>">
-<!ENTITY LoginException "<interface>LoginException</interface>">
-<!ENTITY AccessDeniedException "<interface>AccessDeniedException</interface>">
+<!ENTITY LoginException "<interface>LoginException</interface>">
+<!ENTITY AccessDeniedException "<interface>AccessDeniedException</interface>">
<!-- Types in dna-common -->
<!ENTITY Logger "<ulink url='&API;common/util/Logger.html'><interface>Logger</interface></ulink>">
<!ENTITY ClassLoaderFactory "<ulink url='&API;common/component/ClassLoaderFactory.html'><interface>ClassLoaderFactory</interface></ulink>">
<!ENTITY StandardClassLoaderFactory "<ulink url='&API;common/component/StandardClassLoaderFactory.html'><classname>StandardClassLoaderFactory</classname></ulink>">
+<!ENTITY TextEncoder "<ulink url='&API;common/text/TextEncoder.html'><interface>TextEncoder</interface></ulink>">
+<!ENTITY TextDecoder "<ulink url='&API;common/text/TextDecoder.html'><interface>TextDecoder</interface></ulink>">
+<!ENTITY Jsr283Encoder "<ulink url='&API;common/text/Jsr283Encoder.html'><classname>Jsr283Encoder</classname></ulink>">
+<!ENTITY NoOpEncoder "<ulink url='&API;common/text/Jsr283Encoder.html'><classname>NoOpEncoder</classname></ulink>">
+<!ENTITY UrlEncoder "<ulink url='&API;common/text/Jsr283Encoder.html'><classname>UrlEncoder</classname></ulink>">
+<!ENTITY XmlNameEncoder "<ulink url='&API;common/text/Jsr283Encoder.html'><classname>XmlNameEncoder</classname></ulink>">
+<!ENTITY XmlValueEncoder "<ulink url='&API;common/text/XmlValueEncoder.html'><classname>XmlValueEncoder</classname></ulink>">
<!-- Types in dna-graph -->
@@ -78,15 +92,25 @@
<!ENTITY ExecutionContext "<ulink url='&API;graph/ExecutionContext.html'><classname>ExecutionContext</classname></ulink>">
<!ENTITY SecurityContext "<ulink url='&API;graph/SecurityContext.html'><interface>SecurityContext</interface></ulink>">
<!ENTITY JaasSecurityContext "<ulink url='&API;graph/JaasSecurityContext.html'><classname>JaasSecurityContext</classname></ulink>">
-<!ENTITY ServletSecurityContext "<ulink url='&API;graph/ServletSecurityContext.html'><interface>ServletSecurityContext</interface></ulink>">
+<!ENTITY ServletSecurityContext "<ulink url='&API;graph/ServletSecurityContext.html'><interface>ServletSecurityContext</interface></ulink>">
+<!ENTITY Readable "<ulink url='&API;graph/property/Readable.html'><interface>Readable</interface></ulink>">
<!ENTITY Name "<ulink url='&API;graph/property/Name.html'><interface>Name</interface></ulink>">
<!ENTITY Path "<ulink url='&API;graph/property/Path.html'><interface>Path</interface></ulink>">
<!ENTITY PathSegment "<ulink url='&API;graph/property/Path.Segment.html'><interface>Path.Segment</interface></ulink>">
<!ENTITY Property "<ulink url='&API;graph/property/Property.html'><interface>Property</interface></ulink>">
+<!ENTITY DateTime "<ulink url='&API;graph/property/DateTime.html'><interface>DateTime</interface></ulink>">
+<!ENTITY Binary "<ulink url='&API;graph/property/Binary.html'><interface>Binary</interface></ulink>">
+<!ENTITY ValueFactory "<ulink url='&API;graph/property/ValueFactory.html'><interface>ValueFactory</interface></ulink>">
<!ENTITY ValueFactories "<ulink url='&API;graph/property/ValueFactories.html'><interface>ValueFactories</interface></ulink>">
+<!ENTITY ValueFormatException "<ulink url='&API;graph/property/ValueFormatException.html'><classname>ValueFormatException</classname></ulink>">
+<!ENTITY DateTimeFactory "<ulink url='&API;graph/property/DateTimeFactory.html'><interface>DateTimeFactory</interface></ulink>">
+<!ENTITY BinaryFactory "<ulink url='&API;graph/property/BinaryFactory.html'><interface>BinaryFactory</interface></ulink>">
+<!ENTITY NameFactory "<ulink url='&API;graph/property/NameFactory.html'><interface>NameFactory</interface></ulink>">
+<!ENTITY PathFactory "<ulink url='&API;graph/property/PathFactory.html'><interface>PathFactory</interface></ulink>">
<!ENTITY NamespaceRegistry "<ulink url='&API;graph/property/NamespaceRegistry.html'><interface>NamespaceRegistry</interface></ulink>">
<!ENTITY Namespace "<ulink url='&API;graph/property/NamespaceRegistry.Namespaces.html'><interface>Namespace</interface></ulink>">
<!ENTITY PropertyFactory "<ulink url='&API;graph/property/PropertyFactory.html'><interface>PropertyFactory</interface></ulink>">
+<!ENTITY PropertyType "<ulink url='&API;graph/property/PropertyType.html'><classname>PropertyType</classname></ulink>">
<!ENTITY PathNotFoundException "<ulink url='&API;graph/property/PathNotFoundException.html'><classname>PathNotFoundException</classname></ulink>">
<!ENTITY RepositorySource "<ulink url='&API;graph/connector/RepositorySource.html'><interface>RepositorySource</interface></ulink>">
<!ENTITY RepositoryConnection "<ulink url='&API;graph/connector/RepositoryConnection.html'><interface>RepositoryConnection</interface></ulink>">
16 years, 6 months
DNA SVN: r1002 - in trunk/docs/reference/src/main/docbook/en-US: content/core and 1 other directory.
by dna-commits@lists.jboss.org
Author: rhauch
Date: 2009-06-08 23:30:34 -0400 (Mon, 08 Jun 2009)
New Revision: 1002
Added:
trunk/docs/reference/src/main/docbook/en-US/content/core/graph.xml
Modified:
trunk/docs/reference/src/main/docbook/en-US/master.xml
Log:
More Reference Guide content on the graph api.
Added: trunk/docs/reference/src/main/docbook/en-US/content/core/graph.xml
===================================================================
--- trunk/docs/reference/src/main/docbook/en-US/content/core/graph.xml (rev 0)
+++ trunk/docs/reference/src/main/docbook/en-US/content/core/graph.xml 2009-06-09 03:30:34 UTC (rev 1002)
@@ -0,0 +1,256 @@
+<?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="graph_api">
+ <title>Graph API</title>
+ <para>
+ One of the central concepts within JBoss DNA is that of its <emphasis>graph model</emphasis>.
+ Information is structured into a hierarchy of nodes with properties, where nodes in the hierarchy
+ are identified by their path (and/or identifier properties). Properties are identified by
+ a name that incorporates a namespace and local name, and contain one or more property values
+ consisting of normal Java strings, names, paths, URIs, booleans, longs, doubles, decimals, binary content,
+ dates, UUIDs, references to other nodes, or any other serializable object.
+ </para>
+ <para>
+ This graph model is used throughout JBoss DNA: it forms the basis for the
+ <link linkend="connector_framework">connector framework</link>, it is used by the
+ <link linkend="sequencing_framework">sequencing framework</link> for the generated output,
+ and it is what the <link linkend="jcr">JCR implementation</link> uses internally to access and operate
+ on the repository content.
+ </para>
+ <para>
+ Therefore, this chapter provides essential information that will be essential to really understanding
+ how the connectors, sequencers, and other JBoss DNA features work.
+ </para>
+ <sect1 id="graph-names">
+ <title>Names</title>
+ <para>
+ JBoss DNA uses names to identify quite a few different types of objects. As we'll soon see, each property
+ of a node is given by a name, and each segment in a path is comprised of a name. Therefore,
+ names are a very important concept.
+ </para>
+ <para>
+ JBoss DNA names consist of a local part and are qualified with a namespaces. The local part can consist of
+ any character, and the namespace is identified by a URI. Namespaces were introduced in the
+ <link linkend="execution-context">previous chapter</link> and are managed by the &ExecutionContext;'s
+ <link linkend="namespace-registry">namespace registry</link>. Namespaces help reduce the risk of
+ clashes in names that have an equivalent same local part.
+ </para>
+ <para>
+ All names are immutable, which means that once a &Name; object is created, it will never change.
+ This characteristic makes it much easier to write thread-safe code - the objects never change and therefore
+ require no locks or synchronization to guarantee atomic reads. This is a technique that is more and more
+ often found in newer languages and frameworks that simplify concurrent operations.
+ </para>
+ <para>
+ &Name; is also a interface rather than a concrete class:
+ </para>
+<programlisting>
+</programlisting>
+ <para>
+ This means that you need to use a factory to create &Name; instances.
+ </para>
+ <para>
+ The use of a factory may seem like a disadvantage and unnecessary complexity, but there actually
+ are several benefits. First, it hides the concrete implementations, which is very appealing if
+ an optimized implementation can be chosen for particular situations. It also simplifies the
+ usage, since &Name; only has a few methods. Third, it allows the factory to cache or pool instances
+ where appropriate to help conserve memory. Finally, the very same factory actually serves as
+ a <emphasis>conversion</emphasis> mechanism from other forms. We'll actually see more of this
+ later in this chapter, when we talk about other kinds of <link linkend="graph-properties">property values</link>.
+ </para>
+ <para>
+ The factory for creating &Name; objects is called &NameFactory; and is available within the &ExecutionContext;,
+ via the <code>getValueFactories()</code> method. But before we see that, let's first discuss how names are represented as strings.
+ </para>
+ <para>
+ We mentioned earlier that names are qualified with a namespace, and namespaces are identified by URIs.
+ Also, the local part of the names can contain any character. So what do the string representation of names look like?
+ </para>
+ <para>
+ The &Name; interface defines a number of <code>getString(...)</code> methods that take different parameters.
+ The <code>getString()</code> method (which is called by the <code>toString()</code> method) will simply output
+ the namespace URI followed by a ':' and an local name (encoded with a default &TextEncoder; implementation, which
+ will be discussed in the <link linkend="graph-encoders">next section</link>). If you want to use the
+ namespace prefix, then simply use one of the <code>getString(...)</code> methods that takes a &NamespaceRegistry;
+ parameter.
+ </para>
+ <para>
+ We'll see how names are used later one, but one more point to make: &Name; is both serializable and comparable,
+ and all implementations should support <code>equals(...)</code> and <code>hashCode()</code> so that &Name; can
+ be used as a key in a hash-based map.
+ </para>
+ </sect1>
+ <sect1 id="graph-paths">
+ <title>Paths</title>
+ <para>
+ Another important concept in JBoss DNA's graph model is that of a <emphasis>path</emphasis>, which provides a way
+ of locating a node within a hierarchy. JBoss DNA's &Path; object is actually comprised of an ordered sequence
+ of &PathSegment; objects:
+ </para>
+ <programlisting>
+public interface &Path; extends Comparable<Path>, Iterable<&PathSegment;>, Serializable {
+
+ /**
+ * Return the number of segments in this path.
+ * @return the number of path segments
+ */
+ public int size();
+
+ /**
+ * Return whether this path represents the root path.
+ * @return true if this path is the root path, or false otherwise
+ */
+ public boolean isRoot();
+
+ /**
+ * {@inheritDoc}
+ */
+ public Iterator<&PathSegment;> iterator();
+
+ /**
+ * Obtain a copy of the segments in this path. None of the segments are encoded.
+ * @return the array of segments as a copy
+ */
+ public &PathSegment;[] getSegmentsArray();
+
+ /**
+ * Get an unmodifiable list of the path segments.
+ * @return the unmodifiable list of path segments; never null
+ */
+ public List<&PathSegment;> getSegmentsList();
+ /**
+ * Get the last segment in this path.
+ * @return the last segment, or null if the path is empty
+ */
+ public &PathSegment; getLastSegment();
+
+ /**
+ * Get the segment at the supplied index.
+ * @param index the index
+ * @return the segment
+ * @throws IndexOutOfBoundsException if the index is out of bounds
+ */
+ public &PathSegment; getSegment( int index );
+
+ /**
+ * Return an iterator that walks the paths from the root path down to this path. This method
+ * always returns at least one path (the root returns an iterator containing itself).
+ * @return the path iterator; never null
+ */
+ public Iterator<&Path;> pathsFromRoot();
+
+ /**
+ * Return a new path consisting of the segments starting at <code>beginIndex</code> index (inclusive).
+ * This is equivalent to calling <code>path.subpath(beginIndex,path.size()-1)</code>.
+ * @param beginIndex the beginning index, inclusive.
+ * @return the specified subpath
+ * @exception IndexOutOfBoundsException if the <code>beginIndex</code> is negative or larger
+ * than the length of this <code>Path</code> object
+ */
+ public &Path; subpath( int beginIndex );
+
+ /**
+ * Return a new path consisting of the segments between the <code>beginIndex</code> index (inclusive)
+ * and the <code>endIndex</code> index (exclusive).
+ * @param beginIndex the beginning index, inclusive.
+ * @param endIndex the ending index, exclusive.
+ * @return the specified subpath
+ * @exception IndexOutOfBoundsException if the <code>beginIndex</code> is negative, or
+ * <code>endIndex</code> is larger than the length of this <code>Path</code>
+ * object, or <code>beginIndex</code> is larger than <code>endIndex</code>.
+ */
+ public &Path; subpath( int beginIndex, int endIndex );
+
+ ...
+
+} </programlisting>
+ <para>
+ where each &PathSegment; is comprised of a &Name; and <emphasis>same-name-sibling index</emphasis>:
+ </para>
+ <programlisting>
+ </programlisting>
+ <para>
+
+ </para>
+
+ </sect1>
+ <sect1 id="graph-properties">
+ <title>Properties</title>
+ <para>
+
+ </para>
+ </sect1>
+ <sect1 id="graph-value-factories">
+ <title>Values and value factories</title>
+ <para>
+
+ </para>
+ </sect1>
+ <sect1 id="graph-encoders">
+ <title>Encoders and decoders</title>
+ <para>
+ String representations of &Name; and &Path; objects mentioned earlier that names are qualified with a namespace, and namespaces are identified by URIs.
+ Also, the local part of the names can contain any character. So what do the string representation of names look like?
+ </para>
+ <para>
+ The key is understanding that the namespaces will
+ </para>
+
+
+ </sect1>
+ <sect1 id="graph-api">
+ <title>Graph API</title>
+ <para>
+
+ </para>
+ </sect1>
+ <sect1 id="graph-requests">
+ <title>Request Model</title>
+ <para>
+
+ </para>
+ </sect1>
+ <sect1>
+ <title>Summary</title>
+ <para>
+ In this chapter, we introduced JBoss DNA's <emphasis>graph model</emphasis> and showed the different
+ kinds of objects used to represent nodes, paths, names, and properties. We saw how all of these objects
+ are actually immutable, and how the low-level Graph API uses this characteristic to provide a stateless
+ and thread-safe interface for working with repository content using the <emphasis>request model</emphasis>
+ used to read, update, and change content.
+ </para>
+ <para>
+ Next, we'll dive into the <link linkend="connector_framework">connector framework</link>, which builds
+ on top of the graph model and request model, allowing JBoss DNA to access the graph content stored
+ in many different kinds of systems.
+ </para>
+ </sect1>
+</chapter>
Property changes on: trunk/docs/reference/src/main/docbook/en-US/content/core/graph.xml
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Modified: trunk/docs/reference/src/main/docbook/en-US/master.xml
===================================================================
--- trunk/docs/reference/src/main/docbook/en-US/master.xml 2009-06-08 22:42:13 UTC (rev 1001)
+++ trunk/docs/reference/src/main/docbook/en-US/master.xml 2009-06-09 03:30:34 UTC (rev 1002)
@@ -82,6 +82,7 @@
</para>
</partintro>
<xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="content/core/execution_context.xml"/>
+ <xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="content/core/graph.xml"/>
<xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="content/core/connector.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/configuration.xml"/>
16 years, 6 months
DNA SVN: r1001 - in trunk/docs/reference/src/main/docbook/en-US: content/jcr and 1 other directory.
by dna-commits@lists.jboss.org
Author: bcarothers
Date: 2009-06-08 18:42:13 -0400 (Mon, 08 Jun 2009)
New Revision: 1001
Modified:
trunk/docs/reference/src/main/docbook/en-US/content/jcr/jcr.xml
trunk/docs/reference/src/main/docbook/en-US/custom.dtd
Log:
Added first cut of the new JCR chapter
Modified: trunk/docs/reference/src/main/docbook/en-US/content/jcr/jcr.xml
===================================================================
--- trunk/docs/reference/src/main/docbook/en-US/content/jcr/jcr.xml 2009-06-08 22:41:58 UTC (rev 1000)
+++ trunk/docs/reference/src/main/docbook/en-US/content/jcr/jcr.xml 2009-06-08 22:42:13 UTC (rev 1001)
@@ -40,88 +40,222 @@
</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>Add reference to how to use JcrConfiguration to obtain a JcrEngine.
</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>Once you have obtained a reference to the JCR &Repository;, you can create a JCR session using one of its
+ <code>login(...)</code> methods. The <ulink url="&JSR170;">JSR-170</ulink> specification provides four login methods.
</para>
+ <para>The first method allows the implementation to choose its own security context to create a session in the default workspace
+ for the repository. The JBoss DNA JCR implementation uses the security context from the current &AccessControlContext;. This implies
+ that this method will throw a &LoginException; if it is not executed as a &PrivilegedAction;. Here is one example of how this might
+ work:
<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.
+Subject subject = ...;
+&Session; session = (&Session;) Subject.doAsPrivileged(subject, new PrivilegedExceptionAction<&Session;>() {
+ public Session run() throws Exception {
+ return repository.login();
+ }}, AccessController.getContext());
+</programlisting>
+ This approach will yield a session with the same user name and roles as <code>subject</code>. There is a comparable
+ version of <code>login(...)</code> that allows the workspace to be specified by name.
+ <programlisting>
+Subject subject = ...;
+final &String; workspaceName = ...;
+&Session; session = (&Session;) Subject.doAsPrivileged(subject, new PrivilegedExceptionAction<&Session;>() {
+ public Session run() throws Exception {
+ return repository.login(workspaceName);
+ }}, AccessController.getContext());
+</programlisting>
</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>It is also possible to supply the &Credentials; directly as part of the login process, although JBoss DNA imposes
+ some requirements on what types of &Credentials; may be supplied. The simplest way is to provide a &SimpleCredentials; object.
+ These credentials will be validated against the JAAS realm named "dna-jcr" unless another realm name is provided as an option
+ during the JCR repository configuration. For example:
+ <programlisting>
+&String; userName = ...;
+char[] password = ...;
+&Session; session = repository.login(new &SimpleCredentials;(userName, password));
+</programlisting>
+ The credentials-based <code>login(...)</code> method also supports an optional workspace name.
+ <programlisting>
+&String; userName = ...;
+char[] password = ...;
+&String; workspaceName = ...;
+&Session; session = repository.login(new &SimpleCredentials;(userName, password), workspaceName);
+</programlisting>
+ If a &LoginContext; is available for the user, that can be used as part of the credentials to authenticate the user with
+ JBoss DNA instead. This snippet uses an anonymous class to provide the login context, but any class with a <code>&LoginContext; getLoginContext()</code>
+ method can be used as well.
+ <programlisting>
+final &LoginContext; loginContext = ...;
+&Session; session = repository.login(new &Credentials;() {
+ &LoginContext; loginContext getLoginContext() {
+ return loginContext;
+ }
+}, workspaceName);
+</programlisting>
+ Servlet-based applications may wish to reuse the authentication information from &HttpServletRequest; instead. Please note that
+ the example below assumes that the servlet has a security constraint that prevents unauthenticated access.
+ <programlisting>
+&HttpServletRequest; request = ...;
+&ServletSecurityContext; securityContext = new ServletSecurityContext(request);
+&Session; session = repository.login(new &SecurityContextCredentials;(securityContext);
+</programlisting>
+ Once the &Session; is obtained, the repository content can be access and modified like any other JCR repository. No roles are required to connect
+ to any workspace at this time. Restrictions on workspace connections will likely be added to JBoss DNA in the near future. The roles from the JAAS
+ information or the &HttpServletRequest; are used to control read and write access to the repository. Please see the <ulink linkend="dna_jcr_security">JCR Security section</ulink>
+ for more details on how access is controlled.
</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>
<sect1>
- <title>JCR Support</title>
+ <title>JCR Specification 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>
+ The JBoss DNA JCR implementation will not be JCR-compliant prior to the 1.0 release. Additionally, the JCR
+ specification allows some latitude to implementors for some implementation details. The sections below
+ clarify JBoss DNA's current and planned behavior.
+ </para>
<sect2>
+ <title>L1 and L2 Features</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. Some of the L2 features such as workspace cloning and updating, corresponding nodes,
+ and referential integrity for <code>REFERENCE</code> properties are also not yet implemented. As the current implementation does provide 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>
+ </sect2>
+ <sect2>
<title>Optional Features</title>
+ <para>
+ JBoss DNA does not currently support any of the optional JCR features. Currently, the observation optional feature is planned to be complete prior
+ to the 1.0 release. The locking optional feature <emphasis>may</emphasis> be implemented in this timeframe as well.
+ <note>
+ <para>The JCR-SQL optional feature is not planned to be implemented as it has been dropped from the <ulink url="&JSR283;">JSR-283</ulink> specification.
+ </para>
+ </note>
+ </para>
</sect2>
- <sect2>
+ <sect2 id="dna_jcr_security">
<title>JCR Security</title>
+ <para>
+ Although the <ulink url="&JSR170;">JSR-170</ulink> specification requires implementation of the <code>Session.checkPermission(String, String)</code> method,
+ it allows implementors to choose the granularity of their access controls. JBoss DNA supports coarse-grained, role-based access control at the repository
+ and workspace level.
+ </para>
+ <para>
+ JBoss DNA currently defines two permissions: <code>READONLY</code> and <code>READWRITE</code>. If the &Credentials; passed into <code>Session.login(...)</code>
+ (or the &Subject; from the &AccessControlContext;, if one of the no-credential <code>login</code> methods were used) has either role, the session will have
+ the corresponding access to all workspaces within the repository. That is, having the <code>READONLY</code> role implies that <code>Session.checkPermission(path, "read")</code>
+ will not throw an &AccessDeniedException; for any value of <code>path</code> in any workspace in the repository. Similarly, having the <code>READWRITE</code>
+ role implies that <code>Session.checkPermission(path, actions)</code> will not throw an &AccessDeniedException; for any values of <code>path</code> and
+ <code>actions</code>.
+
+ <note>
+ <para> In this release, JBoss DNA does not properly check for actions or even check that the <code>actions</code> parameter passed into
+ <code>Session.checkPermission(...)</code> is even valid. This will be corrected prior to the 1.0 release.
+ </para>
+ </note>
+
+ It is also possible to grant access only to one or more named workspaces. For a workspace named "staging", this can be done by assigning a role named
+ <code>READONLY.staging</code>. Appending <code>"." + workspaceName</code> to the <code>READWRITE</code> role works as well.
+ </para>
+ <para>
+ As a final note, the JBoss DNA JCR implementation will likely have additional security roles added prior to the 1.0 release. A <code>CONNECT</code> role
+ is already being used by the DNA REST Server to control whether users have access to the repository through that means.
+ </para>
</sect2>
<sect2>
- <title>Node Type Registration</title>
+ <title>Built-In Node Types</title>
+ <para>Add description of the DNA built-in node types.</para>
</sect2>
+ <sect2>
+ <title>Custom Node Type Registration</title>
+ <para>
+ Although the <ulink url="&JSR170;">JSR-170</ulink> specification does not require support for registration of custom types, JBoss DNA supports this extremely
+ useful feature. Custom node types can be added at startup, as noted above or at runtime through a DNA-specific interface. JBoss DNA supports defining node
+ types either through a <ulink url="&JSR283;">JSR-283</ulink>-like template approach or through the use of &CND; (CND) files.
+ Both type registration mechanisms are supported equally within JBoss DNA, although the CND approach for defining node types is recommended.
+ <note>
+ <para>JBoss DNA also supports defining custom node types to load at startup. This is discussed in more detail
+ in the next chapter.
+ </para>
+ </note>
+ </para>
+ <para>
+ Although the JSR-283 specification is not yet final, it does provide a useful means of programatically defining JCR node types. JBoss DNA supports a comparable
+ node type definition API that implements the functionality from the specification, albeit with classes in an <code>org.jboss.dna.jcr</code> package. The intent
+ is to deprecate these classes and replace their usage with the JSR-283 equivalents after JBoss DNA fully supports in the JSR-283 specification in a future release.
+ Node types can be defined like so:
+ <programlisting>
+&Session; session = ... ;
+NodeTypeManager nodeTypeManager = session.getWorkspace().getNodeTypeManager();
+
+// Declare a mixin node type named "searchable" (with no namespace)
+NodeTypeTemplate nodeType = nodeTypeManager.createNodeTypeTemplate();
+nodeType.setName("searchable");
+nodeType.setMixin(true);
+nodeType.getNodeDefinitionTemplates().add(childNode);
+
+// Add a mandatory child named "source" with a required primary type of "nt:file"
+NodeDefinitionTemplate childNode = nodeTypeManager.createNodeDefinitionTemplate();
+childNode.setName("source");
+childNode.setMandatory(true);
+childNode.setRequiredPrimaryTypeNames(new String[] { "nt:file" });
+childNode.setDefaultPrimaryType("nt:file");
+
+// Add a multi-valued STRING property named "keywords"
+PropertyDefinitionTemplate property = nodeTypeManager.createPropertyDefinitionTemplate();
+property.setName("keywords");
+property.setMultiple(true);
+property.setRequiredType(PropertyType.STRING);
+nodeType.getPropertyDefinitionTemplates().add(property);
+
+// Register the custom node type
+nodeTypeManager.registerNodeType(nodeType);
+</programlisting>
+ Residual properties and child node definitions can also be defined simply by not calling <code>setName</code> on
+ the template.
+ </para>
+ <para>
+ Custom node types can be defined more succinctly through the &CND; file format. In fact, this is how JBoss
+ DNA defines its built-in node types. An example CND file that declares the same node type as above would be:
+<programlisting>
+[searchable] mixin
+- keywords (string) multiple
++ source (nt:file) = nt:file
+</programlisting>
+ This definition could then be registered with the following code snippet.
+<programlisting>
+
+String pathToCndFileInClassLoader = ...;
+CndNodeTypeSource nodeTypeSource = new CndNodeTypeSource(pathToCndFileInClassLoader);
+
+for (Problem problem : nodeTypeSource.getProblems()) {
+ System.err.println(problem);
+}
+if (!nodeTypeSource.isValid()) {
+ throw new IllegalStateException("Problems loading node types");
+}
+
+&Session; session = ... ;
+NodeTypeManager nodeTypeManager = session.getWorkspace().getNodeTypeManager();
+nodeTypeManager.registerNodeTypes(nodeTypeSource);
+</programlisting>
+ <note>
+ <para> JBoss DNA does not yet support a simple means on unregistering types at this time, so be careful before registering types outside of a
+ sandboxed environment.
+ </para>
+ </note>
+
+ </para>
+ </sect2>
</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,
+ In this chapter, we covered how to use JCR with JBoss DNA and learned about how it implements the JCR specification. 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>
Modified: trunk/docs/reference/src/main/docbook/en-US/custom.dtd
===================================================================
--- trunk/docs/reference/src/main/docbook/en-US/custom.dtd 2009-06-08 22:41:58 UTC (rev 1000)
+++ trunk/docs/reference/src/main/docbook/en-US/custom.dtd 2009-06-08 22:42:13 UTC (rev 1001)
@@ -28,6 +28,8 @@
<!ENTITY GettingStarted "<ulink url='&DocHome;docs/&versionNumber;/manuals/gettingstarted/html/index.html'>Getting Started</ulink>">
<!ENTITY ReferenceGuide "<ulink url='&DocHome;docs/&versionNumber;/manuals/reference/html/index.html'>Getting Started</ulink>">
+<!ENTITY CND "<ulink url='http://jackrabbit.apache.org/node-type-notation.html'>Compact Node Definition</ulink>">
+
<!-- Types in JRE -->
<!ENTITY String "<ulink url='&Java;java/lang/String.html'><interface>String</interface></ulink>">
@@ -38,6 +40,7 @@
<!ENTITY IOException "<ulink url='&Java;java/io/IOException.html'><classname>IOException</classname></ulink>">
<!ENTITY ClassLoader "<ulink url='&Java;java/lang/ClassLoader.html'><interface>ClassLoader</interface></ulink>">
<!ENTITY AccessControlContext "<ulink url='&Java;java/security/AccessController.html'><classname>AccessControlContext</classname></ulink>">
+<!ENTITY PrivilegedAction "<ulink url='&Java;java/security/PrivilegedAction.html'><classname>PrivilegedAction</classname></ulink>">
<!ENTITY LoginContext "<ulink url='&Java;javax/security/auth/login/LoginContext.html'><classname>LoginContext</classname></ulink>">
<!ENTITY Subject "<ulink url='&Java;javax/security/auth/Subject.html'><classname>Subject</classname></ulink>">
<!ENTITY CallbackHandler "<ulink url='&Java;javax/security/auth/callback/CallbackHandler.html'><interface>CallbackHandler</interface></ulink>">
@@ -45,6 +48,7 @@
<!ENTITY TimeUnit "<ulink url='&Java;java/util/concurrent/TimeUnit.html'><interface>TimeUnit</interface></ulink>">
<!ENTITY UUID "<ulink url='&Java;java/util/UUID.html'><classname>UUID</classname></ulink>">
<!ENTITY DataSource "<ulink url='&Java;javax/sql/DataSource.html'><classname>DataSource</classname></ulink>">
+<!ENTITY HttpServletRequest "<ulink url='&Java;javax/servlet/http/HttpServletRequest.html'><classname>HttpServletRequest</classname></ulink>">
<!-- Types in JCR API -->
@@ -53,6 +57,8 @@
<!ENTITY Session "<interface>Session</interface>">
<!ENTITY Credentials "<interface>Credentials</interface>">
<!ENTITY SimpleCredentials "<interface>SimpleCredentials</interface>">
+<!ENTITY LoginException "<interface>LoginException</interface>">
+<!ENTITY AccessDeniedException "<interface>AccessDeniedException</interface>">
<!-- Types in dna-common -->
@@ -72,6 +78,7 @@
<!ENTITY ExecutionContext "<ulink url='&API;graph/ExecutionContext.html'><classname>ExecutionContext</classname></ulink>">
<!ENTITY SecurityContext "<ulink url='&API;graph/SecurityContext.html'><interface>SecurityContext</interface></ulink>">
<!ENTITY JaasSecurityContext "<ulink url='&API;graph/JaasSecurityContext.html'><classname>JaasSecurityContext</classname></ulink>">
+<!ENTITY ServletSecurityContext "<ulink url='&API;graph/ServletSecurityContext.html'><interface>ServletSecurityContext</interface></ulink>">
<!ENTITY Name "<ulink url='&API;graph/property/Name.html'><interface>Name</interface></ulink>">
<!ENTITY Path "<ulink url='&API;graph/property/Path.html'><interface>Path</interface></ulink>">
<!ENTITY PathSegment "<ulink url='&API;graph/property/Path.Segment.html'><interface>Path.Segment</interface></ulink>">
16 years, 6 months
DNA SVN: r1000 - trunk/docs/gettingstarted/src/main/docbook/en-US/content.
by dna-commits@lists.jboss.org
Author: bcarothers
Date: 2009-06-08 18:41:58 -0400 (Mon, 08 Jun 2009)
New Revision: 1000
Modified:
trunk/docs/gettingstarted/src/main/docbook/en-US/content/use_cases.xml
Log:
Redacted a reference to SOA registries
Modified: trunk/docs/gettingstarted/src/main/docbook/en-US/content/use_cases.xml
===================================================================
--- trunk/docs/gettingstarted/src/main/docbook/en-US/content/use_cases.xml 2009-06-08 19:58:59 UTC (rev 999)
+++ trunk/docs/gettingstarted/src/main/docbook/en-US/content/use_cases.xml 2009-06-08 22:41:58 UTC (rev 1000)
@@ -68,14 +68,6 @@
resource declarations, documentation, presentations, ontologies, etc.
</para>
<para>
- It is also desirable that the service registry can provide accurate and up-to-date information about where the services are deployed
- and their current health. The best way to do this would be to access this information directly from the other components of the SOA,
- such as the Enterprise Service Bus and the management/monitoring system. JBoss DNA connectors might allow the service registry
- to access an ESB to discover which services are deployed, which are running, and automatically annotate the services in the registry
- with this dynamic information. In fact, the service registry could cache this information to provide a safe and low-impact mechanism
- for obtaining this information to other applications without having each application hit the ESB.
- </para>
- <para>
Integration with a management system can be done in a similar manner. A JBoss DNA connector could access the management system
to discover the servers and enable auto-discovery of the services, and "tag" the services deployments with the lifecycle phase
(dev, test, production, etc.).
16 years, 6 months
DNA SVN: r999 - in trunk/docs/reference/src/main/docbook/en-US: content and 2 other directories.
by dna-commits@lists.jboss.org
Author: rhauch
Date: 2009-06-08 15:58:59 -0400 (Mon, 08 Jun 2009)
New Revision: 999
Added:
trunk/docs/reference/src/main/docbook/en-US/content/core/execution_context.xml
trunk/docs/reference/src/main/docbook/en-US/content/mime_type_detectors/
trunk/docs/reference/src/main/docbook/en-US/content/mime_type_detectors/aperture.xml
trunk/docs/reference/src/main/docbook/en-US/content/mime_type_detectors/custom_detectors.xml
Removed:
trunk/docs/reference/src/main/docbook/en-US/content/core/classloaders.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
Modified:
trunk/docs/reference/src/main/docbook/en-US/custom.dtd
trunk/docs/reference/src/main/docbook/en-US/master.xml
Log:
Added more detail to the Execution Context chapter, and broke out the MIME type detector content into this chapter and a new document part focused on MIME type detectors.
Deleted: trunk/docs/reference/src/main/docbook/en-US/content/core/classloaders.xml
===================================================================
--- trunk/docs/reference/src/main/docbook/en-US/content/core/classloaders.xml 2009-06-08 16:12:59 UTC (rev 998)
+++ trunk/docs/reference/src/main/docbook/en-US/content/core/classloaders.xml 2009-06-08 19:58:59 UTC (rev 999)
@@ -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/core/environment.xml
===================================================================
--- trunk/docs/reference/src/main/docbook/en-US/content/core/environment.xml 2009-06-08 16:12:59 UTC (rev 998)
+++ trunk/docs/reference/src/main/docbook/en-US/content/core/environment.xml 2009-06-08 19:58:59 UTC (rev 999)
@@ -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>
-
Added: trunk/docs/reference/src/main/docbook/en-US/content/core/execution_context.xml
===================================================================
--- trunk/docs/reference/src/main/docbook/en-US/content/core/execution_context.xml (rev 0)
+++ trunk/docs/reference/src/main/docbook/en-US/content/core/execution_context.xml 2009-06-08 19:58:59 UTC (rev 999)
@@ -0,0 +1,578 @@
+<?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="execution_context">
+ <title>Execution Context</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.
+ </para>
+ <para>
+ The approach that JBoss DNA takes is simple: a simple POJO that represents the everything about the environment
+ in which components operate. Called &ExecutionContext;, it contains references to most of the essential
+ facilities, including: security (authentication and authorization); namespace registry; name factories; factories
+ for properties and property values; logging; and access to class loaders (given a classpath).
+ Most of the JBoss DNA components require an &ExecutionContext; and thus have access to all these facilities.
+ </para>
+ <para>
+ The &ExecutionContext; is a concrete class that is instantiated with the no-argument constructor:
+ </para>
+ <programlisting>
+public class &ExecutionContext; implements &ClassLoaderFactory; {
+
+ /**
+ * Create an instance of an execution context, with default implementations for all components.
+ */
+ public &ExecutionContext;() { ... }
+
+ /**
+ * 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>
+ 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>
+ As mentioned above, the starting point is to create a default execution context, which will have all the default components:
+ </para>
+<programlisting>
+&ExecutionContext; context = new &ExecutionContext;();
+</programlisting>
+ <para>
+ Once you have this top-level context, you can start creating <emphasis>subcontexts</emphasis> with different components,
+ and different security contexts. (Of course, you can create a subcontext from any &ExceutionContext; instance.)
+ To create a subcontext, simply use one of the <code>with(...)</code> methods on the parent context. We'll show examples
+ later on in this chapter.
+ </para>
+ <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.
+ A &SecurityContext; represents the context of an authenticated user, and is defined as an interface:
+ </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>
+ Every &ExecutionContext; has a &SecurityContext; instance, though the top-level (default) execution context does not represent
+ an authenticated user. But you can create a subcontext for a user authenticated via JAAS:
+ </para>
+<programlisting>
+&ExecutionContext; context = ...
+String username = ...
+char[] password = ...
+String jaasRealm = ...
+&SecurityContext; securityContext = new JaasSecurityContext(jaasRealm, username, password);
+&ExecutionContext; userContext = context.with(securityContext);
+</programlisting>
+ <para>
+ In the case of JAAS, you might not have the password but would rather prompt the user. In that case, simply create
+ a subcontext with a different security context:
+ </para>
+<programlisting>
+&ExecutionContext; context = ...
+String jaasRealm = ...
+&CallbackHandler; callbackHandler = ...
+&ExecutionContext; userContext = context.with(new JaasSecurityContext(jaasRealm, callbackHandler);
+</programlisting>
+ <para>
+ Of course if your application has a non-JAAS authentication and authorization system, you can simply provide your own implementation
+ of &SecurityContext;:
+ </para>
+<programlisting>
+&ExecutionContext; context = ...
+&SecurityContext; mySecurityContext = ...
+&ExecutionContext; myAppContext = context.with(mySecurityContext);
+</programlisting>
+ <para>
+ These &ExecutionContext; then represent the authenticated user in any component that uses the context.
+ </para>
+ <sect2 id="jaas_security">
+ <title>JAAS</title>
+ <para>
+ One of the &SecurityContext; implementations provided by JBoss DNA 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.
+ </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);
+&ExecutionContext; servletContext = context.with(securityContext);
+</programlisting>
+ <para>
+ We'll see later in the <link linkend="jcr">JCR chapter</link> how this can be use to obtain a JCR &Session; for
+ the authenticated user.
+ </para>
+ </sect2>
+ </sect1>
+ <sect1 id="namespace_registry">
+ <title>Namespace Registry</title>
+ <para>
+ As we saw earlier, every &ExecutionContext; has a registry of namespaces. Namespaces are used throughout the graph API
+ (as we'll see soon), and the prefix associated with each namespace makes for more readable string representations.
+ The namespace registry tracks all of these namespaces and prefixes, and allows registrations to be added, modified, or
+ removed. The interface for the &NamespaceRegistry; shows how these operations are done:
+ </para>
+ <programlisting>
+public interface &NamespaceRegistry; {
+
+ /**
+ * Return the namespace URI that is currently mapped to the empty prefix.
+ * @return the namespace URI that represents the default namespace,
+ * or null if there is no default namespace
+ */
+ String getDefaultNamespaceUri();
+
+ /**
+ * Get the namespace URI for the supplied prefix.
+ * @param prefix the namespace prefix
+ * @return the namespace URI for the supplied prefix, or null if there is no
+ * namespace currently registered to use that prefix
+ * @throws IllegalArgumentException if the prefix is null
+ */
+ String getNamespaceForPrefix( String prefix );
+
+ /**
+ * Return the prefix used for the supplied namespace URI.
+ * @param namespaceUri the namespace URI
+ * @param generateIfMissing true if the namespace URI has not already been registered and the
+ * method should auto-register the namespace with a generated prefix, or false if the
+ * method should never auto-register the namespace
+ * @return the prefix currently being used for the namespace, or "null" if the namespace has
+ * not been registered and "generateIfMissing" is "false"
+ * @throws IllegalArgumentException if the namespace URI is null
+ * @see #isRegisteredNamespaceUri(String)
+ */
+ String getPrefixForNamespaceUri( String namespaceUri, boolean generateIfMissing );
+
+ /**
+ * Return whether there is a registered prefix for the supplied namespace URI.
+ * @param namespaceUri the namespace URI
+ * @return true if the supplied namespace has been registered with a prefix, or false otherwise
+ * @throws IllegalArgumentException if the namespace URI is null
+ */
+ boolean isRegisteredNamespaceUri( String namespaceUri );
+
+ /**
+ * Register a new namespace using the supplied prefix, returning the namespace URI previously
+ * registered under that prefix.
+ * @param prefix the prefix for the namespace, or null if a namesapce prefix should be generated
+ * automatically
+ * @param namespaceUri the namespace URI
+ * @return the namespace URI that was previously registered with the supplied prefix, or null if the
+ * prefix was not previously bound to a namespace URI
+ * @throws IllegalArgumentException if the namespace URI is null
+ */
+ String register( String prefix, String namespaceUri );
+
+ /**
+ * Unregister the namespace with the supplied URI.
+ * @param namespaceUri the namespace URI
+ * @return true if the namespace was removed, or false if the namespace was not registered
+ * @throws IllegalArgumentException if the namespace URI is null
+ * @throws NamespaceException if there is a problem unregistering the namespace
+ */
+ boolean unregister( String namespaceUri );
+
+ /**
+ * Obtain the set of namespaces that are registered.
+ * @return the set of namespace URIs; never null
+ */
+ Set<String> getRegisteredNamespaceUris();
+
+ /**
+ * Obtain a snapshot of all of the {@link Namespace namespaces} registered at the time this method
+ * is called. The resulting set is immutable, and will not reflect changes made to the registry.
+ * @return an immutable set of &Namespace; objects reflecting a snapshot of the registry; never null
+ */
+ Set<&Namespace;> getNamespaces();
+}
+</programlisting>
+ <para>
+ This interfaces exposes &Namespace; objects that are immutable:
+ </para>
+<programlisting>
+@Immutable
+interface &Namespace; extends Comparable<&Namespace;> {
+ /**
+ * Get the prefix for the namespace
+ * @return the prefix; never null but possibly the empty string
+ */
+ String getPrefix();
+
+ /**
+ * Get the URI for the namespace
+ * @return the namespace URI; never null but possibly the empty string
+ */
+ String getNamespaceUri();
+}
+</programlisting>
+ <para>
+ JBoss DNA actually uses several implementations of &NamespaceRegistry;, but you can even implement your own
+ and create &ExecutionContext;s that uses it:
+ </para>
+<programlisting>
+&NamespaceRegistry; myRegistry = ...
+&ExecutionContext; contextWithMyRegistry = context.with(myRegistry);
+</programlisting>
+ </sect1>
+ <sect1 id="class_loader_factory">
+ <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 many of the "interesting" components
+ are extensions that plug into and are used by different parts of the core or by layers above (such as the
+ <link linkend="jcr">JCR implementation</link>). 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.
+ </para>
+ <note>
+ <para>Of course, you can put all the JARs on the application classpath, too.
+ This is what the examples in the &GettingStarted; document do.
+ </para>
+ </note>
+ <para>
+ But in this case, how does JBoss DNA load all the extension classes? You may have noticed earlier that
+ &ExecutionContext; implements the &ClassLoaderFactory; interface with a single method:
+ </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>
+ This means that any component that has a reference to an &ExecutionContext; has the ability to create a
+ class loader with a supplied class path. As we'll see later, the connectors and sequencers are all
+ defined with a class and optional class path. This is where that class path comes in.
+ </para>
+ <para>
+ The actual meaning of the class path, however, is a function of the implementation. JBoss DNA uses
+ a &StandardClassLoaderFactory; that just loads the classes using the Thread's current context
+ class loader (or, if there is none, delegates to the class loader that loaded the &StandardClassLoaderFactory; class).
+ Of course, it's possible to implement other &ClassLoaderFactory; with other implementations.
+ Then, just create a subcontext with your implementation:
+ </para>
+<programlisting>
+&ClassLoaderFactory; myClassLoaderFactory = ...
+&ExecutionContext; contextWithMyClassLoaderFactories = context.with(myClassLoaderFactory);
+</programlisting>
+ <note>
+ <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 not ready for use, however, since there is no tooling to help populate the repository.
+ </para>
+ </note>
+ </sect1>
+ <sect1 id="mime-type-detectors">
+ <title>Property factory and value factories</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.
+ </para>
+ <para>
+ JBoss DNA defines a &MimeTypeDetector; interface that abstracts the implementation that actually determines
+ the MIME type given the name and content.
+ 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.
+ Here is the interface:
+ </para>
+ <programlisting>
+@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>
+ To use a detector, simply invoke the 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. 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>
+ <para>
+ Once again, you can obtain a &MimeTypeDetector; from the &ExecutionContext;. JBoss DNA provides and uses by
+ default an implementation that uses only the name (the content is ignored), looking at the name's extension
+ and looking for a match in a small listing (loaded from the <code>org/jboss/dna/graph/mime.types</code> loaded from the classpath).
+ You can add extensions by copying this file, adding or correcting the entries, and then placing your updated file in the
+ expected location on the classpath.
+ </para>
+ <para>
+ Of course, you can always use a different &MimeTypeDetector; by creating a subcontext and supplying your implementation:
+ </para>
+<programlisting>
+&MimeTypeDetector; myDetector = ...
+&ExecutionContext; contextWithMyDetector = context.with(myDetector);
+</programlisting>
+ </sect1>
+ <sect1>
+ <title>Property factory and value factories</title>
+ <para>
+ Two other components are made available by the &ExecutionContext;. The &PropertyFactory; is an interface
+ that can be used to create &Property; instances, which are used throughout the graph API. The &ValueFactories;
+ interface provides access to a number of different factories for different kinds of property values.
+ These will be discussed in much more detail in the next chapter. But like the other components that
+ are in an &ExecutionContext;, you can create subcontexts with different implementations:
+ </para>
+<programlisting>
+&PropertyFactory; myPropertyFactory = ...
+&ExecutionContext; contextWithMyPropertyFactory = context.with(myPropertyFactory);
+</programlisting>
+ <para>and</para>
+<programlisting>
+&ValueFactories; myValueFactories = ...
+&ExecutionContext; contextWithMyValueFactories = context.with(myValueFactories);
+</programlisting>
+ <para>
+ Of course, implementing your own factories is a pretty advanced topic, and it will likely be something you do not
+ need to do in your application.
+ </para>
+ </sect1>
+ <sect1>
+ <title>Summary</title>
+ <para>
+ In this chapter, we introduced the &ExecutionContext; as a representation of the environment in which many of the
+ JBoss DNA components operate. &ExecutionContext; provides a very simple but powerful way to inject commonly-needed
+ facilities throughout the system.
+ </para>
+ <para>
+ In the <link linkend="graph-api">next chapter</link>, we'll dive into Graph API and will introduce the notion of
+ nodes, paths, names, and properties, that are so essential and used throughout JBoss DNA.
+ </para>
+ </sect1>
+</chapter>
+
Property changes on: trunk/docs/reference/src/main/docbook/en-US/content/core/execution_context.xml
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Deleted: trunk/docs/reference/src/main/docbook/en-US/content/core/mimetypes.xml
===================================================================
--- trunk/docs/reference/src/main/docbook/en-US/content/core/mimetypes.xml 2009-06-08 16:12:59 UTC (rev 998)
+++ trunk/docs/reference/src/main/docbook/en-US/content/core/mimetypes.xml 2009-06-08 19:58:59 UTC (rev 999)
@@ -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_do_I_make_m...">Maven 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>
Added: trunk/docs/reference/src/main/docbook/en-US/content/mime_type_detectors/aperture.xml
===================================================================
--- trunk/docs/reference/src/main/docbook/en-US/content/mime_type_detectors/aperture.xml (rev 0)
+++ trunk/docs/reference/src/main/docbook/en-US/content/mime_type_detectors/aperture.xml 2009-06-08 19:58:59 UTC (rev 999)
@@ -0,0 +1,47 @@
+<?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="aperture-mimetype-detector">
+ <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>
+ <para>
+ To use, simply include the <code>dna-mime-type-detector-aperture.jar</code> file on the classpath
+ and create a new &ExecutionContext; subcontext with it:
+ </para>
+<programlisting>
+&MimeTypeDetector; myDetector = new ApertureMimeTypeDetector();
+&ExecutionContext; contextWithMyDetector = context.with(myDetector);
+</programlisting>
+</chapter>
Property changes on: trunk/docs/reference/src/main/docbook/en-US/content/mime_type_detectors/aperture.xml
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Added: trunk/docs/reference/src/main/docbook/en-US/content/mime_type_detectors/custom_detectors.xml
===================================================================
--- trunk/docs/reference/src/main/docbook/en-US/content/mime_type_detectors/custom_detectors.xml (rev 0)
+++ trunk/docs/reference/src/main/docbook/en-US/content/mime_type_detectors/custom_detectors.xml 2009-06-08 19:58:59 UTC (rev 999)
@@ -0,0 +1,128 @@
+<?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="custom-mimetype-detector">
+ <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>
+ <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_do_I_make_m...">Maven 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>
+</chapter>
Property changes on: trunk/docs/reference/src/main/docbook/en-US/content/mime_type_detectors/custom_detectors.xml
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Modified: trunk/docs/reference/src/main/docbook/en-US/custom.dtd
===================================================================
--- trunk/docs/reference/src/main/docbook/en-US/custom.dtd 2009-06-08 16:12:59 UTC (rev 998)
+++ trunk/docs/reference/src/main/docbook/en-US/custom.dtd 2009-06-08 19:58:59 UTC (rev 999)
@@ -78,6 +78,7 @@
<!ENTITY Property "<ulink url='&API;graph/property/Property.html'><interface>Property</interface></ulink>">
<!ENTITY ValueFactories "<ulink url='&API;graph/property/ValueFactories.html'><interface>ValueFactories</interface></ulink>">
<!ENTITY NamespaceRegistry "<ulink url='&API;graph/property/NamespaceRegistry.html'><interface>NamespaceRegistry</interface></ulink>">
+<!ENTITY Namespace "<ulink url='&API;graph/property/NamespaceRegistry.Namespaces.html'><interface>Namespace</interface></ulink>">
<!ENTITY PropertyFactory "<ulink url='&API;graph/property/PropertyFactory.html'><interface>PropertyFactory</interface></ulink>">
<!ENTITY PathNotFoundException "<ulink url='&API;graph/property/PathNotFoundException.html'><classname>PathNotFoundException</classname></ulink>">
<!ENTITY RepositorySource "<ulink url='&API;graph/connector/RepositorySource.html'><interface>RepositorySource</interface></ulink>">
Modified: trunk/docs/reference/src/main/docbook/en-US/master.xml
===================================================================
--- trunk/docs/reference/src/main/docbook/en-US/master.xml 2009-06-08 16:12:59 UTC (rev 998)
+++ trunk/docs/reference/src/main/docbook/en-US/master.xml 2009-06-08 19:58:59 UTC (rev 999)
@@ -81,11 +81,9 @@
JCR-related components are covered in the <link linkend="jcr-part">next part</link>.
</para>
</partintro>
- <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/execution_context.xml"/>
<xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="content/core/connector.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 id="jcr-part">
@@ -135,5 +133,17 @@
<xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="content/sequencers/image.xml"/>
<xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="content/sequencers/mp3.xml"/>
</part>
+ <part id="provied-mime-type-detectors-part">
+ <title>Provided MIME type detectors</title>
+ <partintro>
+ <para>
+ The JBoss DNA project provides a number of <link linkend="mime-type-detectors">MIME type detectors</link> out-of-the-box.
+ These are ready to be used by simply including them in the classpath and
+ <link linkend="mime-type-detectors">setting up the &ExecutionContext;</link> appropriately.
+ </para>
+ </partintro>
+ <xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="content/mime_type_detectors/aperture.xml"/>
+ <xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="content/mime_type_detectors/custom_detectors.xml"/>
+ </part>
<xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="content/future.xml"/>
</book>
16 years, 6 months
DNA SVN: r998 - in trunk/docs/reference/src/main/docbook/en-US: content/connectors and 2 other directories.
by dna-commits@lists.jboss.org
Author: rhauch
Date: 2009-06-08 12:12:59 -0400 (Mon, 08 Jun 2009)
New Revision: 998
Added:
trunk/docs/reference/src/main/docbook/en-US/content/connectors/federation.xml
trunk/docs/reference/src/main/docbook/en-US/content/connectors/file_system.xml
trunk/docs/reference/src/main/docbook/en-US/content/connectors/jboss_cache.xml
trunk/docs/reference/src/main/docbook/en-US/content/connectors/jdbc_metadata.xml
trunk/docs/reference/src/main/docbook/en-US/content/connectors/jdbc_storage.xml
trunk/docs/reference/src/main/docbook/en-US/content/connectors/subversion.xml
trunk/docs/reference/src/main/docbook/en-US/content/sequencers/compact_node_types.xml
trunk/docs/reference/src/main/docbook/en-US/content/sequencers/image.xml
trunk/docs/reference/src/main/docbook/en-US/content/sequencers/java_source.xml
trunk/docs/reference/src/main/docbook/en-US/content/sequencers/microsoft_office.xml
trunk/docs/reference/src/main/docbook/en-US/content/sequencers/mp3.xml
trunk/docs/reference/src/main/docbook/en-US/content/sequencers/xml.xml
Modified:
trunk/docs/reference/src/main/docbook/en-US/content/developers/tools.xml
trunk/docs/reference/src/main/docbook/en-US/custom.dtd
trunk/docs/reference/src/main/docbook/en-US/master.xml
Log:
Updated the skeleton connector and sequencer chapters to have the correct titles (but nothing more). Also added reference to Git within the developer tools chapter.
Added: trunk/docs/reference/src/main/docbook/en-US/content/connectors/federation.xml
===================================================================
--- trunk/docs/reference/src/main/docbook/en-US/content/connectors/federation.xml (rev 0)
+++ trunk/docs/reference/src/main/docbook/en-US/content/connectors/federation.xml 2009-06-08 16:12:59 UTC (rev 998)
@@ -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="federation-connector">
+ <title>Federation Connector</title>
+ <para>
+ blah blah
+ </para>
+</chapter>
+
Property changes on: trunk/docs/reference/src/main/docbook/en-US/content/connectors/federation.xml
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Added: trunk/docs/reference/src/main/docbook/en-US/content/connectors/file_system.xml
===================================================================
--- trunk/docs/reference/src/main/docbook/en-US/content/connectors/file_system.xml (rev 0)
+++ trunk/docs/reference/src/main/docbook/en-US/content/connectors/file_system.xml 2009-06-08 16:12:59 UTC (rev 998)
@@ -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="file-system-connector">
+ <title>File System Connector</title>
+ <para>
+ blah blah
+ </para>
+</chapter>
+
Property changes on: trunk/docs/reference/src/main/docbook/en-US/content/connectors/file_system.xml
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Added: trunk/docs/reference/src/main/docbook/en-US/content/connectors/jboss_cache.xml
===================================================================
--- trunk/docs/reference/src/main/docbook/en-US/content/connectors/jboss_cache.xml (rev 0)
+++ trunk/docs/reference/src/main/docbook/en-US/content/connectors/jboss_cache.xml 2009-06-08 16:12:59 UTC (rev 998)
@@ -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="jboss-cache-connector">
+ <title>JBoss Cache Connector</title>
+ <para>
+ blah blah
+ </para>
+</chapter>
+
Property changes on: trunk/docs/reference/src/main/docbook/en-US/content/connectors/jboss_cache.xml
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Added: trunk/docs/reference/src/main/docbook/en-US/content/connectors/jdbc_metadata.xml
===================================================================
--- trunk/docs/reference/src/main/docbook/en-US/content/connectors/jdbc_metadata.xml (rev 0)
+++ trunk/docs/reference/src/main/docbook/en-US/content/connectors/jdbc_metadata.xml 2009-06-08 16:12:59 UTC (rev 998)
@@ -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="jdbc-metadata-connector">
+ <title>JDBC Metadata Connector</title>
+ <para>
+ blah blah
+ </para>
+</chapter>
+
Property changes on: trunk/docs/reference/src/main/docbook/en-US/content/connectors/jdbc_metadata.xml
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Added: trunk/docs/reference/src/main/docbook/en-US/content/connectors/jdbc_storage.xml
===================================================================
--- trunk/docs/reference/src/main/docbook/en-US/content/connectors/jdbc_storage.xml (rev 0)
+++ trunk/docs/reference/src/main/docbook/en-US/content/connectors/jdbc_storage.xml 2009-06-08 16:12:59 UTC (rev 998)
@@ -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="jdbc-storage-connector">
+ <title>JDBC Storage (JPA) Connector</title>
+ <para>
+ blah blah
+ </para>
+</chapter>
+
Property changes on: trunk/docs/reference/src/main/docbook/en-US/content/connectors/jdbc_storage.xml
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Added: trunk/docs/reference/src/main/docbook/en-US/content/connectors/subversion.xml
===================================================================
--- trunk/docs/reference/src/main/docbook/en-US/content/connectors/subversion.xml (rev 0)
+++ trunk/docs/reference/src/main/docbook/en-US/content/connectors/subversion.xml 2009-06-08 16:12:59 UTC (rev 998)
@@ -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="subversion-connector">
+ <title>Subversion Connector</title>
+ <para>
+ blah blah
+ </para>
+</chapter>
+
Property changes on: trunk/docs/reference/src/main/docbook/en-US/content/connectors/subversion.xml
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Modified: trunk/docs/reference/src/main/docbook/en-US/content/developers/tools.xml
===================================================================
--- trunk/docs/reference/src/main/docbook/en-US/content/developers/tools.xml 2009-06-08 15:47:40 UTC (rev 997)
+++ trunk/docs/reference/src/main/docbook/en-US/content/developers/tools.xml 2009-06-08 16:12:59 UTC (rev 998)
@@ -163,6 +163,16 @@
by the patch.
</para>
</sect1>
+ <sect1 id="git">
+ <title>Git</title>
+ <para>
+ Several contributors are using <ulink url="&Git;">Git</ulink> on their local development machines. This allows
+ the developer to use Git branches, commits, merges, and other Git tools, but still using
+ the JBoss DNA <link linkend="svn">Subversion</link> repository. For more information, see our
+ <ulink url="http://jbossdna.blogspot.com/2009/05/git-and-svn-beginning.html">blog</ulink>
+ <ulink url="http://jbossdna.blogspot.com/2009/05/git-it-together-with-subversion.html">posts</ulink> on the topic.
+ </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
Added: trunk/docs/reference/src/main/docbook/en-US/content/sequencers/compact_node_types.xml
===================================================================
--- trunk/docs/reference/src/main/docbook/en-US/content/sequencers/compact_node_types.xml (rev 0)
+++ trunk/docs/reference/src/main/docbook/en-US/content/sequencers/compact_node_types.xml 2009-06-08 16:12:59 UTC (rev 998)
@@ -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="cnd-sequencer">
+ <title>Compact Node Type (CND) 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/compact_node_types.xml
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Added: trunk/docs/reference/src/main/docbook/en-US/content/sequencers/image.xml
===================================================================
--- trunk/docs/reference/src/main/docbook/en-US/content/sequencers/image.xml (rev 0)
+++ trunk/docs/reference/src/main/docbook/en-US/content/sequencers/image.xml 2009-06-08 16:12:59 UTC (rev 998)
@@ -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="image-sequencer">
+ <title>Image 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/image.xml
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Added: trunk/docs/reference/src/main/docbook/en-US/content/sequencers/java_source.xml
===================================================================
--- trunk/docs/reference/src/main/docbook/en-US/content/sequencers/java_source.xml (rev 0)
+++ trunk/docs/reference/src/main/docbook/en-US/content/sequencers/java_source.xml 2009-06-08 16:12:59 UTC (rev 998)
@@ -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="java-source-sequencer">
+ <title>Java Source 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/java_source.xml
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Added: trunk/docs/reference/src/main/docbook/en-US/content/sequencers/microsoft_office.xml
===================================================================
--- trunk/docs/reference/src/main/docbook/en-US/content/sequencers/microsoft_office.xml (rev 0)
+++ trunk/docs/reference/src/main/docbook/en-US/content/sequencers/microsoft_office.xml 2009-06-08 16:12:59 UTC (rev 998)
@@ -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="msoffice-sequencer">
+ <title><trademark class='registered'>Microsoft Office</trademark> Document 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/microsoft_office.xml
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Added: trunk/docs/reference/src/main/docbook/en-US/content/sequencers/mp3.xml
===================================================================
--- trunk/docs/reference/src/main/docbook/en-US/content/sequencers/mp3.xml (rev 0)
+++ trunk/docs/reference/src/main/docbook/en-US/content/sequencers/mp3.xml 2009-06-08 16:12:59 UTC (rev 998)
@@ -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="mp3-sequencer">
+ <title>MP3 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/mp3.xml
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Added: trunk/docs/reference/src/main/docbook/en-US/content/sequencers/xml.xml
===================================================================
--- trunk/docs/reference/src/main/docbook/en-US/content/sequencers/xml.xml (rev 0)
+++ trunk/docs/reference/src/main/docbook/en-US/content/sequencers/xml.xml 2009-06-08 16:12:59 UTC (rev 998)
@@ -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="xml-sequencer">
+ <title>XML Document 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/xml.xml
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Modified: trunk/docs/reference/src/main/docbook/en-US/custom.dtd
===================================================================
--- trunk/docs/reference/src/main/docbook/en-US/custom.dtd 2009-06-08 15:47:40 UTC (rev 997)
+++ trunk/docs/reference/src/main/docbook/en-US/custom.dtd 2009-06-08 16:12:59 UTC (rev 998)
@@ -21,6 +21,7 @@
<!ENTITY MailTo "mailto:dna-users@jboss.org">
<!ENTITY Wikipedia "http://en.wikipedia.org/wiki/">
<!ENTITY JBossMaven "http://repository.jboss.com/maven2/">
+<!ENTITY Git "http://git-scm.com/">
<!ENTITY Java "http://java.sun.com/j2se/1.5.0/docs/api/">
Modified: trunk/docs/reference/src/main/docbook/en-US/master.xml
===================================================================
--- trunk/docs/reference/src/main/docbook/en-US/master.xml 2009-06-08 15:47:40 UTC (rev 997)
+++ trunk/docs/reference/src/main/docbook/en-US/master.xml 2009-06-08 16:12:59 UTC (rev 998)
@@ -103,21 +103,37 @@
</part>
<part id="provied-connectors-part">
<title>Provided Connectors</title>
- <para>
- The JBoss DNA project provides a number of <link linkend="connector_framework">connectors</link> out-of-the-box.
- These are ready to be used by simply including them in the classpath and <link linkend="configuration">configuring</link>
- them as a repository source.
- </para>
+ <partintro>
+ <para>
+ The JBoss DNA project provides a number of <link linkend="connector_framework">connectors</link> out-of-the-box.
+ These are ready to be used by simply including them in the classpath and <link linkend="configuration">configuring</link>
+ them as a repository source.
+ </para>
+ </partintro>
<xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="content/connectors/in_memory.xml"/>
+ <xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="content/connectors/file_system.xml"/>
+ <xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="content/connectors/jdbc_storage.xml"/>
+ <xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="content/connectors/federation.xml"/>
+ <xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="content/connectors/subversion.xml"/>
+ <xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="content/connectors/jboss_cache.xml"/>
+ <xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="content/connectors/jdbc_metadata.xml"/>
</part>
<part id="provied-sequencers-part">
<title>Provided Sequencers</title>
- <para>
- The JBoss DNA project provides a number of <link linkend="sequencing_framework">sequencers</link> out-of-the-box.
- These are ready to be used by simply including them in the classpath and <link linkend="configuration">configuring</link>
- them appropriately.
- </para>
+ <partintro>
+ <para>
+ The JBoss DNA project provides a number of <link linkend="sequencing_framework">sequencers</link> out-of-the-box.
+ These are ready to be used by simply including them in the classpath and <link linkend="configuration">configuring</link>
+ them appropriately.
+ </para>
+ </partintro>
+ <xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="content/sequencers/compact_node_types.xml"/>
+ <xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="content/sequencers/xml.xml"/>
<xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="content/sequencers/zip.xml"/>
+ <xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="content/sequencers/microsoft_office.xml"/>
+ <xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="content/sequencers/java_source.xml"/>
+ <xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="content/sequencers/image.xml"/>
+ <xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="content/sequencers/mp3.xml"/>
</part>
<xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="content/future.xml"/>
</book>
16 years, 6 months
DNA SVN: r997 - in trunk/docs/reference/src/main/docbook/en-US: content/core and 1 other directory.
by dna-commits@lists.jboss.org
Author: rhauch
Date: 2009-06-08 11:47:40 -0400 (Mon, 08 Jun 2009)
New Revision: 997
Added:
trunk/docs/reference/src/main/docbook/en-US/content/core/connector.xml
Removed:
trunk/docs/reference/src/main/docbook/en-US/content/core/repositories.xml
Modified:
trunk/docs/reference/src/main/docbook/en-US/content/core/sequencing.xml
trunk/docs/reference/src/main/docbook/en-US/master.xml
Log:
More revisions to the master Reference Guide document.
Copied: trunk/docs/reference/src/main/docbook/en-US/content/core/connector.xml (from rev 996, trunk/docs/reference/src/main/docbook/en-US/content/core/repositories.xml)
===================================================================
--- trunk/docs/reference/src/main/docbook/en-US/content/core/connector.xml (rev 0)
+++ trunk/docs/reference/src/main/docbook/en-US/content/core/connector.xml 2009-06-08 15:47:40 UTC (rev 997)
@@ -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="connector_framework">
+ <title>Connector Framework</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=true&mod...">more 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_do_I_make_m...">Maven 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">internal 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/connector.xml
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Deleted: trunk/docs/reference/src/main/docbook/en-US/content/core/repositories.xml
===================================================================
--- trunk/docs/reference/src/main/docbook/en-US/content/core/repositories.xml 2009-06-08 15:21:29 UTC (rev 996)
+++ trunk/docs/reference/src/main/docbook/en-US/content/core/repositories.xml 2009-06-08 15:47:40 UTC (rev 997)
@@ -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=true&mod...">more 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_do_I_make_m...">Maven 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">internal 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>
Modified: trunk/docs/reference/src/main/docbook/en-US/content/core/sequencing.xml
===================================================================
--- trunk/docs/reference/src/main/docbook/en-US/content/core/sequencing.xml 2009-06-08 15:21:29 UTC (rev 996)
+++ trunk/docs/reference/src/main/docbook/en-US/content/core/sequencing.xml 2009-06-08 15:47:40 UTC (rev 997)
@@ -28,7 +28,7 @@
<!ENTITY % CustomDTD SYSTEM "../../custom.dtd">
%CustomDTD;
]>
-<chapter id="sequencing">
+<chapter id="sequencing_framework">
<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
Modified: trunk/docs/reference/src/main/docbook/en-US/master.xml
===================================================================
--- trunk/docs/reference/src/main/docbook/en-US/master.xml 2009-06-08 15:21:29 UTC (rev 996)
+++ trunk/docs/reference/src/main/docbook/en-US/master.xml 2009-06-08 15:47:40 UTC (rev 997)
@@ -52,32 +52,71 @@
</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"/>
- <part>
+ <part id="developers-part">
<title>Developers and Contributors</title>
+ <partintro>
+ <para>
+ The JBoss DNA project uses a number of process, tools, and procedures to assist in the development of
+ the software. This portion of the document focuses on these aspects and will help developers and contributors
+ obtain the source code, build locally, and contribute to the project.
+ </para>
+ <para>If you're not contributing to the project but are still developing
+ custom <link linkend="connector_framework">connectors</link> or <link linkend="sequencing_framework">sequencers</link>.
+ this information may be helpful in establishing your own environment.
+ </para>
+ </partintro>
<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>
+ <part id="core-part">
<title>JBoss DNA Core</title>
+ <partintro>
+ <para>
+ The JBoss DNA project organizes the codebase into a number of subprojects. The most fundamental are those
+ <emphasis>core libraries</emphasis>, including the graph API, connector framework, sequencing framework,
+ as well as the configuration and engine in which all the components run. These are all topics covered
+ in this part of the document.
+ </para>
+ <para>The JBoss DNA implementation of the <ulink url="&JSR170;">JCR API</ulink> as well as some other
+ JCR-related components are covered in the <link linkend="jcr-part">next part</link>.
+ </para>
+ </partintro>
<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/connector.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>
+ <part id="jcr-part">
<title>JBoss DNA JCR</title>
+ <partintro>
+ <para>
+ The JBoss DNA project provides an implementation of the <ulink url="&JSR170;">JCR API</ulink>, which is
+ built on top of the <link linkend="core-part">core libraries</link> discussed earlier. This implementation
+ as well as a number of JCR-related components are described in this part of the document.
+ </para>
+ </partintro>
<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/deploying_dna_jcr.xml"/>
<xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="content/jcr/rest_service.xml"/>
</part>
- <part>
+ <part id="provied-connectors-part">
<title>Provided Connectors</title>
+ <para>
+ The JBoss DNA project provides a number of <link linkend="connector_framework">connectors</link> out-of-the-box.
+ These are ready to be used by simply including them in the classpath and <link linkend="configuration">configuring</link>
+ them as a repository source.
+ </para>
<xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="content/connectors/in_memory.xml"/>
</part>
- <part>
+ <part id="provied-sequencers-part">
<title>Provided Sequencers</title>
+ <para>
+ The JBoss DNA project provides a number of <link linkend="sequencing_framework">sequencers</link> out-of-the-box.
+ These are ready to be used by simply including them in the classpath and <link linkend="configuration">configuring</link>
+ them appropriately.
+ </para>
<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"/>
16 years, 6 months