<?xml version="1.0" encoding="UTF-8"?>
<!--
  ~ ModeShape (http://www.modeshape.org)
  ~
  ~ 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.
  ~
  ~ ModeShape is free software. Unless otherwise indicated, all code in ModeShape
  ~ 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.
  ~
  ~ ModeShape is distributed in the hope that it will be useful,
  ~ but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
  ~ or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License
  ~ for more details.
  ~
  ~ You should have received a copy of the GNU Lesser General Public License
  ~ along with this distribution; if not, write to:
  ~ Free Software Foundation, Inc.
  ~ 51 Franklin Street, Fifth Floor
  ~ Boston, MA  02110-1301  USA
  -->
<!DOCTYPE preface PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN" "http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd"	[
<!ENTITY % CustomDTD SYSTEM "../../custom.dtd">
%CustomDTD;
]>
<chapter id="sequencing_framework">
  <title>Sequencing framework</title>
	<para>
		Many repositories are used (at least in part) to manage files and other artifacts, including 
		service definitions, policy files, images, media, documents, presentations, application components, 
		reusable libraries, configuration files, application installations, databases schemas, management scripts, and so on.
		Unlocking the information buried within	all of those files is what ModeShape sequencing is all about.  
		As files are loaded into the repository, you ModeShape instance can automatically sequence these files to extract 
		from their content meaningful information that can be stored in the repository, where it can then be 
		searched, accessed, and analyzed using the JCR API.
	</para>
  <sect1 id="sequencers">
    <title>Sequencers</title>
		<para>
			Sequencers are just POJOs that implement a specific interface, and their job is to process a stream of
			data (supplied by ModeShape) to extract meaningful content that usually takes the form of a structured graph.  
			Exactly what content is up to each sequencer
			implementation.  For example, ModeShape comes with an <link linkend="image-sequencer">image sequencer</link>
			that extracts the simple metadata from different kinds of image files (e.g., JPEG, GIF, PNG, etc.).
			Another example is the <link linkend="cnd-sequencer">Compact Node Definition (CND) sequencer</link> 
			that processes the CND files to extract and produce a structured representation of the node type definitions,
			property definitions, and child node definitions contained within the file.
		</para>
		<para>
			Sequencers are configured to identify the kinds of nodes that the sequencers can work against.
			When content in the repository changes, ModeShape looks to see which (if any) sequencers might be able
			to run on the changed content.  If any sequencer configurations do match, those sequencers are run
			against the content, and the structured graph output of the sequencers is then written back into the repository
			(at a location dictated by the sequencer configuration).  And once that information is in the repository,
			it can be easily found and accessed via the standard JCR API.
		</para>
		<para>
			In other words, ModeShape uses sequencers to help you extract more meaning from the artifacts you already
			are managing, and makes it much easier for applications to find and use all that valuable information.
			All without your applications doing anything extra.
		</para> 
	</sect1>
	<sect1 id="stream-sequencers">
		<title>Stream Sequencers</title>
    <para>
			The &StreamSequencer; interface defines the single method that must be implemented by a sequencer:
		</para>
    <programlisting>
public interface &StreamSequencer; {

    /**
     * Sequence the data found in the supplied stream, placing the output 
     * information into the supplied map.
     *
     * @param stream the stream with the data to be sequenced; never null
     * @param output the output from the sequencing operation; never null
     * @param context the context for the sequencing operation; never null
     */
    void sequence( &InputStream; stream, &SequencerOutput; output, &StreamSequencerContext; context );
}
</programlisting>
		<para>
			A new instance is created for each sequencing operation, so there is no need for the class to be synchronized
			or thread-safe. Additionally, when a sequencer configuration includes properties (see 
			<link linkend="programmatically_configuring_sequencers">configuring a sequencer</link>), ModeShape will 
			set those properties on the &StreamSequencer; implementation using JavaBean-style setter methods.
			This makes it easy to define sequencer-specific properties on the sequencer configurations,
			while making it easy to implement with JavaBean-style setter methods.
		</para>
		<para>
			Implementations are responsible for processing the content in the supplied &InputStream; content and generating
			structured content using the supplied &SequencerOutput; interface.  
	    The &StreamSequencerContext; provides additional details about the information that is being sequenced,
		  including the location and properties of the node being sequenced, the MIME type
	    of the node being sequenced, and a &Problems; object where the sequencer can record problems that aren't 
			severe enough to warrant throwing an exception.  The &StreamSequencerContext; also provides access
			to the &ValueFactories; that can be used to create &Path;, &Name;, and any other value objects.
    </para>
    <para>The &SequencerOutput; interface is fairly easy to use, and its job is to hide from the sequencer
			all the specifics about where the output is being written.  Therefore, the interface has only a few methods
			for implementations to call.
	    Two methods set the property values on a node, while the other sets references to other nodes in the repository.
			Use these methods to describe the properties of the nodes you want to create, using relative paths for the nodes and
	    valid JCR property names for properties and references.  ModeShape will ensure that nodes are created or updated
	    whenever they're needed.
		</para>
	  <programlisting>
public interface &SequencerOutput; {

  /**
   * Set the supplied property on the supplied node.  The allowable
   * values are any of the following:
   *   - primitives (which will be autoboxed)
   *   - String instances
   *   - String arrays
   *   - byte arrays
   *   - InputStream instances
   *   - Calendar instances
   *
   * @param nodePath the path to the node containing the property; 
   * may not be null
   * @param property the name of the property to be set
   * @param values the value(s) for the property; may be empty if 
   * any existing property is to be removed
   */
  void setProperty( String nodePath, String property, Object... values );
  void setProperty( &Path; nodePath, &Name; property, Object... values );

  /**
   * Set the supplied reference on the supplied node.
   *
   * @param nodePath the path to the node containing the property; 
   * may not be null
   * @param property the name of the property to be set
   * @param paths the paths to the referenced property, which may be
   * absolute paths or relative to the sequencer output node;
   * may be empty if any existing property is to be removed
   */
  void setReference( String nodePath, String property, String... paths );
}
		</programlisting>
		<note>
    	<para>
				ModeShape will create nodes of type <code>nt:unstructured</code> unless you specify the value for the
      	<code>jcr:primaryType</code> property.  You can also specify the values for the <code>jcr:mixinTypes</code> property
      	if you want to add mixins to any node.
			</para>
		</note>
  </sect1>
  <sect1 id="path-expressions">
    <title>Path Expressions</title>
		<para>
			Each sequencer must be configured to describe the areas or types of content that the sequencer is capable
			of handling.  This is done by specifying these patterns using path expressions that
	    identify the nodes (or node patterns) that should be sequenced and where to store the output generated by the sequencer.
			We'll see how to fully configure a sequencer in the <link linkend="configuration">next chapter</link>,
			but before then let's dive into path expressions in more detail.
		</para>
		<para>
			A path expression consist of two parts: a selection criteria (or an input path) and an output path:
		</para>
	  <programlisting><![CDATA[  inputPath => outputPath ]]></programlisting>
		<para>
			The <emphasis>inputPath</emphasis> part defines an expression for the path of a node that is to be sequenced.
		 	Input paths consist of '<code>/</code>' separated segments, where each segment represents a pattern for a single node's
			name (including the same-name-sibling indexes) and '<code>@</code>' signifies a property name.
		</para>
		<para>
			Let's first look at some simple examples:
		</para>
		<table frame='all'>
			<title>Simple Input Path Examples</title>
			<tgroup cols='2' align='left' colsep='1' rowsep='1'>
	      <colspec colname='c1' colwidth="1*"/>
	      <colspec colname='c2' colwidth="1*"/>
				<thead>
					<row>
			  		<entry>Input Path</entry>
			  		<entry>Description</entry>
					</row>
				</thead>
				<tbody>
					<row><entry>/a/b</entry><entry>Match node "<code>b</code>" that is a child of the top level node "<code>a</code>". Neither node
					     may have any same-name-sibilings.</entry></row>
					<row><entry>/a/*</entry><entry>Match any child node of the top level node "<code>a</code>".</entry></row>
					<row><entry>/a/*.txt</entry><entry>Match any child node of the top level node "<code>a</code>" that also has a name ending in "<code>.txt</code>".</entry></row>
					<row><entry>/a/*.txt</entry><entry>Match any child node of the top level node "<code>a</code>" that also has a name ending in "<code>.txt</code>".</entry></row>
					<row><entry>/a/b@c</entry><entry>Match the property "<code>c</code>" of node "<code>/a/b</code>".</entry></row>
					<row><entry>/a/b[2]</entry><entry>The second child named "<code>b</code>" below the top level node "<code>a</code>".</entry></row>
					<row><entry>/a/b[2,3,4]</entry><entry>The second, third or fourth child named "<code>b</code>" below the top level node "<code>a</code>".</entry></row>
					<row><entry>/a/b[*]</entry><entry>Any (and every) child named "<code>b</code>" below the top level node "<code>a</code>".</entry></row>
					<row><entry>//a/b</entry><entry>Any node named "<code>b</code>" that exists below a node named "<code>a</code>", regardless 
					     of where node "<code>a</code>" occurs.  Again, neither node may have any same-name-sibilings.</entry></row>
				</tbody>
			</tgroup>
		</table>
		<para>
			With these simple examples, you can probably discern the most important rules.  First, the '<code>*</code>' is a wildcard character
		  that matches any character or sequence of characters in a node's name (or index if appearing in between square brackets), and
		  can be used in conjunction with other characters (e.g., "<code>*.txt</code>").
		</para>
		<para>
			Second, square brackets (i.e., '<code>[</code>' and '<code>]</code>') are used to match a node's same-name-sibiling index.
		  You can put a single non-negative number or a comma-separated list of non-negative numbers.  Use '0' to match a node that has no
		  same-name-sibilings, or any positive number to match the specific same-name-sibling.
		</para>
		<para>
			Third, combining two delimiters (e.g., "<code>//</code>") matches any sequence of nodes, regardless of what their names are 
		  or how many nodes.  Often used with other patterns to identify nodes at any level matching other patterns. 
			Three or more sequential slash characters are treated as two.
		</para>
		<para>
			Many input paths can be created using just these simple rules.  However, input paths can be more complicated.  Here are some
			more examples:
		</para>
		<table frame='all'>
			<title>More Complex Input Path Examples</title>
			<tgroup cols='2' align='left' colsep='1' rowsep='1'>
	      <colspec colname='c1' colwidth="1*"/>
	      <colspec colname='c2' colwidth="1*"/>
				<thead>
					<row>
			  		<entry>Input Path</entry>
			  		<entry>Description</entry>
					</row>
				</thead>
				<tbody>
					<row><entry>/a/(b|c|d)</entry><entry>Match children of the top level node "<code>a</code>" that are named "<code>b</code>", 
							 "<code>c</code>" or "<code>d</code>". None of the nodes may have same-name-sibling indexes.</entry></row>
					<row><entry>/a/b[c/d]</entry><entry>Match node "<code>b</code>" child of the top level node "<code>a</code>", when node
					     "<code>b</code>" has a child named "<code>c</code>", and "<code>c</code>" has a child named "<code>d</code>".
							 Node "<code>b</code>" is the selected node, while nodes "<code>c</code>" and "<code>d</code>" are used as criteria but are not
							 selected.</entry></row>
					<row><entry>/a(/(b|c|d|)/e)[f/g/@something]</entry><entry>Match node "<code>/a/b/e</code>", "<code>/a/c/e</code>", "<code>/a/d/e</code>",
					     or "<code>/a/e</code>" when they also have a child "<code>f</code>" that itself has a child "<code>g</code>" with property
					     "<code>something</code>". None of the nodes may have same-name-sibling indexes.</entry></row>
				</tbody>
			</tgroup>
		</table>
		<para>
			These examples show a few more advanced rules.  Parentheses (i.e., '<code>(</code>' and '<code>)</code>') can be used
		  to define a set of options for names, as shown in the first and third rules.  Whatever part of the selected node's path
		  appears between the parentheses is captured for use within the output path.  Thus, the first input path in the previous table
		  would match node "<code>/a/b</code>", and "b" would be captured and could be used within the output path using "<code>$1</code>",
		  where the number used in the output path identifies the parentheses.
		</para>
		<para>
			Square brackets can also be used to specify criteria on a node's properties or children.  Whatever appears in between the square
			brackets does not appear in the selected node.
		</para>
		<para>
			So far, we've talked about how input paths and output paths are independent of the repository and workspace. However, there are times
			when it's desirable to configure sequencers to only work against content in a specific source and/or specific workspace.
			In these cases, it is possible to specify the repository name and workspace names before the path. For example:
		</para>
		<table frame='all'>
			<title>Input Paths with Source and Workspace Names</title>
			<tgroup cols='2' align='left' colsep='1' rowsep='1'>
	      <colspec colname='c1' colwidth="1*"/>
	      <colspec colname='c2' colwidth="1*"/>
				<thead>
					<row>
			  		<entry>Input Path</entry>
			  		<entry>Description</entry>
					</row>
				</thead>
				<tbody>
					<row><entry>source:default:/a/(b|c|d)</entry><entry>Match nodes in the "<code>default</code>" workspace within the "<code>source</code>"
					     source that are children of the top level node "<code>a</code>" and named "<code>b</code>", 
							 "<code>c</code>" or "<code>d</code>". None of the nodes may have same-name-sibling indexes.</entry></row>
  				<row><entry>:default:/a/(b|c|d)</entry><entry>Match nodes in the "<code>default</code>" workspace within any source
					     source that are children of the top level node "<code>a</code>" and named "<code>b</code>", 
							 "<code>c</code>" or "<code>d</code>". None of the nodes may have same-name-sibling indexes.</entry></row>
  				<row><entry>source::/a/(b|c|d)</entry><entry>Match nodes in any workspace in the "<code>source</code>" source
					     that are children of the top level node "<code>a</code>" and named "<code>b</code>", 
							 "<code>c</code>" or "<code>d</code>". None of the nodes may have same-name-sibling indexes.</entry></row>
  				<row><entry>::/a/(b|c|d)</entry><entry>Match nodes in any within any source
					     source that are children of the top level node "<code>a</code>" and named "<code>b</code>", 
							 "<code>c</code>" or "<code>d</code>". None of the nodes may have same-name-sibling indexes. (This is equivalent to 
							 the path "<code>/a/(b|c|d)</code>".)</entry></row>
				</tbody>
			</tgroup>
		</table>
		<para>
			Again, the rules are pretty straightforward. You can leave off the repository name and workspace name, or you can prepend the path
			with "<code>{sourceNamePattern}:{workspaceNamePattern}:</code>", where "<code>{sourceNamePattern}</code> is a regular-expression
			pattern used to match the applicable source names, and "<code>{workspaceNamePattern}</code> is a regular-expression
			pattern used to match the applicable workspace names. A blank pattern implies any match, and is a shorthand notation for "<code>.*</code>".
			Note that the repository names may not include forward slashes (e.g., '/') or colons (e.g., ':').
		</para>
	  <para>
			Let's go back to the previous code fragment and look at the first path expression:
		</para>
	  <programlisting><![CDATA[  //(*.(jpg|jpeg|gif|bmp|pcx|png)[*])/jcr:content[@jcr:data] => /images/$1 ]]></programlisting>
	  <para>
			This matches a node named "<code>jcr:content</code>" with property "<code>jcr:data</code>" but no siblings with the same name,
	    and that is a child of a node whose name ends with "<code>.jpg</code>", "<code>.jpeg</code>", "<code>.gif</code>", "<code>.bmp</code>", "<code>.pcx</code>", 
	    or  "<code>.png</code>" that may have any same-name-sibling index.  These nodes can appear at any level in the repository.
		  Note how the input path capture the filename (the segment containing the file extension), including any same-name-sibling index.
		  This filename is then used in the output path, which is where the sequenced content is placed.
		</para>
  </sect1>
  <sect1 id="sequencer-library">
    <title>Out-of-the-box Sequencers</title>
    <para>
    	A number of sequencers are already available in ModeShape, and are outlined in detail
      <link linkend="provided-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&amp;mode=hide&amp;pid=12310520&amp;sorter/order=DESC&amp;sorter/field=priority&amp;resolution=-1&amp;component=12311441">more sequencers</ulink>
			in the upcoming releases.
    </para>
  </sect1>
  <sect1 id="custom-sequencers">
	  <title>Creating Custom Sequencers</title>
	  <para>The current release of ModeShape comes with eleven sequencers.  However, it's very easy to create your own
	  sequencers and to then configure ModeShape to use them in your own application.
	  </para>
	  <para>
	  Creating a custom sequencer involves the following steps:
	  <orderedlist>
	    <listitem>
	      <para>Create a Maven 3 project for your sequencer;</para>
	    </listitem>
	    <listitem>
	      <para>Implement the &StreamSequencer; interface with your own implementation, and create unit tests to verify
	      the functionality and expected behavior;</para>
	    </listitem>
	    <listitem>
	      <para>Add the sequencer configuration to the ModeShape &SequencingService; in your application
	      as described in the <link linkend="using_modeshape">previous chapter</link>; and</para>
	    </listitem>
	    <listitem>
	      <para>Deploy the JAR file with your implementation (as well as any dependencies), and make them available to ModeShape
	      in your application.</para>
	    </listitem>
	  </orderedlist>
	  It's that simple.
	  </para>
	  <sect2 id="custom_sequencer_project">
	    <title>Creating the Maven 3 project</title>
	    <para>The first step is to create the Maven 3 project that you can use to compile your code and build the JARs.
	    Maven 3 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 3, but then you'll
	    have to get the required libraries and manage the compiling and building process yourself.</para>
	    <note>
	      <para>ModeShape may provide in the future a Maven archetype for creating sequencer projects.  If you'd find this useful
	        and would like to help create it, please <link linkend="preface">join the community</link>.
				</para>
		    <para>In lieu of a Maven archetype, you may find it easier to start with a small existing sequencer project.
					The <emphasis role="strong">modeshape-sequencer-images</emphasis> project is a small, self-contained sequencer implementation that
		    	has only the minimal dependencies.  
		    	See the Git repository: 
					<ulink url="&GitRepository;/tree/modeshape-&versionNumber;/extensions/modeshape-sequencer-images/">&GitRepository;/tree/modeshape-&versionNumber;/extensions/modeshape-sequencer-images/</ulink>
		    </para>
		  </note>
	    <para>You can create your Maven project any way you'd like.  For examples, see the <ulink url="http://maven.apache.org/guides/getting-started/index.html#How_do_I_make_my_first_Maven_project">Maven 3 documentation</ulink>.  
	    Once you've done that, just add the dependencies in your project's <code>pom.xml</code> dependencies section:</para>
	  	<programlisting language="XML" role="XML"><![CDATA[
<dependency>
  <groupId>org.modeshape</groupId>
  <artifactId>modeshape-graph</artifactId>
  <version>2.8-SNAPSHOT</version>
</dependency>
	 ]]></programlisting>
			<para>These are minimum dependencies required for compiling a sequencer.  Of course, you'll have to add
	  		other dependencies that your sequencer needs.</para>
	  	<para>As for testing, you probably will want to add more dependencies, such as those listed here:</para>
			<programlisting language="XML" role="XML"><![CDATA[
<!-- ModeShape-related unit testing utilities and classes -->
<dependency>
  <groupId>org.modeshape</groupId>
  <artifactId>modeshape-graph</artifactId>
  <version>2.8-SNAPSHOT</version>
  <type>test-jar</type>
  <scope>test</scope>
</dependency>
<dependency>
  <groupId>org.modeshape</groupId>
  <artifactId>modeshape-common</artifactId>
  <version>2.8-SNAPSHOT</version>
  <type>test-jar</type>
  <scope>test</scope>
</dependency>
<!-- Unit testing -->
<dependency>
  <groupId>junit</groupId>
  <artifactId>junit</artifactId>
  <version>4.4</version>
  <scope>test</scope>
</dependency>
<dependency>
	<groupId>org.mockito</groupId>
	<artifactId>mockito-all</artifactId>
	<version>1.8.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.6.1</version>
  <scope>test</scope>
</dependency>
<dependency>
  <groupId>log4j</groupId>
  <artifactId>log4j</artifactId>
  <version>1.2.16</version>
  <scope>test</scope>
</dependency>	 ]]></programlisting>
	  	<para>Testing ModeShape sequencers does not require a JCR repository or the ModeShape services.  (For more detail,
	  		see the <link linkend="testing_custom_sequencers">testing section</link>.)  However, if you want to do
	  		integration testing with a JCR repository and the ModeShape services, you'll need additional dependencies for these libraries.</para>
			<programlisting language="XML" role="XML"><![CDATA[
<!-- ModeShape JCR Repository -->
<dependency>
  <groupId>org.modeshape</groupId>
  <artifactId>modeshape-jcr</artifactId>
  <version>2.8-SNAPSHOT</version>
  <scope>test</scope>
</dependency>
<!-- Java Content Repository API -->
<dependency>
  <groupId>javax.jcr</groupId>
  <artifactId>jcr</artifactId>
  <version>2.0</version>
  <scope>test</scope>
</dependency>
	 ]]></programlisting>
		  <para>
				At this point, your project should be set up correctly, and you're ready to move on to 
			 	write your custom implementation of the &StreamSequencer; interface.  As stated earlier, this should be fairly
				straightforward: process the stream and generate the output that's appropriate for the kind of file being
				sequenced.
			</para>
		  <para>
				Let's look at an example.  Here is the complete code for the &ImageMetadataSequencer; implementation:
			</para>
		  <programlisting>
public class &ImageMetadataSequencer; implements &StreamSequencer; {

		/**
		 * {@inheritDoc}
		 * 
		 * @see StreamSequencer#sequence(InputStream, SequencerOutput, StreamSequencerContext)
		 */
		public void sequence( InputStream stream,
		                      SequencerOutput output,
		                      StreamSequencerContext context ) {

		    ImageMetadata metadata = new ImageMetadata();
		    metadata.setInput(stream);
		    metadata.setDetermineImageNumber(true);
		    metadata.setCollectComments(true);

		    // Process the image stream and extract the metadata ...
		    if (!metadata.check()) {
		        metadata = null;
		    }

		    // Generate the output graph if we found useful metadata ...
		    if (metadata != null) {
		        PathFactory pathFactory = context.getValueFactories().getPathFactory();
		        Path metadataNode = pathFactory.createRelativePath(ImageMetadataLexicon.METADATA_NODE);

		        // Place the image metadata into the output map ...
		        output.setProperty(metadataNode, JcrLexicon.PRIMARY_TYPE, "image:metadata");
		        // output.psetProperty(metadataNode, nameFactory.create(IMAGE_MIXINS), "");
		        output.setProperty(metadataNode, JcrLexicon.MIMETYPE, metadata.getMimeType());
		        // output.setProperty(metadataNode, nameFactory.create(IMAGE_ENCODING), "");
		        output.setProperty(metadataNode, ImageMetadataLexicon.FORMAT_NAME, metadata.getFormatName());
		        output.setProperty(metadataNode, ImageMetadataLexicon.WIDTH, metadata.getWidth());
		        output.setProperty(metadataNode, ImageMetadataLexicon.HEIGHT, metadata.getHeight());
		        output.setProperty(metadataNode, ImageMetadataLexicon.BITS_PER_PIXEL, metadata.getBitsPerPixel());
		        output.setProperty(metadataNode, ImageMetadataLexicon.PROGRESSIVE, metadata.isProgressive());
		        output.setProperty(metadataNode, ImageMetadataLexicon.NUMBER_OF_IMAGES, metadata.getNumberOfImages());
		        output.setProperty(metadataNode, ImageMetadataLexicon.PHYSICAL_WIDTH_DPI, metadata.getPhysicalWidthDpi());
		        output.setProperty(metadataNode, ImageMetadataLexicon.PHYSICAL_HEIGHT_DPI, metadata.getPhysicalHeightDpi());
		        output.setProperty(metadataNode, ImageMetadataLexicon.PHYSICAL_WIDTH_INCHES, metadata.getPhysicalWidthInch());
		        output.setProperty(metadataNode, ImageMetadataLexicon.PHYSICAL_HEIGHT_INCHES, metadata.getPhysicalHeightInch());
		    }
		}
}
</programlisting>
      <para>where the &ImageMetadataLexicon; class contains the &Name; constants and is defined as:</para>
<programlisting>
	/**
	 * A lexicon of names used within the image sequencer.
	 */
	@Immutable
	public class &ImageMetadataLexicon; {

	    public static class Namespace {
	        public static final String URI = "http://www.modeshape.org/images/1.0";
	        public static final String PREFIX = "image";
	    }

	    public static final Name METADATA_NODE = new BasicName(Namespace.URI, "metadata");
	    public static final Name FORMAT_NAME = new BasicName(Namespace.URI, "formatName");
	    public static final Name WIDTH = new BasicName(Namespace.URI, "width");
	    public static final Name HEIGHT = new BasicName(Namespace.URI, "height");
	    public static final Name BITS_PER_PIXEL = new BasicName(Namespace.URI, "bitsPerPixel");
	    public static final Name PROGRESSIVE = new BasicName(Namespace.URI, "progressive");
	    public static final Name NUMBER_OF_IMAGES = new BasicName(Namespace.URI, "numberOfImages");
	    public static final Name PHYSICAL_WIDTH_DPI = new BasicName(Namespace.URI, "physicalWidthDpi");
	    public static final Name PHYSICAL_HEIGHT_DPI = new BasicName(Namespace.URI, "physicalHeightDpi");
	    public static final Name PHYSICAL_WIDTH_INCHES = new BasicName(Namespace.URI, "physicalWidthInches");
	    public static final Name PHYSICAL_HEIGHT_INCHES = new BasicName(Namespace.URI, "physicalHeightInches");

	}
</programlisting>
		  <para>
		    Notice how the image metadata is extracted and the output graph is generated.  A single node is created with the name 
				<code>image:metadata</code>
		    and with the <code>image:metadata</code> node type.  No mixins are defined for the node, but several properties are set on the node
		    using the values obtained from the image metadata.  After this method returns, the constructed graph will be saved to the repository
		    in all of the places defined by its configuration.  (This is why only relative paths are used in the sequencer.)
	    </para>
	  </sect2>
	  <sect2 id="testing_custom_sequencers">
	    <title>Testing custom sequencers</title>
	    <para>The sequencing framework was designed to make testing sequencers much easier.  In particular, the
	    &StreamSequencer; interface does not make use of the JCR API.  So instead of requiring a fully-configured
	    JCR repository and ModeShape system, unit tests for a sequencer can focus on testing that the content is
	    processed correctly and the desired output graph is generated.</para>
	    <note>
	      <para>For a complete example of a sequencer unit test, see the <code>ImageMetadataSequencerTest</code> unit test
	      in the <code>org.modeshape.sequencer.images</code> package of the <code>modeshape-sequencers-image</code> project.
	      </para>
	    </note>
	    <para>The following code fragment shows one way of testing a sequencer, using JUnit 4.4 assertions and 
	      some of the classes made available by ModeShape.  Of course,
	      this example code does not do any error handling and does not make all the assertions a real test would.</para>
	    <programlisting>
&StreamSequencer; sequencer = new &ImageMetadataSequencer;();
&MockSequencerOutput; output = new &MockSequencerOutput;();
&MockSequencerContext; context = new &MockSequencerContext;();
&InputStream; stream = null;
try {
    stream = this.getClass().getClassLoader().getResource("caution.gif").openStream();
    sequencer.sequence(stream,output,context);   // writes to 'output'
    assertThat(output.getPropertyValues("image:metadata", "jcr:primaryType"), 
               is(new Object[] {"image:metadata"}));
    assertThat(output.getPropertyValues("image:metadata", "jcr:mimeType"), 
               is(new Object[] {"image/gif"}));
    // ... make more assertions here
    assertThat(output.hasReferences(), is(false));
} finally {
    stream.close();
}
</programlisting>
	  <para>It's also useful to test that a sequencer produces no output for something it should not understand:</para>
	    <programlisting>
&Sequencer; sequencer = new &ImageMetadataSequencer;();
&MockSequencerOutput; output = new &MockSequencerOutput;();
&MockSequencerContext; context = new &MockSequencerContext;();
&InputStream; stream = null;
try {
    stream = this.getClass().getClassLoader().getResource("caution.pict").openStream();
    sequencer.sequence(stream,output,context);   // writes to 'output'
    assertThat(output.hasProperties(), is(false));
    assertThat(output.hasReferences(), is(false));
} finally {
    stream.close();
}
</programlisting>
	    <para>These are just two simple tests that show ways of testing a sequencer.  Some tests may get quite involved,
	    especially if a lot of output data is produced.  
	    </para>
	    <para>It may also be useful to create some integration tests
	    that <link linkend="using_modeshape">configure ModeShape</link> to use a custom sequencer, and to then upload
	    content using the JCR API, verifying that the custom sequencer did run.  However, remember that ModeShape
	    runs sequencers asynchronously in the background, and you must synchronize your tests to ensure that the
	    sequencers have a chance to run before checking the results.
	    </para>  
	  </sect2>
  </sect1>
	<sect1>
		<title>Summary</title>
		<para>
			In this chapter, we described how ModeShape sequences files as they're uploaded into a repository.  We've also learned 
			in previous chapters about the ModeShape <link linkend="execution-context">execution contexts</link>, 
			<link linkend="graph-model">graph model</link>, and <link linkend="connector-framework">connectors</link>.
			In the <link linkend="jcr-part">next part</link> we'll put all these pieces together to learn how
			to set up a ModeShape repository and access it using the JCR API.
		</para>
	</sect1>
</chapter>
