Author: rhauch
Date: 2008-10-29 14:23:01 -0400 (Wed, 29 Oct 2008)
New Revision: 598
Added:
trunk/extensions/dna-sequencer-xml/
trunk/extensions/dna-sequencer-xml/.classpath
trunk/extensions/dna-sequencer-xml/.project
trunk/extensions/dna-sequencer-xml/pom.xml
trunk/extensions/dna-sequencer-xml/src/
trunk/extensions/dna-sequencer-xml/src/main/
trunk/extensions/dna-sequencer-xml/src/main/java/
trunk/extensions/dna-sequencer-xml/src/main/java/org/
trunk/extensions/dna-sequencer-xml/src/main/java/org/jboss/
trunk/extensions/dna-sequencer-xml/src/main/java/org/jboss/dna/
trunk/extensions/dna-sequencer-xml/src/main/java/org/jboss/dna/sequencer/
trunk/extensions/dna-sequencer-xml/src/main/java/org/jboss/dna/sequencer/xml/
trunk/extensions/dna-sequencer-xml/src/main/java/org/jboss/dna/sequencer/xml/DnaDtdLexicon.java
trunk/extensions/dna-sequencer-xml/src/main/java/org/jboss/dna/sequencer/xml/DnaXmlLexicon.java
trunk/extensions/dna-sequencer-xml/src/main/java/org/jboss/dna/sequencer/xml/XmlSequencer.java
trunk/extensions/dna-sequencer-xml/src/main/java/org/jboss/dna/sequencer/xml/XmlSequencerI18n.java
trunk/extensions/dna-sequencer-xml/src/main/resources/
trunk/extensions/dna-sequencer-xml/src/main/resources/org/
trunk/extensions/dna-sequencer-xml/src/main/resources/org/jboss/
trunk/extensions/dna-sequencer-xml/src/main/resources/org/jboss/dna/
trunk/extensions/dna-sequencer-xml/src/main/resources/org/jboss/dna/sequencer/
trunk/extensions/dna-sequencer-xml/src/main/resources/org/jboss/dna/sequencer/xml/
trunk/extensions/dna-sequencer-xml/src/main/resources/org/jboss/dna/sequencer/xml/XmlSequencerI18n.properties
trunk/extensions/dna-sequencer-xml/src/main/resources/org/jboss/dna/sequencer/xml/xml.cnd
trunk/extensions/dna-sequencer-xml/src/test/
trunk/extensions/dna-sequencer-xml/src/test/java/
trunk/extensions/dna-sequencer-xml/src/test/java/org/
trunk/extensions/dna-sequencer-xml/src/test/java/org/jboss/
trunk/extensions/dna-sequencer-xml/src/test/java/org/jboss/dna/
trunk/extensions/dna-sequencer-xml/src/test/java/org/jboss/dna/sequencer/
trunk/extensions/dna-sequencer-xml/src/test/java/org/jboss/dna/sequencer/xml/
trunk/extensions/dna-sequencer-xml/src/test/java/org/jboss/dna/sequencer/xml/XmlSequencerTest.java
trunk/extensions/dna-sequencer-xml/src/test/resources/
trunk/extensions/dna-sequencer-xml/src/test/resources/CurrencyFormatterExample.mxml
trunk/extensions/dna-sequencer-xml/src/test/resources/Descriptor.1.0.xsd
trunk/extensions/dna-sequencer-xml/src/test/resources/jackrabbitInMemoryTestRepositoryConfig.xml
trunk/extensions/dna-sequencer-xml/src/test/resources/log4j.properties
trunk/extensions/dna-sequencer-xml/src/test/resources/master.xml
trunk/extensions/dna-sequencer-xml/src/test/resources/plugin.xml
Removed:
trunk/dna-graph/src/main/java/org/jboss/dna/graph/xml/DnaDtdLexicon.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/xml/DnaXmlLexicon.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/xml/XmlSequencer.java
trunk/dna-graph/src/test/java/org/jboss/dna/graph/xml/XmlSequencerTest.java
trunk/dna-graph/src/test/resources/CurrencyFormatterExample.mxml
trunk/dna-graph/src/test/resources/Descriptor.1.0.xsd
trunk/dna-graph/src/test/resources/jackrabbitInMemoryTestRepositoryConfig.xml
trunk/dna-graph/src/test/resources/master.xml
trunk/dna-graph/src/test/resources/plugin.xml
Modified:
trunk/dna-graph/src/main/java/org/jboss/dna/graph/GraphI18n.java
trunk/dna-graph/src/main/resources/org/jboss/dna/graph/GraphI18n.properties
trunk/dna-graph/src/test/java/org/jboss/dna/graph/GraphImporterTest.java
trunk/pom.xml
Log:
DNA-243 - Change (and move) XML Sequencer to use more general-purpose XML importer
http://jira.jboss.com/jira/browse/DNA-243
Moved the XmlSequencer into a separate extension project.
Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/GraphI18n.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/GraphI18n.java 2008-10-29 17:48:23
UTC (rev 597)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/GraphI18n.java 2008-10-29 18:23:01
UTC (rev 598)
@@ -75,13 +75,6 @@
public static I18n actualNewLocationMustHaveSameParentAsOldLocation;
public static I18n actualNewLocationMustHaveSameNameAsRequest;
- // XML Sequencer
- public static I18n errorSequencingXmlDocument;
- public static I18n fatalErrorSequencingXmlDocument;
- public static I18n sequencingXmlDocument;
- public static I18n canceledSequencingXmlDocument;
- public static I18n warningSequencingXmlDocument;
-
public static I18n errorImportingContent;
public static I18n unableToFindRepositorySourceWithName;
Deleted: trunk/dna-graph/src/main/java/org/jboss/dna/graph/xml/DnaDtdLexicon.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/xml/DnaDtdLexicon.java 2008-10-29
17:48:23 UTC (rev 597)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/xml/DnaDtdLexicon.java 2008-10-29
18:23:01 UTC (rev 598)
@@ -1,42 +0,0 @@
-/*
- * JBoss, Home of Professional Open Source.
- * Copyright 2008, Red Hat Middleware LLC, and individual contributors
- * as indicated by the @author tags. See the copyright.txt file in the
- * distribution for a full listing of individual contributors.
- *
- * This is free software; you can redistribute it and/or modify it
- * 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.
- *
- * This software 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.xml;
-
-import org.jboss.dna.graph.properties.Name;
-import org.jboss.dna.graph.properties.basic.BasicName;
-
-/**
- * @author Randall Hauch
- */
-public class DnaDtdLexicon {
-
- public static class Namespace {
- public static final String URI = "http://www.jboss.org/dna/dtd/1.0";
- public static final String PREFIX = "dnadtd";
- }
-
- public static final Name NAME = new BasicName(Namespace.URI, "name");
- public static final Name PUBLIC_ID = new BasicName(Namespace.URI,
"publicId");
- public static final Name SYSTEM_ID = new BasicName(Namespace.URI,
"systemId");
- public static final Name VALUE = new BasicName(Namespace.URI, "value");
- public static final Name ENTITY = new BasicName(Namespace.URI, "entity");
-}
Deleted: trunk/dna-graph/src/main/java/org/jboss/dna/graph/xml/DnaXmlLexicon.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/xml/DnaXmlLexicon.java 2008-10-29
17:48:23 UTC (rev 597)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/xml/DnaXmlLexicon.java 2008-10-29
18:23:01 UTC (rev 598)
@@ -1,46 +0,0 @@
-/*
- * JBoss, Home of Professional Open Source.
- * Copyright 2008, Red Hat Middleware LLC, and individual contributors
- * as indicated by the @author tags. See the copyright.txt file in the
- * distribution for a full listing of individual contributors.
- *
- * This is free software; you can redistribute it and/or modify it
- * 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.
- *
- * This software 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.xml;
-
-import org.jboss.dna.graph.properties.Name;
-import org.jboss.dna.graph.properties.basic.BasicName;
-
-/**
- * @author Randall Hauch
- */
-public class DnaXmlLexicon {
-
- public static class Namespace {
- public static final String URI = "http://www.jboss.org/dna/xml/1.0";
- public static final String PREFIX = "dnaxml";
- }
-
- public static final Name CDATA = new BasicName(Namespace.URI, "cData");
- public static final Name CDATA_CONTENT = new BasicName(Namespace.URI,
"cDataContent");
- public static final Name COMMENT = new BasicName(Namespace.URI,
"comment");
- public static final Name COMMENT_CONTENT = new BasicName(Namespace.URI,
"commentContent");
- public static final Name DOCUMENT = new BasicName(Namespace.URI,
"document");
- public static final Name ELEMENT_CONTENT = new BasicName(Namespace.URI,
"elementContent");
- public static final Name PROCESSING_INSTRUCTION = new BasicName(Namespace.URI,
"processingInstruction");
- public static final Name PROCESSING_INSTRUCTION_CONTENT = new
BasicName(Namespace.URI, "processingInstructionContent");
- public static final Name TARGET = new BasicName(Namespace.URI, "target");
-}
Deleted: trunk/dna-graph/src/main/java/org/jboss/dna/graph/xml/XmlSequencer.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/xml/XmlSequencer.java 2008-10-29
17:48:23 UTC (rev 597)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/xml/XmlSequencer.java 2008-10-29
18:23:01 UTC (rev 598)
@@ -1,703 +0,0 @@
-/*
- * JBoss, Home of Professional Open Source.
- * Copyright 2008, Red Hat Middleware LLC, and individual contributors
- * as indicated by the @author tags. See the copyright.txt file in the
- * distribution for a full listing of individual contributors.
- *
- * This is free software; you can redistribute it and/or modify it
- * 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.
- *
- * This software 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.xml;
-
-import java.io.InputStream;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Map;
-import org.jboss.dna.common.monitor.ProgressMonitor;
-import org.jboss.dna.common.util.StringUtil;
-import org.jboss.dna.graph.GraphI18n;
-import org.jboss.dna.graph.JcrLexicon;
-import org.jboss.dna.graph.properties.Name;
-import org.jboss.dna.graph.properties.NameFactory;
-import org.jboss.dna.graph.properties.NamespaceRegistry;
-import org.jboss.dna.graph.properties.Path;
-import org.jboss.dna.graph.sequencers.SequencerContext;
-import org.jboss.dna.graph.sequencers.SequencerOutput;
-import org.jboss.dna.graph.sequencers.StreamSequencer;
-import org.xml.sax.Attributes;
-import org.xml.sax.InputSource;
-import org.xml.sax.SAXException;
-import org.xml.sax.SAXNotRecognizedException;
-import org.xml.sax.SAXNotSupportedException;
-import org.xml.sax.SAXParseException;
-import org.xml.sax.XMLReader;
-import org.xml.sax.ext.DefaultHandler2;
-import org.xml.sax.helpers.XMLReaderFactory;
-
-/**
- * @author John Verhaeg
- */
-public class XmlSequencer implements StreamSequencer {
-
- private static final String DEFAULT_PRIMARY_TYPE = "nt:unstructured";
- private static final String DECL_HANDLER_FEATURE =
"http://xml.org/sax/properties/declaration-handler";
- private static final String ENTITY_RESOLVER_2_FEATURE =
"http://xml.org/sax/features/use-entity-resolver2";
- private static final String LEXICAL_HANDLER_FEATURE =
"http://xml.org/sax/properties/lexical-handler";
- private static final String RESOLVE_DTD_URIS_FEATURE =
"http://xml.org/sax/features/resolve-dtd-uris";
- private static final String LOAD_EXTERNAL_DTDS_FEATURE =
"http://apache.org/xml/features/nonvalidating/load-external-dtd";
-
- /**
- * {@inheritDoc}
- *
- * @see org.jboss.dna.graph.sequencers.StreamSequencer#sequence(InputStream,
SequencerOutput, SequencerContext,
- * ProgressMonitor)
- */
- public void sequence( InputStream stream,
- SequencerOutput output,
- SequencerContext context,
- ProgressMonitor monitor ) {
- monitor.beginTask(100.0, GraphI18n.sequencingXmlDocument);
- XMLReader reader;
- try {
- reader = XMLReaderFactory.createXMLReader();
- Handler handler = new Handler(output, context, monitor);
- reader.setContentHandler(handler);
- reader.setErrorHandler(handler);
- // Ensure handler acting as entity resolver 2
- reader.setProperty(DECL_HANDLER_FEATURE, handler);
- // Ensure handler acting as lexical handler
- reader.setProperty(LEXICAL_HANDLER_FEATURE, handler);
- // Ensure handler acting as entity resolver 2
- setFeature(reader, ENTITY_RESOLVER_2_FEATURE, true);
- // Prevent loading of external DTDs
- setFeature(reader, LOAD_EXTERNAL_DTDS_FEATURE, false);
- // Prevent the resolving of DTD entities into fully-qualified URIS
- setFeature(reader, RESOLVE_DTD_URIS_FEATURE, false);
- // Parse XML document
- reader.parse(new InputSource(stream));
- } catch (Exception error) {
- context.getLogger(getClass()).error(error,
GraphI18n.fatalErrorSequencingXmlDocument, error);
- monitor.getProblems().addError(error,
GraphI18n.fatalErrorSequencingXmlDocument, error);
- } finally {
- monitor.done();
- }
- }
-
- /**
- * Sets the reader's named feature to the supplied value, only if the feature is
not already set to that value. This method
- * does nothing if the feature is not known to the reader.
- *
- * @param reader the reader; may not be null
- * @param featureName the name of the feature; may not be null
- * @param value the value for the feature
- */
- private void setFeature( XMLReader reader,
- String featureName,
- boolean value ) {
- try {
- if (reader.getFeature(featureName) != value) {
- reader.setFeature(featureName, value);
- }
- } catch (SAXNotRecognizedException meansFeatureNotRecognized) {
- } catch (SAXNotSupportedException meansFeatureNotSupported) {
- }
- }
-
- private final class Handler extends DefaultHandler2 {
-
- private final SequencerOutput output;
- private final SequencerContext context;
- private final ProgressMonitor monitor;
-
- private double progress;
-
- private Path path; // The DNA path of the node representing the current XML
element
-
- // Cached instances of the name factory and commonly referenced names
- private final NameFactory nameFactory;
- private Name defaultPrimaryType;
-
- // Recursive map used to track the number of occurrences of names for elements
under a particular path
- private Map<Name, List<IndexedName>> nameToIndexedNamesMap = new
HashMap<Name, List<IndexedName>>();
-
- // The stack of recursive maps being processed, with the head entry being the map
for the current path
- private final LinkedList<Map<Name, List<IndexedName>>>
nameToIndexedNamesMapStack = new LinkedList<Map<Name,
List<IndexedName>>>();
-
- // The stack of XML namespace in scope, with the head entry being namespace of
the closest ancestor element declaring a
- // namespace.
- private final LinkedList<String> nsStack = new LinkedList<String>();
-
- // Builder used to concatenate concurrent lines of CDATA into a single value.
- private StringBuilder cDataBuilder;
-
- // Builder used to concatenate concurrent lines of element content and entity
evaluations into a single value.
- private StringBuilder contentBuilder;
-
- // The entity being processed
- private String entity;
-
- Handler( SequencerOutput output,
- SequencerContext context,
- ProgressMonitor monitor ) {
- assert output != null;
- assert monitor != null;
- assert context != null;
- this.output = output;
- this.context = context;
- this.monitor = monitor;
- // Initialize path to a an empty path relative to the SequencerOutput's
target path.
- path = context.getValueFactories().getPathFactory().createRelativePath();
- // Cache name factory since it is frequently used
- nameFactory = context.getValueFactories().getNameFactory();
- }
-
- /**
- * <p>
- * {@inheritDoc}
- * </p>
- *
- * @see org.xml.sax.ext.DefaultHandler2#attributeDecl(java.lang.String,
java.lang.String, java.lang.String,
- * java.lang.String, java.lang.String)
- */
- @Override
- public void attributeDecl( String name,
- String name2,
- String type,
- String mode,
- String value ) throws SAXException {
- stopIfCancelled();
- }
-
- /**
- * <p>
- * {@inheritDoc}
- * </p>
- *
- * @see org.xml.sax.helpers.DefaultHandler#characters(char[], int, int)
- */
- @Override
- public void characters( char[] ch,
- int start,
- int length ) throws SAXException {
- stopIfCancelled();
- String content = String.valueOf(ch, start, length);
- // Check if data should be appended to previously parsed CDATA
- if (cDataBuilder == null) {
- // If content is for an entity, replace with entity reference
- if (entity != null) {
- content = '&' + entity + ';';
- }
- // Check if first line of content
- if (contentBuilder == null) {
- contentBuilder = new StringBuilder(content);
- } else {
- // Append additional lines or entity evaluations to previous content,
separated by a space
- if (entity == null) {
- contentBuilder.append(' ');
- }
- contentBuilder.append(content);
- // Text within builder will be output when another element or CDATA
is encountered
- }
- } else {
- cDataBuilder.append(ch, start, length);
- // Text within builder will be output at the end of CDATA
- }
- updateProgress();
- }
-
- /**
- * <p>
- * {@inheritDoc}
- * </p>
- *
- * @see org.xml.sax.ext.DefaultHandler2#comment(char[], int, int)
- */
- @Override
- public void comment( char[] ch,
- int start,
- int length ) throws SAXException {
- stopIfCancelled();
- // Output separate nodes for each comment since multiple are allowed
- startElement(DnaXmlLexicon.COMMENT);
- output.setProperty(path, JcrLexicon.PRIMARY_TYPE, DnaXmlLexicon.COMMENT);
- output.setProperty(path, DnaXmlLexicon.COMMENT_CONTENT, String.valueOf(ch,
start, length));
- endElement();
- updateProgress();
- }
-
- /**
- * <p>
- * {@inheritDoc}
- * </p>
- *
- * @see org.xml.sax.ext.DefaultHandler2#elementDecl(java.lang.String,
java.lang.String)
- */
- @Override
- public void elementDecl( String name,
- String model ) throws SAXException {
- stopIfCancelled();
- }
-
- /**
- * <p>
- * {@inheritDoc}
- * </p>
- *
- * @see org.xml.sax.ext.DefaultHandler2#endCDATA()
- */
- @Override
- public void endCDATA() throws SAXException {
- stopIfCancelled();
- // Output CDATA built in characters() method
- output.setProperty(path, DnaXmlLexicon.CDATA_CONTENT,
cDataBuilder.toString());
- endElement();
- // Null-out builder to free memory
- cDataBuilder = null;
- updateProgress();
- }
-
- private void endContent() {
- if (contentBuilder != null) {
- // Normalize content
- String content = StringUtil.normalize(contentBuilder.toString());
- // Null-out builder to setup for subsequent content.
- // Must be done before call to startElement below to prevent infinite
loop.
- contentBuilder = null;
- // Skip if nothing in content but whitespace
- if (content.length() > 0) {
- // Create separate node for each content entry since entries can be
interspersed amongst child elements
- startElement(DnaXmlLexicon.ELEMENT_CONTENT);
- output.setProperty(path, JcrLexicon.PRIMARY_TYPE,
DnaXmlLexicon.ELEMENT_CONTENT);
- output.setProperty(path, DnaXmlLexicon.ELEMENT_CONTENT, content);
- endElement();
- }
- }
- }
-
- /**
- * <p>
- * {@inheritDoc}
- * </p>
- *
- * @see org.xml.sax.helpers.DefaultHandler#endDocument()
- */
- @Override
- public void endDocument() throws SAXException {
- stopIfCancelled();
- }
-
- /**
- * <p>
- * {@inheritDoc}
- * </p>
- *
- * @see org.xml.sax.ext.DefaultHandler2#endDTD()
- */
- @Override
- public void endDTD() throws SAXException {
- stopIfCancelled();
- }
-
- private void endElement() {
- // Recover parent's path, namespace, and indexedName map, clearing the
ended element's map to free memory
- path = path.getParent();
- nameToIndexedNamesMap.clear();
- nameToIndexedNamesMap = nameToIndexedNamesMapStack.removeFirst();
- nsStack.removeFirst();
- }
-
- /**
- * <p>
- * {@inheritDoc}
- * </p>
- *
- * @see org.xml.sax.helpers.DefaultHandler#endElement(java.lang.String,
java.lang.String, java.lang.String)
- */
- @Override
- public void endElement( String uri,
- String localName,
- String name ) throws SAXException {
- stopIfCancelled();
- // Check if content still needs to be output
- endContent();
- endElement();
- updateProgress();
- }
-
- /**
- * <p>
- * {@inheritDoc}
- * </p>
- *
- * @see org.xml.sax.ext.DefaultHandler2#endEntity(java.lang.String)
- */
- @Override
- public void endEntity( String name ) throws SAXException {
- stopIfCancelled();
- entity = null;
- updateProgress();
- }
-
- /**
- * <p>
- * {@inheritDoc}
- * </p>
- *
- * @see org.xml.sax.helpers.DefaultHandler#error(org.xml.sax.SAXParseException)
- */
- @Override
- public void error( SAXParseException error ) {
- context.getLogger(XmlSequencer.class).error(error,
GraphI18n.errorSequencingXmlDocument, error);
- monitor.getProblems().addError(error, GraphI18n.errorSequencingXmlDocument,
error);
- }
-
- /**
- * <p>
- * {@inheritDoc}
- * </p>
- *
- * @see org.xml.sax.ext.DefaultHandler2#externalEntityDecl(java.lang.String,
java.lang.String, java.lang.String)
- */
- @Override
- public void externalEntityDecl( String name,
- String publicId,
- String systemId ) throws SAXException {
- stopIfCancelled();
- // Add "synthetic" entity container to path to help prevent name
collisions with XML elements
- Name entityName = DnaDtdLexicon.ENTITY;
- startElement(entityName);
- output.setProperty(path, JcrLexicon.PRIMARY_TYPE, entityName);
- output.setProperty(path, nameFactory.create(DnaDtdLexicon.NAME), name);
- output.setProperty(path, nameFactory.create(DnaDtdLexicon.PUBLIC_ID),
publicId);
- output.setProperty(path, nameFactory.create(DnaDtdLexicon.SYSTEM_ID),
systemId);
- endElement();
- updateProgress();
- }
-
- /**
- * <p>
- * {@inheritDoc}
- * </p>
- *
- * @see
org.xml.sax.helpers.DefaultHandler#fatalError(org.xml.sax.SAXParseException)
- */
- @Override
- public void fatalError( SAXParseException error ) {
- context.getLogger(XmlSequencer.class).error(error,
GraphI18n.fatalErrorSequencingXmlDocument, error);
- monitor.getProblems().addError(error,
GraphI18n.fatalErrorSequencingXmlDocument, error);
- }
-
- private Name getDefaultPrimaryType() {
- if (defaultPrimaryType == null) {
- defaultPrimaryType = nameFactory.create(DEFAULT_PRIMARY_TYPE);
- }
- return defaultPrimaryType;
- }
-
- /**
- * <p>
- * {@inheritDoc}
- * </p>
- *
- * @see org.xml.sax.helpers.DefaultHandler#ignorableWhitespace(char[], int, int)
- */
- @Override
- public void ignorableWhitespace( char[] ch,
- int start,
- int length ) throws SAXException {
- stopIfCancelled();
- }
-
- /**
- * <p>
- * {@inheritDoc}
- * </p>
- *
- * @see org.xml.sax.ext.DefaultHandler2#internalEntityDecl(java.lang.String,
java.lang.String)
- */
- @Override
- public void internalEntityDecl( String name,
- String value ) throws SAXException {
- stopIfCancelled();
- // Add "synthetic" entity container to path to help prevent name
collisions with XML elements
- Name entityName = DnaDtdLexicon.ENTITY;
- startElement(entityName);
- output.setProperty(path, JcrLexicon.PRIMARY_TYPE, entityName);
- output.setProperty(path, DnaDtdLexicon.NAME, name);
- output.setProperty(path, DnaDtdLexicon.VALUE, value);
- endElement();
- updateProgress();
- }
-
- /**
- * <p>
- * {@inheritDoc}
- * </p>
- *
- * @see org.xml.sax.helpers.DefaultHandler#notationDecl(java.lang.String,
java.lang.String, java.lang.String)
- */
- @Override
- public void notationDecl( String name,
- String publicId,
- String systemId ) throws SAXException {
- stopIfCancelled();
- }
-
- /**
- * <p>
- * {@inheritDoc}
- * </p>
- *
- * @see
org.xml.sax.helpers.DefaultHandler#processingInstruction(java.lang.String,
java.lang.String)
- */
- @Override
- public void processingInstruction( String target,
- String data ) throws SAXException {
- stopIfCancelled();
- // Output separate nodes for each instruction since multiple are allowed
- Name name = DnaXmlLexicon.PROCESSING_INSTRUCTION;
- startElement(name);
- output.setProperty(path, JcrLexicon.PRIMARY_TYPE, name);
- output.setProperty(path, DnaXmlLexicon.TARGET, target);
- output.setProperty(path, DnaXmlLexicon.PROCESSING_INSTRUCTION_CONTENT,
data);
- endElement();
- updateProgress();
- }
-
- /**
- * <p>
- * {@inheritDoc}
- * </p>
- *
- * @see org.xml.sax.helpers.DefaultHandler#skippedEntity(java.lang.String)
- */
- @Override
- public void skippedEntity( String name ) throws SAXException {
- stopIfCancelled();
- }
-
- /**
- * <p>
- * {@inheritDoc}
- * </p>
- *
- * @see org.xml.sax.ext.DefaultHandler2#startCDATA()
- */
- @Override
- public void startCDATA() throws SAXException {
- stopIfCancelled();
- // Output separate nodes for each CDATA since multiple are allowed
- startElement(DnaXmlLexicon.CDATA);
- // Prepare builder for concatenating consecutive lines of CDATA
- cDataBuilder = new StringBuilder();
- updateProgress();
- }
-
- /**
- * <p>
- * {@inheritDoc}
- * </p>
- *
- * @see org.xml.sax.helpers.DefaultHandler#startDocument()
- */
- @Override
- public void startDocument() throws SAXException {
- stopIfCancelled();
- output.setProperty(path, JcrLexicon.PRIMARY_TYPE, DnaXmlLexicon.DOCUMENT);
- updateProgress();
- }
-
- /**
- * <p>
- * {@inheritDoc}
- * </p>
- *
- * @see org.xml.sax.ext.DefaultHandler2#startDTD(java.lang.String,
java.lang.String, java.lang.String)
- */
- @Override
- public void startDTD( String name,
- String publicId,
- String systemId ) throws SAXException {
- stopIfCancelled();
- output.setProperty(path, DnaDtdLexicon.NAME, name);
- output.setProperty(path, DnaDtdLexicon.PUBLIC_ID, publicId);
- output.setProperty(path, DnaDtdLexicon.SYSTEM_ID, systemId);
- updateProgress();
- }
-
- private void startElement( Name name ) {
- // Check if content still needs to be output
- endContent();
- // Add name to list of indexed names for this element to ensure we use the
correct index (which is the size of the
- // list)
- List<IndexedName> indexedNames = nameToIndexedNamesMap.get(name);
- if (indexedNames == null) {
- indexedNames = new ArrayList<IndexedName>();
- nameToIndexedNamesMap.put(name, indexedNames);
- }
- IndexedName indexedName = new IndexedName();
- indexedNames.add(indexedName);
- // Add element name and the appropriate index to the path.
- // Per the JCR spec, the index must be relative to same-name sibling nodes
- path = context.getValueFactories().getPathFactory().create(path, name,
indexedNames.size());
- path = path.getNormalizedPath();
- // Add the indexed name map to the stack and set the current map to the new
element's map
- nameToIndexedNamesMapStack.addFirst(nameToIndexedNamesMap);
- nameToIndexedNamesMap = indexedName.nameToIndexedNamesMap;
- // Set the current namespace to whatever is declared by this element, or if
not declared, to its nearest ancestor that
- // does declare a namespace.
- String ns = name.getNamespaceUri();
- if (ns.length() == 0) {
- nsStack.addFirst(nsStack.isEmpty() ? "" : nsStack.getFirst());
- } else {
- nsStack.addFirst(ns);
- }
- }
-
- /**
- * <p>
- * {@inheritDoc}
- * </p>
- *
- * @see org.xml.sax.helpers.DefaultHandler#startElement(java.lang.String,
java.lang.String, java.lang.String,
- * org.xml.sax.Attributes)
- */
- @Override
- public void startElement( String uri,
- String localName,
- String name,
- Attributes attributes ) throws SAXException {
- stopIfCancelled();
- // Look for the "jcr:name" attribute, and use that if it's
there
- Name type = getDefaultPrimaryType();
- Name nameObj = nameFactory.create(name);
- for (int ndx = 0, len = attributes.getLength(); ndx < len; ++ndx) {
- String ns = attributes.getURI(ndx);
- String attrLocalName = attributes.getLocalName(ndx);
- Object value = attributes.getValue(ndx);
- String jcrNsUri =
context.getNamespaceRegistry().getNamespaceForPrefix("jcr");
- if (jcrNsUri != null && jcrNsUri.equals(ns) &&
attrLocalName.equals("name")) {
- nameObj = nameFactory.create(value);
- break;
- }
- }
- startElement(nameObj);
- output.setProperty(path, JcrLexicon.PRIMARY_TYPE, type);
- // Output this element's attributes using the attribute's namespace,
if supplied, or the current namespace in scope.
- String inheritedNs = nsStack.getFirst();
- for (int ndx = 0, len = attributes.getLength(); ndx < len; ++ndx) {
- String ns = attributes.getURI(ndx);
- String attrLocalName = attributes.getLocalName(ndx);
- Object value = attributes.getValue(ndx);
- String jcrNsUri =
context.getNamespaceRegistry().getNamespaceForPrefix("jcr");
- if (jcrNsUri != null && jcrNsUri.equals(ns) &&
attrLocalName.equals("primaryType")) {
- value = nameFactory.create(value);
- }
- if (jcrNsUri != null && jcrNsUri.equals(ns) &&
attrLocalName.equals("name")) {
- continue;
- }
- output.setProperty(path, nameFactory.create(ns.length() == 0 ?
inheritedNs : ns, attrLocalName), value);
- }
- updateProgress();
- }
-
- /**
- * <p>
- * {@inheritDoc}
- * </p>
- *
- * @see org.xml.sax.ext.DefaultHandler2#startEntity(java.lang.String)
- */
- @Override
- public void startEntity( String name ) throws SAXException {
- stopIfCancelled();
- entity = name;
- updateProgress();
- }
-
- /**
- * <p>
- * {@inheritDoc}
- * </p>
- *
- * @see org.xml.sax.helpers.DefaultHandler#startPrefixMapping(java.lang.String,
java.lang.String)
- */
- @Override
- public void startPrefixMapping( String prefix,
- String uri ) throws SAXException {
- stopIfCancelled();
- // Register any unregistered namespaces
- NamespaceRegistry registry = context.getNamespaceRegistry();
- if (!registry.isRegisteredNamespaceUri(uri)) {
- registry.register(prefix, uri);
- }
- updateProgress();
- }
-
- private void stopIfCancelled() throws SAXException {
- if (monitor.isCancelled()) {
- throw new SAXException(GraphI18n.canceledSequencingXmlDocument.text());
- }
- }
-
- /**
- * <p>
- * {@inheritDoc}
- * </p>
- *
- * @see org.xml.sax.helpers.DefaultHandler#unparsedEntityDecl(java.lang.String,
java.lang.String, java.lang.String,
- * java.lang.String)
- */
- @Override
- public void unparsedEntityDecl( String name,
- String publicId,
- String systemId,
- String notationName ) throws SAXException {
- stopIfCancelled();
- }
-
- private void updateProgress() {
- if (progress == 100.0) {
- progress = 1;
- } else {
- progress++;
- }
- monitor.worked(progress);
- }
-
- /**
- * <p>
- * {@inheritDoc}
- * </p>
- *
- * @see
org.xml.sax.helpers.DefaultHandler#warning(org.xml.sax.SAXParseException)
- */
- @Override
- public void warning( SAXParseException warning ) {
- context.getLogger(XmlSequencer.class).warn(warning,
GraphI18n.warningSequencingXmlDocument);
- monitor.getProblems().addWarning(warning,
GraphI18n.warningSequencingXmlDocument, warning);
- }
- }
-
- private class IndexedName {
-
- Map<Name, List<IndexedName>> nameToIndexedNamesMap = new
HashMap<Name, List<IndexedName>>();
-
- IndexedName() {
- }
- }
-}
Modified: trunk/dna-graph/src/main/resources/org/jboss/dna/graph/GraphI18n.properties
===================================================================
--- trunk/dna-graph/src/main/resources/org/jboss/dna/graph/GraphI18n.properties 2008-10-29
17:48:23 UTC (rev 597)
+++ trunk/dna-graph/src/main/resources/org/jboss/dna/graph/GraphI18n.properties 2008-10-29
18:23:01 UTC (rev 598)
@@ -62,12 +62,6 @@
actualNewLocationMustHaveSameParentAsOldLocation = The new location of {0} must be a
sibling of the old location of {1}
actualNewLocationMustHaveSameNameAsRequest = The new location of {0} must have the same
name as in the request ({1})
-errorSequencingXmlDocument = An error was received while sequencing XML: {0}
-fatalErrorSequencingXmlDocument = A fatal error was received while sequencing XML: {0}
-sequencingXmlDocument = Sequencing XML
-canceledSequencingXmlDocument = Canceled sequencing XML
-warningSequencingXmlDocument = A warning was received while sequencing XML: {0}
-
errorImportingContent = Error importing {0} content from {1}
unableToFindRepositorySourceWithName = Unable to find a repository source named
"{0}"
Modified: trunk/dna-graph/src/test/java/org/jboss/dna/graph/GraphImporterTest.java
===================================================================
--- trunk/dna-graph/src/test/java/org/jboss/dna/graph/GraphImporterTest.java 2008-10-29
17:48:23 UTC (rev 597)
+++ trunk/dna-graph/src/test/java/org/jboss/dna/graph/GraphImporterTest.java 2008-10-29
18:23:01 UTC (rev 598)
@@ -47,8 +47,6 @@
import org.jboss.dna.graph.requests.CompositeRequest;
import org.jboss.dna.graph.requests.CreateNodeRequest;
import org.jboss.dna.graph.requests.Request;
-import org.jboss.dna.graph.xml.DnaDtdLexicon;
-import org.jboss.dna.graph.xml.DnaXmlLexicon;
import org.junit.Before;
import org.junit.Test;
import org.mockito.MockitoAnnotations;
@@ -76,8 +74,6 @@
xmlContent = new
File("src/test/resources/repositoryImporterTestData1.xml").toURI();
context = new BasicExecutionContext();
context.getNamespaceRegistry().register(DnaLexicon.Namespace.PREFIX,
DnaLexicon.Namespace.URI);
- context.getNamespaceRegistry().register(DnaXmlLexicon.Namespace.PREFIX,
DnaXmlLexicon.Namespace.URI);
- context.getNamespaceRegistry().register(DnaDtdLexicon.Namespace.PREFIX,
DnaDtdLexicon.Namespace.URI);
context.getNamespaceRegistry().register("jcr",
"http://www.jcp.org/jcr/1.0");
context.getNamespaceRegistry().register("nt",
"http://www.jcp.org/jcr/nt/1.0");
sourceName = "sourceA";
Deleted: trunk/dna-graph/src/test/java/org/jboss/dna/graph/xml/XmlSequencerTest.java
===================================================================
--- trunk/dna-graph/src/test/java/org/jboss/dna/graph/xml/XmlSequencerTest.java 2008-10-29
17:48:23 UTC (rev 597)
+++ trunk/dna-graph/src/test/java/org/jboss/dna/graph/xml/XmlSequencerTest.java 2008-10-29
18:23:01 UTC (rev 598)
@@ -1,221 +0,0 @@
-/*
- * JBoss, Home of Professional Open Source.
- * Copyright 2008, Red Hat Middleware LLC, and individual contributors
- * as indicated by the @author tags. See the copyright.txt file in the
- * distribution for a full listing of individual contributors.
- *
- * This is free software; you can redistribute it and/or modify it
- * 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.
- *
- * This software 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.xml;
-
-import static org.hamcrest.core.Is.is;
-import static org.hamcrest.core.IsInstanceOf.instanceOf;
-import static org.hamcrest.core.IsNull.notNullValue;
-import static org.junit.Assert.assertThat;
-import java.io.IOException;
-import java.io.InputStream;
-import java.net.URL;
-import org.jboss.dna.common.monitor.ProgressMonitor;
-import org.jboss.dna.common.monitor.SimpleProgressMonitor;
-import org.jboss.dna.graph.properties.Name;
-import org.jboss.dna.graph.sequencers.MockSequencerContext;
-import org.jboss.dna.graph.sequencers.MockSequencerOutput;
-import org.jboss.dna.graph.sequencers.SequencerContext;
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-
-/**
- * @author John Verhaeg
- */
-public class XmlSequencerTest {
-
- private static final String CDATA = "dnaxml:cData";
- private static final String CDATA_CONTENT = "dnaxml:cDataContent";
- private static final String COMMENT = "dnaxml:comment";
- private static final String COMMENT_CONTENT = "dnaxml:commentContent";
- private static final String DOCUMENT = "dnaxml:document";
- private static final String DTD_NAME = "dnadtd:name";
- private static final String DTD_PUBLIC_ID = "dnadtd:publicId";
- private static final String DTD_SYSTEM_ID = "dnadtd:systemId";
- private static final String DTD_VALUE = "dnadtd:value";
- private static final String ELEMENT_CONTENT = "dnaxml:elementContent";
- private static final String ENTITY = "dnadtd:entity";
- private static final String PI = "dnaxml:processingInstruction";
- private static final String PI_CONTENT =
"dnaxml:processingInstructionContent";
- private static final String TARGET = "dnaxml:target";
-
- private XmlSequencer sequencer;
- private InputStream stream;
- private MockSequencerOutput output;
- private ProgressMonitor monitor;
- private URL xml1;
- private URL xml2;
- private URL xml3;
- private URL xml4;
- private URL xsd;
- private SequencerContext context;
-
- @Before
- public void beforeEach() {
- sequencer = new XmlSequencer();
- context = new MockSequencerContext();
- output = new MockSequencerOutput(context);
- monitor = new SimpleProgressMonitor("Test activity");
- xml1 =
this.getClass().getClassLoader().getResource("jackrabbitInMemoryTestRepositoryConfig.xml");
- assertThat(xml1, is(notNullValue()));
- xml2 = this.getClass().getClassLoader().getResource("master.xml");
- assertThat(xml2, is(notNullValue()));
- xml3 =
this.getClass().getClassLoader().getResource("CurrencyFormatterExample.mxml");
- assertThat(xml3, is(notNullValue()));
- xml4 = this.getClass().getClassLoader().getResource("plugin.xml");
- assertThat(xml4, is(notNullValue()));
- xsd =
this.getClass().getClassLoader().getResource("Descriptor.1.0.xsd");
- assertThat(xsd, is(notNullValue()));
- }
-
- @After
- public void afterEach() throws Exception {
- if (stream != null) {
- try {
- stream.close();
- } finally {
- stream = null;
- }
- }
- }
-
- @Test
- public void shouldSequenceXml() throws IOException {
- verifyDocument(xml1);
- verifyName(COMMENT + "[1]", "jcr:primaryType", COMMENT);
- String text = verify(COMMENT + "[1]", COMMENT_CONTENT, String.class);
- assertThat(text.startsWith("\n Licensed to the Apache Software Foundation
(ASF)"), is(true));
- assertThat(text.endsWith(" limitations under the License.\n"),
is(true));
- verifyString("/", DTD_NAME, "Repository");
- verifyString("/", DTD_PUBLIC_ID, "-//The Apache Software
Foundation//DTD Jackrabbit 1.2//EN");
- verifyString("/", DTD_SYSTEM_ID,
"http://jackrabbit.apache.org/dtd/repository-1.2.dtd");
- verifyName(COMMENT + "[2]", "jcr:primaryType", COMMENT);
- verifyString(COMMENT + "[2]", COMMENT_CONTENT, " Example
Repository Configuration File ");
- verifyName("Repository[1]", "jcr:primaryType",
"nt:unstructured");
- verifyName("Repository[1]/" + COMMENT + "[1]",
"jcr:primaryType", COMMENT);
- }
-
- @Test
- public void shouldHandleNamespaces() throws IOException {
- verifyDocument(xml2);
- verifyName("book[1]/bookinfo[1]/xi:include[1]",
"jcr:primaryType", "nt:unstructured");
- verifyString("book[1]/bookinfo[1]/xi:include[1]", "xi:href",
"Author_Group.xml");
- verifyName("book[1]/bookinfo[1]/xi:include[2]",
"jcr:primaryType", "nt:unstructured");
- verifyString("book[1]/bookinfo[1]/xi:include[2]", "xi:href",
"Legal_Notice.xml");
- }
-
- @Test
- public void shouldSequenceEntityDeclarations() throws IOException {
- verifyDocument(xml2);
- verifyName(ENTITY + "[1]", "jcr:primaryType", ENTITY);
- verifyString(ENTITY + "[1]", DTD_NAME, "%RH-ENTITIES");
- verifyString(ENTITY + "[1]", DTD_SYSTEM_ID,
"Common_Config/rh-entities.ent");
- verifyName(ENTITY + "[2]", "jcr:primaryType", ENTITY);
- verifyString(ENTITY + "[2]", DTD_NAME, "versionNumber");
- verifyString(ENTITY + "[2]", DTD_VALUE, "0.1");
- verifyName(ENTITY + "[3]", "jcr:primaryType", ENTITY);
- verifyString(ENTITY + "[3]", DTD_NAME, "copyrightYear");
- verifyString(ENTITY + "[3]", DTD_VALUE, "2008");
- }
-
- @Test
- public void shouldSequenceElementContent() throws IOException {
- verifyDocument(xml2);
- verifyString("book[1]/chapter[4]/sect1[1]/para[8]/" + ELEMENT_CONTENT +
"[1]",
- ELEMENT_CONTENT,
- "The path expression is more complicated."
- + " Sequencer path expressions are used by the sequencing
service to determine whether a particular changed node should be sequenced."
- + " The expressions consist of two parts: a selection criteria
and an output expression."
- + " Here's a simple example:");
- verifyString("book[1]/chapter[4]/sect1[1]/para[8]/programlisting[1]/" +
ELEMENT_CONTENT + "[1]",
- ELEMENT_CONTENT,
- "/a/b/c@title => /d/e/f");
- }
-
- @Test
- public void shouldSequenceCData() throws IOException {
- verifyDocument(xml3);
- verifyString("mx:Application[1]/mx:Script[1]/" + CDATA +
"[1]",
- CDATA_CONTENT,
- "\n\n" + " import
mx.events.ValidationResultEvent;\t\t\t\n"
- + " private var
vResult:ValidationResultEvent;\n" + "\t\t\t\n"
- + " // Event handler to validate and format
input.\n"
- + " private function Format():void {\n" +
" \n"
- + " vResult = numVal.validate();\n\n"
- + " if
(vResult.type==ValidationResultEvent.VALID) {\n"
- + " var
temp:Number=Number(priceUS.text); \n"
- + " formattedUSPrice.text=
usdFormatter.format(temp);\n" + " }\n"
- + " \n" + "
else {\n"
- + "
formattedUSPrice.text=\"\";\n" + " }\n" +
" }\n"
- + " ");
- }
-
- @Test
- public void shouldSequenceProcessingInstructions() throws IOException {
- verifyDocument(xml4);
- verifyName(PI + "[1]", "jcr:primaryType", PI);
- verifyString(PI + "[1]", TARGET, "eclipse");
- verifyString(PI + "[1]", PI_CONTENT,
"version=\"3.0\"");
- }
-
- @Test
- public void shouldSequenceXsds() throws IOException {
- verifyDocument(xsd);
- verifyName("xs:schema[1]", "jcr:primaryType",
"nt:unstructured");
- verifyString("xs:schema[1]", "xs:targetNamespace",
"http://ns.adobe.com/air/application/1.0");
- verifyString("xs:schema[1]", "xs:elementFormDefault",
"qualified");
- verifyName("xs:schema[1]/xs:element[1]", "jcr:primaryType",
"nt:unstructured");
- verifyString("xs:schema[1]/xs:element[1]", "xs:name",
"application");
- }
-
- private <T> T verify( String nodePath,
- String property,
- Class<T> expectedClass ) {
- Object[] values = output.getPropertyValues(nodePath.length() == 0 ? "."
: nodePath, property);
- assertThat(values, notNullValue());
- assertThat(values.length, is(1));
- Object value = values[0];
- assertThat(value, instanceOf(expectedClass));
- return expectedClass.cast(value);
- }
-
- private void verifyDocument( URL url ) throws IOException {
- stream = url.openStream();
- assertThat(stream, is(notNullValue()));
- sequencer.sequence(stream, output, context, monitor);
- verifyName("", "jcr:primaryType", DOCUMENT);
- }
-
- private void verifyName( String nodePath,
- String property,
- String expectedName ) {
- Name name = verify(nodePath, property, Name.class);
- assertThat(name,
is(context.getValueFactories().getNameFactory().create(expectedName)));
- }
-
- private void verifyString( String nodePath,
- String property,
- String expectedString ) {
- String string = verify(nodePath, property, String.class);
- assertThat(string, is(expectedString));
- }
-}
Deleted: trunk/dna-graph/src/test/resources/CurrencyFormatterExample.mxml
===================================================================
--- trunk/dna-graph/src/test/resources/CurrencyFormatterExample.mxml 2008-10-29 17:48:23
UTC (rev 597)
+++ trunk/dna-graph/src/test/resources/CurrencyFormatterExample.mxml 2008-10-29 18:23:01
UTC (rev 598)
@@ -1,54 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Simple example to demonstrate the CurrencyFormatter. -->
-<mx:Application
xmlns:mx="http://www.adobe.com/2006/mxml">
-
- <mx:Script>
- <![CDATA[
-
- import mx.events.ValidationResultEvent;
- private var vResult:ValidationResultEvent;
-
- // Event handler to validate and format input.
- private function Format():void {
-
- vResult = numVal.validate();
-
- if (vResult.type==ValidationResultEvent.VALID) {
- var temp:Number=Number(priceUS.text);
- formattedUSPrice.text= usdFormatter.format(temp);
- }
-
- else {
- formattedUSPrice.text="";
- }
- }
- ]]>
- </mx:Script>
-
- <mx:CurrencyFormatter id="usdFormatter" precision="2"
- currencySymbol="$" decimalSeparatorFrom="."
- decimalSeparatorTo="." useNegativeSign="true"
- useThousandsSeparator="true" alignSymbol="left"/>
-
- <mx:NumberValidator id="numVal" source="{priceUS}"
property="text"
- allowNegative="true" domain="real"/>
-
- <mx:Panel title="CurrencyFormatter Example" width="75%"
height="75%"
- paddingTop="10" paddingLeft="10" paddingRight="10"
paddingBottom="10">
-
- <mx:Form>
- <mx:FormItem label="Enter U.S. dollar amount:">
- <mx:TextInput id="priceUS" text=""
width="50%"/>
- </mx:FormItem>
-
- <mx:FormItem label="Formatted amount: ">
- <mx:TextInput id="formattedUSPrice" text=""
width="50%" editable="false"/>
- </mx:FormItem>
-
- <mx:FormItem>
- <mx:Button label="Validate and Format"
click="Format();"/>
- </mx:FormItem>
- </mx:Form>
-
- </mx:Panel>
-</mx:Application>
Deleted: trunk/dna-graph/src/test/resources/Descriptor.1.0.xsd
===================================================================
--- trunk/dna-graph/src/test/resources/Descriptor.1.0.xsd 2008-10-29 17:48:23 UTC (rev
597)
+++ trunk/dna-graph/src/test/resources/Descriptor.1.0.xsd 2008-10-29 18:23:01 UTC (rev
598)
@@ -1,127 +0,0 @@
-<?xml version="1.0"?>
-<xs:schema
-
xmlns:xs="http://www.w3.org/2001/XMLSchema"
-
targetNamespace="http://ns.adobe.com/air/application/1.0"
-
xmlns="http://ns.adobe.com/air/application/1.0"
- elementFormDefault="qualified"
->
- <xs:element name="application">
- <xs:complexType>
- <xs:all>
- <!-- About this application -->
- <xs:element name="id">
- <xs:simpleType>
- <xs:restriction base="xs:string">
- <xs:pattern value="[A-Za-z0-9\-\.]{1,212}"/>
- </xs:restriction>
- </xs:simpleType>
- </xs:element>
- <xs:element name="name" type="xs:string"
minOccurs="0"/>
- <xs:element name="version" type="xs:string"/>
- <xs:element name="filename">
- <xs:simpleType>
- <xs:restriction base="xs:string">
- <!-- name cannot begin with a ' ' (space), have any of these
characters: *"/:<>?\|, and end with a . (dot) or ' ' (space) -->
- <xs:pattern value='[^\*"/:><\?\\\|\.
]|[^\*"/:><\?\\\|
][^\*"/:><\?\\\|]*[^\*"/:><\?\\\|\. ]'/>
- </xs:restriction>
- </xs:simpleType>
- </xs:element>
- <xs:element name="description" type="xs:string"
minOccurs="0"/>
- <xs:element name="copyright" type="xs:string"
minOccurs="0"/>
- <xs:element name="icon" type="IconType"
minOccurs="0"/>
-
- <!-- How to start this application -->
- <xs:element name="initialWindow">
- <xs:complexType>
- <xs:all>
- <xs:element name="content" type="xs:anyURI"
minOccurs="1" />
- <xs:element name="title" type="xs:string"
minOccurs="0" />
-
- <xs:element name="systemChrome" minOccurs="0" >
- <xs:simpleType>
- <xs:restriction base="xs:string">
- <xs:enumeration value="none"/>
- <xs:enumeration value="standard"/>
- </xs:restriction>
- </xs:simpleType>
- </xs:element>
- <xs:element name="transparent" type="xs:boolean"
minOccurs="0" />
- <xs:element name="visible" type="xs:boolean"
minOccurs="0" />
-
- <xs:element name="minimizable" type="xs:boolean"
minOccurs="0" />
- <xs:element name="maximizable" type="xs:boolean"
minOccurs="0" />
- <xs:element name="resizable" type="xs:boolean"
minOccurs="0" />
-
- <xs:element name="x" type="xs:int"
minOccurs="0" />
- <xs:element name="y" type="xs:int"
minOccurs="0" />
- <xs:element name="width" type="xs:unsignedInt"
minOccurs="0" />
- <xs:element name="height" type="xs:unsignedInt"
minOccurs="0" />
- <xs:element name="minSize" type="BoundsSizeType"
minOccurs="0" />
- <xs:element name="maxSize" type="BoundsSizeType"
minOccurs="0" />
- </xs:all>
- </xs:complexType>
- </xs:element>
-
- <!-- About installing this application -->
- <xs:element name="installFolder" minOccurs="0">
- <xs:simpleType>
- <xs:restriction base="xs:string">
- <!-- installFolder cannot begin with a / (forward-slash) or a '
' (space), have any of these characters: *":<>?\|, and end with a . (dot)
or ' ' (space) -->
- <xs:pattern value='[^\*"/:><\?\\\|\.
]|[^\*"/:><\?\\\|
][^\*":><\?\\\|]*[^\*":><\?\\\|\. ]'/>
- </xs:restriction>
- </xs:simpleType>
- </xs:element>
- <xs:element name="programMenuFolder" minOccurs="0">
- <xs:simpleType>
- <xs:restriction base="xs:string">
- <!-- programMenuFolder cannot begin with a / (forward-slash) or a '
' (space), have any of these characters: *":<>?\|, and end with a . (dot)
or ' ' (space) -->
- <xs:pattern value='[^\*"/:><\?\\\|\.
]|[^\*"/:><\?\\\|
][^\*":><\?\\\|]*[^\*":><\?\\\|\. ]'/>
- </xs:restriction>
- </xs:simpleType>
- </xs:element>
-
- <!-- Features this application can opt in to -->
- <xs:element name="customUpdateUI" type="xs:boolean"
minOccurs="0"/>
- <xs:element name="allowBrowserInvocation"
type="xs:boolean" minOccurs="0"/>
- <xs:element name="fileTypes" minOccurs="0">
- <xs:complexType>
- <xs:sequence>
- <xs:element name="fileType" minOccurs="0"
maxOccurs="unbounded">
- <xs:complexType>
- <xs:all>
- <xs:element name="name" type="xs:string"/>
- <xs:element name="extension"
type="xs:string"/>
- <xs:element name="description" type="xs:string"
minOccurs="0"/>
- <xs:element name="contentType" type="xs:string"
minOccurs="0"/>
- <xs:element name="icon" type="IconType"
minOccurs="0"/>
- </xs:all>
- </xs:complexType>
- </xs:element>
- </xs:sequence>
- </xs:complexType>
- </xs:element>
- </xs:all>
-
- <!-- About the runtime version required -->
- <xs:attribute name="minimumPatchLevel"
type="xs:unsignedInt"/>
- </xs:complexType>
- </xs:element>
-
- <!-- Type definitions -->
- <xs:complexType name="IconType">
- <xs:all>
- <xs:element name="image16x16" type="xs:anyURI"
minOccurs="0"/>
- <xs:element name="image32x32" type="xs:anyURI"
minOccurs="0"/>
- <xs:element name="image48x48" type="xs:anyURI"
minOccurs="0"/>
- <xs:element name="image128x128" type="xs:anyURI"
minOccurs="0"/>
- </xs:all>
- </xs:complexType>
- <xs:simpleType name="UnsignedIntListType">
- <xs:list itemType="xs:unsignedInt"/>
- </xs:simpleType>
- <xs:simpleType name="BoundsSizeType">
- <xs:restriction base="UnsignedIntListType">
- <xs:length value="2"/>
- </xs:restriction>
- </xs:simpleType>
-</xs:schema>
Deleted: trunk/dna-graph/src/test/resources/jackrabbitInMemoryTestRepositoryConfig.xml
===================================================================
---
trunk/dna-graph/src/test/resources/jackrabbitInMemoryTestRepositoryConfig.xml 2008-10-29
17:48:23 UTC (rev 597)
+++
trunk/dna-graph/src/test/resources/jackrabbitInMemoryTestRepositoryConfig.xml 2008-10-29
18:23:01 UTC (rev 598)
@@ -1,116 +0,0 @@
-<?xml version="1.0"?>
-<!--
- Licensed to the Apache Software Foundation (ASF) under one or more
- contributor license agreements. See the NOTICE file distributed with
- this work for additional information regarding copyright ownership.
- The ASF licenses this file to You under the Apache License, Version 2.0
- (the "License"); you may not use this file except in compliance with
- the License. You may obtain a copy of the License at
-
-
http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<!DOCTYPE Repository PUBLIC "-//The Apache Software Foundation//DTD Jackrabbit
1.2//EN"
-
"http://jackrabbit.apache.org/dtd/repository-1.2.dtd">
-<!-- Example Repository Configuration File -->
-<Repository>
- <!--
- virtual file system where the repository stores global state
- (e.g. registered namespaces, custom node types, etc.)
- -->
- <FileSystem
class="org.apache.jackrabbit.core.fs.mem.MemoryFileSystem">
- <param name="path" value="${rep.home}/repository"/>
- </FileSystem>
-
- <!--
- security configuration
- -->
- <Security appName="Jackrabbit">
- <!--
- access manager:
- class: FQN of class implementing the AccessManager interface
- -->
- <AccessManager
class="org.apache.jackrabbit.core.security.SimpleAccessManager">
- <!-- <param name="config"
value="${rep.home}/access.xml"/> -->
- </AccessManager>
-
- <LoginModule
class="org.apache.jackrabbit.core.security.SimpleLoginModule">
- <!-- anonymous user name ('anonymous' is the default value) -->
- <param name="anonymousId" value="anonymous"/>
- <!--
- default user name to be used instead of the anonymous user
- when no login credentials are provided (unset by default)
- -->
- <!-- <param name="defaultUserId"
value="superuser"/> -->
- </LoginModule>
- </Security>
-
- <!--
- location of workspaces root directory and name of default workspace
- -->
- <Workspaces rootPath="${rep.home}/workspaces"
defaultWorkspace="default"/>
- <!--
- workspace configuration template:
- used to create the initial workspace if there's no workspace yet
- -->
- <Workspace name="Jackrabbit Core">
- <!--
- virtual file system of the workspace:
- class: FQN of class implementing the FileSystem interface
- -->
-
- <FileSystem
class="org.apache.jackrabbit.core.fs.mem.MemoryFileSystem">
- <param name="path" value="${wsp.home}"/>
- </FileSystem>
- <!--
- persistence manager of the workspace:
- class: FQN of class implementing the PersistenceManager interface
- -->
- <PersistenceManager
class="org.apache.jackrabbit.core.persistence.mem.InMemPersistenceManager">
- <param name="persistent" value="false"/>
- </PersistenceManager>
- <!--
- Search index and the file system it uses.
- class: FQN of class implementing the QueryHandler interface
- -->
- <SearchIndex
class="org.apache.jackrabbit.core.query.lucene.SearchIndex">
- <param name="path" value="${wsp.home}/index"/>
- </SearchIndex>
- </Workspace>
-
- <!--
- Configures the versioning
- -->
- <Versioning rootPath="${rep.home}/version">
- <!--
- Configures the filesystem to use for versioning for the respective
- persistence manager
- -->
- <FileSystem
class="org.apache.jackrabbit.core.fs.mem.MemoryFileSystem">
- <param name="path" value="${rep.home}/version" />
- </FileSystem>
-
- <!--
- Configures the persistence manager to be used for persisting version state.
- Please note that the current versioning implementation is based on
- a 'normal' persistence manager, but this could change in future
- implementations.
- -->
- <PersistenceManager
class="org.apache.jackrabbit.core.persistence.mem.InMemPersistenceManager">
- <param name="persistent" value="false"/>
- </PersistenceManager>
- </Versioning>
-
- <!--
- Search index for content that is shared repository wide
- (/jcr:system tree, contains mainly versions)
- -->
- <SearchIndex
class="org.apache.jackrabbit.core.query.lucene.SearchIndex">
- <param name="path"
value="${rep.home}/repository/index"/>
- </SearchIndex>
-</Repository>
Deleted: trunk/dna-graph/src/test/resources/master.xml
===================================================================
--- trunk/dna-graph/src/test/resources/master.xml 2008-10-29 17:48:23 UTC (rev 597)
+++ trunk/dna-graph/src/test/resources/master.xml 2008-10-29 18:23:01 UTC (rev 598)
@@ -1,1890 +0,0 @@
-<!--
- ~ JBoss, Home of Professional Open Source.
- ~
- ~ Copyright (c) 2008, Red Hat Middleware LLC or third-party contributors as
- ~ indicated by the @author tags or express copyright attribution
- ~ statements applied by the authors. All third-party contributions are
- ~ distributed under license by Red Hat Middleware LLC.
- ~
- ~ This copyrighted material is made available to anyone wishing to use, modify,
- ~ copy, or redistribute it subject to the terms and conditions of the GNU
- ~ Lesser General Public License, as published by the Free Software Foundation.
- ~
- ~ This program 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 book PUBLIC "-//OASIS//DTD DocBook XML V4.4//EN"
"http://www.oasis-open.org/docbook/xml/4.4/docbookx.dtd" [
-<!ENTITY % RH-ENTITIES SYSTEM "Common_Config/rh-entities.ent">
-<!ENTITY versionNumber "0.1">
-<!ENTITY copyrightYear "2008">
-<!ENTITY copyrightHolder "Red Hat Middleware, LLC.">]>
-<book>
- <bookinfo>
- <title>JBoss DNA</title>
- <subtitle>Getting Started Guide</subtitle>
- <releaseinfo>&versionNumber;
- </releaseinfo>
- <productnumber>&versionNumber;
- </productnumber>
- <issuenum>1</issuenum>
- <mediaobject>
- <imageobject role="fo">
- <imagedata fileref="images/dna-logo.png" align="center"
/>
- </imageobject>
- <imageobject role="pdf">
- <imagedata fileref="images/dna-logo.png" scale="75"
align="center" />
- </imageobject>
- <imageobject role="html">
- <imagedata fileref="images/dna-logo.png" depth="3cm"
/>
- </imageobject>
- <imageobject role="xhtml">
- <imagedata fileref="images/dna-logo.png" depth="3cm"
/>
- </imageobject>
- <imageobject role="xhtml_single">
- <imagedata fileref="images/dna-logo.png" depth="3cm"
/>
- </imageobject>
- </mediaobject>
- <xi:include
xmlns:xi="http://www.w3.org/2001/XInclude"
href="Author_Group.xml" />
- <xi:include
xmlns:xi="http://www.w3.org/2001/XInclude"
href="Legal_Notice.xml" />
- </bookinfo>
- <preface id="preface" revision="1">
- <title>What this book covers</title>
- <para>The goal of this book is to help you learn about JBoss DNA and how you
can use it in your own applications to get the
- most out of your JCR repositories.</para>
- <para>The first part of the book starts out with an introduction to content
repositories and an overview of the JCR API,
- both of which are important aspects of JBoss DNA. This is followed by an overview
of the JBoss DNA project, its
- architecture, and a basic roadmap for what's coming next.</para>
- <para>The next part of the book covers how to download and build the examples,
how to use JBoss DNA with existing
- repositories, and how to build and use custom sequencers.</para>
- <para>
- If you have any questions or comments, please feel free to contact JBoss DNA's
- <ulink url="mailto:dna-users@jboss.org">user mailing
list</ulink>
- or use the
- <ulink
url="http://www.jboss.com/index.html?module=bb&op=viewforum&...
forums</ulink>
- . If you'd like to get involved on the project, join the
- <ulink
url="http://www.jboss.org/dna/lists.html">mailing
lists</ulink>
- ,
- <ulink
url="http://www.jboss.org/dna/subversion.html">download the
code</ulink>
- and get it building, and visit our
- <ulink
url="http://jira.jboss.org/jira/browse/DNA">JIRA issue
management system</ulink>
- . If there's something in particular you're interested in, talk with the
community - there may be others interested in the
- same thing.
- </para>
- </preface>
- <chapter id="introduction">
- <title>Introduction</title>
- <para>There are a lot of choices for how applications can store information
persistently so that it can be accessed at a
- later time and by other processes. The challenge developers face is how to use an
approach that most closely matches the
- needs of their application. This choice becomes more important as developers choose
to focus their efforts on
- application-specific logic, delegating much of the responsibilities for persistence
to libraries and frameworks.</para>
- <para>
- Perhaps one of the easiest techniques is to simply store information in
- <emphasis>files</emphasis>
- . The Java language makes working with files relatively easy, but Java really
doesn't provide many bells and whistles. So
- using files is an easy choice when the information is either not complicated (for
example property files), or when users may
- need to read or change the information outside of the application (for example log
files or configuration files). But using
- files to persist information becomes more difficult as the information becomes more
complex, as the volume of it increases,
- or if it needs to be accessed by multiple processes. For these situations, other
techniques often offer better choices.
- </para>
- <para>
- Another technique built into the Java language is
- <emphasis>Java serialization</emphasis>
- , which is capable of persisting the state of an object graph so that it can be
read back in at a later time. However, Java
- serialization can quickly become tricky if the classes are changed, and so it's
beneficial usually when the information is
- persisted for a very short period of time. For example, serialization is sometimes
used to send an object graph from one
- process to another.
- </para>
- <para>
- One of the more popular persistence technologies is the
- <emphasis>relational database</emphasis>
- . Relational database management systems have been around for decades and are very
capable. The Java Database Connectivity
- (JDBC) API provides a standard interface for connecting to and interacting with
relational databases. However, it is a
- low-level API that requires a lot of code to use correctly, and it still
doesn't abstract away the DBMS-specific SQL
- grammar. Also, working with relational data in an object-oriented language can feel
somewhat unnatural, so many developers
- map this data to classes that fit much more cleanly into their application. The
problem is that manually creating this
- mapping layer requires a lot of repetitive and non-trivial JDBC code.
- </para>
- <para>
- <emphasis>Object-relational mapping</emphasis>
- libraries automate the creation of this mapping layer and result in far less code
that is much more maintainable with
- performance that is often as good as (if not better than) handwritten JDBC code.
The new
- <ulink
url="http://java.sun.com/developer/technicalArticles/J2EE/jpa/"...
Persistence API (JPA)</ulink>
- provide a standard mechanism for defining the mappings (through annotations) and
working with these entity objects. Several
- commercial and open-source libraries implement JPA, and some even offer additional
capabilities and features that go beyond
- JPA. For example,
- <ulink url="http://www.hibernate.org">Hibernate</ulink>
- is one of the most feature-rich JPA implementations and offers object caching,
statement caching, extra association
- mappings, and other features that help to improve performance and usefulness.
- </para>
- <para>
- While relational databases and JPA are solutions that work for many applications,
they become more limited in cases when the
- information structure is highly flexible, is not known
- <emphasis>a priori</emphasis>
- , or is subject to frequent change and customization. In these situations,
- <emphasis>content repositories</emphasis>
- may offer a better choice for persistence. Content repositories are almost a hybrid
between relational databases and file
- systems, and typically provide other capabilities as well, including versioning,
indexing, search, access control,
- transactions, and observation. Because of this, content repositories are used by
content management systems (CMS), document
- management systems (DMS), and other applications that manage electronic files
(e.g., documents, images, multi-media, web
- content, etc.) and metadata associated with them (e.g., author, date, status,
security information, etc.). The
- <ulink
url="http://www.jcp.org/en/jsr/detail?id=170">Content
Repository for Java technology API</ulink>
- provides a standard Java API for working with content repositories. Abbreviated
"JCR", this API was developed as part of the
- Java Community Process under
- <ulink
url="http://www.jcp.org/en/jsr/detail?id=170">JSR-170</ul...
- and is being revised under
- <ulink
url="http://www.jcp.org/en/jsr/detail?id=283">JSR-283</ul...
- .
- </para>
- <para>
- The
- <emphasis>JBoss DNA project</emphasis>
- is building the tools and services that surround content repositories. Nearly all
of these capabilities are to be hidden
- below the JCR API and involve automated processing of the information in the
repository. Thus, JBoss DNA can add value to
- existing repository implementations. For example, JCR repositories offer the
ability to upload files into the repository and
- have the file content indexed for search purposes. JBoss DNA also defines a library
for "sequencing" content - to extract
- meaningful information from that content and store it in the repository, where it
can then be searched, accessed, and
- analyzed using the JCR API.
- </para>
- <para> JBoss DNA is building other features as well. One goal of JBoss DNA is
to create federated repositories that
- dynamically merge the information from multiple databases, services, applications,
and other JCR repositories. Another is to
- create customized views based upon the type of data and the role of the user that
is accessing the data. And yet another is
- to create a REST-ful API to allow the JCR content to be accessed easily by other
applications written in other languages.
- </para>
- <para>
- The
- <link linkend="jboss_dna">next chapter</link>
- in this book goes into more detail about JBoss DNA and its architecture, the
different components, what's available now, and
- what's coming in future releases.
- <link linkend="downloading_and_running">Chapter 3</link>
- then provides instructions for downloading and running the sequencer examples for
the current release.
- <link linkend="using_dna">Chapter 4</link>
- walks through how to use JBoss DNA in your applications, while
- <link linkend="custom_sequencers">Chapter 5</link>
- goes over how to create custom sequencers. Finally,
- <link linkend="future_directions">Chapter 6</link>
- wraps things up with a discussion about the future of JBoss DNA.
- </para>
- </chapter>
- <chapter id="jboss_dna">
- <title>Understanding JBoss DNA</title>
- <sect1 id="jboss_dna_overview">
- <title>Overview</title>
- <para>JBoss DNA is a repository and set of tools that make it easy to
capture, version, analyze, and understand the
- fundamental building blocks of information. As models, service and process
definitions, schemas, source code, and other
- artifacts are added to the repository, JBoss DNA "sequences" the makeup
of these components and extracts their structure
- and interdependencies. The JBoss DNA web application allows end users to access,
visualize, and edit this information in
- the terminology and structure they are familiar with. Such domain-specific
solutions can be easily created with little or
- no programming.</para>
- <para> JBoss DNA supports the Java Content Repository (JCR) standard and is
able to provide a single integrated view of
- multiple repositories, external databases, services, and applications, ensuring
that JBoss DNA has access to the latest
- and most reliable master data. For instance, DNA could provide in a single view
valuable insight into the business
- processes and process-level services impacted by a change to in an intermediary
web server operation defined via WSDL.
- Similarly, a user could quickly view and navigate the dependencies between the
data source models and transformation
- information stored within a content repository, the code base stored within a
version control system, and the database
- schemas used by an application.</para>
- </sect1>
- <sect1 id="architecture">
- <title>Architecture</title>
- <para>The architecture for JBoss DNA consists of several major components
that will be built on top of standard APIs,
- including JCR, JDBC, JNDI and HTTP. The goal is to allow these components to be
assembled as needed and add value on top
- of other DNA components or third-party systems that support these standard
APIs.</para>
- <mediaobject>
- <imageobject role="fo">
- <imagedata align="center"
fileref="images/dna-architecture.png" />
- </imageobject>
- <imageobject role="html">
- <imagedata align="center"
fileref="images/dna-architecture.png" />
- </imageobject>
- </mediaobject>
- <para>
- As shown in the diagram above, the major components are (starting at the top):
- <itemizedlist>
- <listitem>
- <para>
- <emphasis role="strong">DNA Eclipse
Plugins</emphasis>
- enable Eclipse users to access the contents of a JBoss DNA repository.
- </para>
- </listitem>
- <listitem>
- <para>
- <emphasis role="strong">DNA JDBC Driver</emphasis>
- provides a driver implementation, allowing JDBC-aware applications to
connect to and use a JBoss DNA repository.
- </para>
- </listitem>
- <listitem>
- <para>
- <emphasis role="strong">DNA Remote JCR</emphasis>
- is a client-side component for accessing remote JCR repositories.
- </para>
- </listitem>
- <listitem>
- <para>
- <emphasis role="strong">DNA Web
Application</emphasis>
- is used by end users and domain experts to visualize, search, edit, change
and tag the repository content. The web
- application uses views to define how different types of information are to
be presented and edited in
- domain-specific ways. The goal is that this web application is easily
customized and branded for inclusion into
- other solutions and application systems. The DNA Web Application operates
upon any JCR-compliant repository,
- although it does rely upon the DNA analysis and templating services.
- </para>
- </listitem>
- <listitem>
- <para>
- <emphasis role="strong">DNA Publishing
Server</emphasis>
- allows content to be downloaded, uploaded, and edited using the Atom
Publishing Protocol. With the DNA Publishing
- Server, the content of the repository can easily be created, read, edited,
and deleted using the standard HTTP
- operations of POST, GET, PUT, and DELETE (respectively). More and more
tools are being created that support working
- with Atom Publishing servers. The DNA Publishing Server operates upon any
JCR-compliant repository.
- </para>
- </listitem>
- <listitem>
- <para>
- <emphasis role="strong">DNA WebDAV Server</emphasis>
- allows clients such as Microsoft Windows and Apple OS X to connect to,
read, and edit the content in the repository
- using the WebDAV standard. Since WebDAV is an extension of HTTP, web
browsers are able to read (but not modify) the
- content served by a WebDAV compliant server. The DNA WebDAV Server operates
upon any JCR-compliant repository.
- </para>
- </listitem>
- <listitem>
- <para>
- <emphasis role="strong">DNA Sequencers</emphasis>
- are pluggable components that make it possible for content to be uploaded
to the repository and automatically
- processed to extract meaningful structure and place that structure in the
repository. Once this information is in
- the repository, it can be viewed, edited, analyzed, searched, and related
to other content. DNA defines a Java
- interface that sequencers must implement. DNA sequencers operate upon any
JCR-compliant repository.
- </para>
- </listitem>
- <listitem>
- <para>
- <emphasis role="strong">DNA Analyses</emphasis>
- are pluggable components that analyze content and the relationships between
content to generate reports or to answer
- queries. DNA will include some standard analyzers, like dependency analysis
and similarity analysis, that are
- commonly needed by many different solutions. DNA analyzers operate upon any
JCR-compliant repository.
- </para>
- </listitem>
- <listitem>
- <para>
- <emphasis role="strong">DNA Views</emphasis>
- are definitions of how types of information are to be presented in a user
interface to allow for creation, reading,
- editing, and deletion of information. DNA view definitions consist of data
stored in a JCR repository, and as such
- views can be easily added, changed or removed entirely by using the DNA Web
Application, requiring no programming.
- </para>
- </listitem>
- <listitem>
- <para>
- <emphasis role="strong">DNA Federation</emphasis>
- is an implementation of the JCR API that builds the content within the
repository by accessing and integrating
- information from multiple sources. DNA Federation allows the integration of
external systems, like other JCR
- repositories, databases, applications, and services.
- </para>
- </listitem>
- <listitem>
- <para>
- <emphasis role="strong">DNA Connectors</emphasis>
- are used to communicate with these external sources of information. In the
federation engine, each source is able to
- contribute node structure and node properties to any part of the federated
graph, although typically many connectors
- will contribute most of their information to isolated subgraphs. The result
is that integration from a wide range of
- systems can be integrated and accessed through the DNA Web Application, DNA
Publishing Server, and DNA WebDAV
- Server. Connectors also may optionally participate in distributed
transactions by exposing an XAResource.
- </para>
- </listitem>
- <listitem>
- <para>
- <emphasis role="strong">DNA Maven</emphasis>
- is a classloader library compatible with Maven 2 project dependencies. This
allows the creation of Java ClassLoader
- instances using Maven 2 style paths, and all dependencies are transitively
managed and included.
- </para>
- </listitem>
- </itemizedlist>
- </para>
- <para>
- Continue reading the rest of this chapter for more detail about the
- <link linkend="sequencers">sequencing framework</link>
- available in this release, or the
- <link linkend="federation">federation engine</link>
- and
- <link linkend="federation_connectors">connectors</link>
- that will be the focus of the next release. Or, skip to the
- <link linkend="downloading_and_running">examples</link>
- to see how to start using JBoss DNA &versionNumber;
- today.
- </para>
- </sect1>
- <sect1 id="sequencers">
- <title>Sequencing content</title>
- <para> The current JBoss DNA release contains a sequencing framework that is
designed to sequence data (typically files)
- stored in a JCR repository to automatically extract meaningful and useful
information. This additional information is then
- saved back into the repository, where it can be accessed and used.</para>
- <para> In other words, you can just upload various kinds of files into a JCR
repository, and DNA automatically processes
- those files to extract meaningful structured information. For example, load DDL
files into the repository, and let
- sequencers extract the structure and metadata for the database schema. Load
Hibernate configuration files into the
- repository, and let sequencers extract the schema and mapping information. Load
Java source into the repository, and let
- sequencers extract the class structure, JavaDoc, and annotations. Load a PNG,
JPEG, or other image into the repository,
- and let sequencers extract the metadata from the image and save it in the
repository. The same with XSDs, WSDL, WS
- policies, UML, MetaMatrix models, etc.</para>
- <para>
- JBoss DNA sequencers sit on top of existing JCR repositories (including federated
repositories) - they basically extract
- more useful information from what's already stored in the repository. And
they use the existing JCR versioning system. Each
- sequencer typically processes a single kind of file format or a single kind of
content. </para>
- <para>The following sequencers are included in JBoss DNA:
- <itemizedlist>
- <listitem>
- <para>
- <emphasis role="strong">Image sequencer</emphasis>
- - A sequencer that processes the binary content of an image file, extracts
the metadata for the image, and then
- writes that image metadata to the repository. It gets the file format,
image resolution, number of bits per pixel
- (and optionally number of images), comments and physical resolution from
JPEG, GIF, BMP, PCX, PNG, IFF, RAS, PBM,
- PGM, PPM, and PSD files. (This sequencer may be improved in the future to
also extract EXIF metadata from JPEG
- files; see
- <ulink
url="http://jira.jboss.org/jira/browse/DNA-26">DNA-26</ul...
- .)
- </para>
- </listitem>
- <listitem>
- <para>
- <emphasis role="strong">MP3 sequencer</emphasis>
- - A sequencer that processes the contents of an MP3 audio file, extracts
the metadata for the file, and then
- writes that image metadata to the repository. It gets the title, author,
album, year, and comment.
- (This sequencer may be improved in the future to also extract other ID3
metadata from other audio file formats; see
- <ulink
url="http://jira.jboss.org/jira/browse/DNA-66">DNA-26</ul...
- .)
- </para>
- </listitem>
- </itemizedlist>
- </para>
- <para>
- As the community develops additional sequencers, they will also be included in
JBoss DNA. Some of those that have been
- identified as being useful include:
- <itemizedlist>
- <listitem>
- <para>
- <emphasis role="strong">XML Schema Document (XSD)
Sequencer</emphasis>
- - Process XSD files and extract the various elements, attributes, complex
types, simple types, groups, and other
- information. (See
- <ulink
url="http://jira.jboss.org/jira/browse/DNA-32">DNA-32</ul...
- )
- </para>
- </listitem>
- <listitem>
- <para>
- <emphasis role="strong">Web Service Definition Language
(WSDL) Sequencer</emphasis>
- - Process WSDL files and extract the services, bindings, ports, operations,
parameters, and other information. (See
- <ulink
url="http://jira.jboss.org/jira/browse/DNA-33">DNA-33</ul...
- )
- </para>
- </listitem>
- <listitem>
- <para>
- <emphasis role="strong">Hibernate File
Sequencer</emphasis>
- - Process Hibernate configuration (cfg.xml) and mapping (hbm.xml) files to
extract the configuration and mapping
- information. (See
- <ulink
url="http://jira.jboss.org/jira/browse/DNA-61">DNA-61</ul...
- )
- </para>
- </listitem>
- <listitem>
- <para>
- <emphasis role="strong">XML Metadata Interchange (XMI)
Sequencer</emphasis>
- - Process XMI documents that contain UML models or models using another
metamodel, extracting the model structure
- into the repository. (See
- <ulink
url="http://jira.jboss.org/jira/browse/DNA-31">DNA-31</ul...
- )
- </para>
- </listitem>
- <listitem>
- <para>
- <emphasis role="strong">ZIP Archive
Sequencer</emphasis>
- - Process ZIP archive files to extract (explode) the contents into the
repository. (See
- <ulink
url="http://jira.jboss.org/jira/browse/DNA-63">DNA-63</ul...
- )
- </para>
- </listitem>
- <listitem>
- <para>
- <emphasis role="strong">Java Archive (JAR)
Sequencer</emphasis>
- - Process JAR files to extract (explode) the contents into the classes and
file resources. (See
- <ulink
url="http://jira.jboss.org/jira/browse/DNA-64">DNA-64</ul...
- )
- </para>
- </listitem>
- <listitem>
- <para>
- <emphasis role="strong">Java Class File
Sequencer</emphasis>
- - Process Java class files (bytecode) to extract the class structure
(including annotations) into the repository.
- (See
- <ulink
url="http://jira.jboss.org/jira/browse/DNA-62">DNA-62</ul...
- )
- </para>
- </listitem>
- <listitem>
- <para>
- <emphasis role="strong">Java Source File
Sequencer</emphasis>
- - Process Java source files to extract the class structure (including
annotations) into the repository. (See
- <ulink
url="http://jira.jboss.org/jira/browse/DNA-51">DNA-51</ul...
- )
- </para>
- </listitem>
- <listitem>
- <para>
- <emphasis role="strong">PDF Sequencer</emphasis>
- - Process PDF files to extract the document metadata, including table of
contents. (See
- <ulink
url="http://jira.jboss.org/jira/browse/DNA-50">DNA-50</ul...
- )
- </para>
- </listitem>
- <listitem>
- <para>
- <emphasis role="strong">Maven 2 POM
Sequencer</emphasis>
- - Process Maven 2 Project Object Model (POM) files to extract the project
information, dependencies, plugins, and
- other content. (See
- <ulink
url="http://jira.jboss.org/jira/browse/DNA-24">DNA-24</ul...
- )
- </para>
- </listitem>
- <listitem>
- <para>
- <emphasis role="strong">Data Definition Language (DDL)
Sequencer</emphasis>
- - Process various dialects of DDL, including that from Oracle, SQL Server,
MySQL, PostgreSQL, and others. May need
- to be split up into a different sequencer for each dialect. (See
- <ulink
url="http://jira.jboss.org/jira/browse/DNA-26">DNA-26</ul...
- )
- </para>
- </listitem>
- <listitem>
- <para>
- <emphasis role="strong">MP3 and MP4
Sequencer</emphasis>
- - Process MP3 and MP4 audio files to extract the name of the song, artist,
album, track number, and other metadata.
- (See
- <ulink
url="http://jira.jboss.org/jira/browse/DNA-30">DNA-30</ul...
- )
- </para>
- </listitem>
- </itemizedlist>
- </para>
- <para>
- The
- <link linkend="using_dna">examples</link>
- in this book go into more detail about how sequencers are managed and used, and
- <link linkend="custom_sequencers">Chapter 5</link>
- goes into detail about how to write custom sequencers.
- </para>
- </sect1>
- <sect1 id="federation">
- <title>Federating content</title>
- <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 move the information into a JCR repository? Most likely
there are existing applications that rely upon
- that information being where it is. If we were to move it, then all those
applications would break. Or they'd have to be
- changed to use JCR. If the information is being used, the most practical thing is
to leave it where it is.</para>
- <para>
- Then why not just copy the information into a JCR repository? Actually, there are
times when it's perfectly reasonable to
- make a copy of the data. Perhaps the system managing the existing information
cannot handle the additional load of more
- clients. Or, perhaps the information doesn't change, or it does change and we
want snapshots that don't change. But more
- likely, the data
- <emphasis>does</emphasis>
- change. So if applications are to use the most current information and we make
copies of the data, we have to keep the
- copies synchronized with the master. That's generally a lot of work.
- </para>
- <para>The JBoss DNA federation engine lets us leave the information where it
is, yet lets client applications use the JCR
- API to access all the information without caring where the information really
exists. If the underlying information
- changes, client applications using JCR observation will be notified of the
changes. If a JBoss DNA federated repository is
- configured to allow updates, client applications can change the information in
the repository and JBoss DNA will propagate
- those changes down to the original source.</para>
- <sect2 id="federation_connectors">
- <title>Connecting to information sources</title>
- <para>
- The JBoss DNA federation engine will use connectors to interact with different
information sources to get at the content
- in those systems. Some ideas for connectors include:
- <itemizedlist>
- <listitem>
- <para>
- <emphasis role="strong">JCR Repository
Connector</emphasis>
- - Connect to and interact with other JCR repositories.
- </para>
- </listitem>
- <listitem>
- <para>
- <emphasis role="strong">File System
Connector</emphasis>
- - Expose the files and directories on a file system through JCR.
- </para>
- </listitem>
- <listitem>
- <para>
- <emphasis role="strong">Maven 2 Repository
Connector</emphasis>
- - Access and expose the contents of a Maven 2 repository (either on the
local file system or via HTTP) through
- JCR.
- </para>
- </listitem>
- <listitem>
- <para>
- <emphasis role="strong">JDBC Metadata
Connector</emphasis>
- - Connect to relational databases via JDBC and expose their schema as
content in a repository.
- </para>
- </listitem>
- <listitem>
- <para>
- <emphasis role="strong">UDDI Connector</emphasis>
- - Interact with UDDI registries to integrate their content into a
repository.
- </para>
- </listitem>
- <listitem>
- <para>
- <emphasis role="strong">SVN Connector</emphasis>
- - Interact with Subversion software configuration management (SCM)
repositories to expose the managed resources
- through JCR. Consider using the
- <ulink
url="http://svnkit.com/">SVNkit</ulink>
- (dual license) library for an API into Subversion.
- </para>
- </listitem>
- <listitem>
- <para>
- <emphasis role="strong">CVS Connector</emphasis>
- - Interact with CVS software configuration management (SCM) repositories
to expose the managed resources through
- JCR.
- </para>
- </listitem>
- <listitem>
- <para>
- <emphasis role="strong">JDBC Storage
Connector</emphasis>
- - Store and access information in a relational database. Also useful for
persisting information in the federated
- repository not stored elsewhere.
- </para>
- </listitem>
- <listitem>
- <para>
- <emphasis role="strong">Distributed Database
Connector</emphasis>
- - Store and access information in a
- <ulink
url="http://www.hypertable.org/">Hypertable</ulink>
- or
- <ulink
url="http://hadoop.apache.org/hbase/">HBase</ulink>
- distributed databases. Also useful for persisting information in the
federated repository not stored elsewhere.
- </para>
- </listitem>
- </itemizedlist>
- </para>
- <para>
- If the connectors allow the information they contribute to be updated, they
must provide an
- <code>XAResource</code>
- implementation that can be used with a Java Transaction Service. Connectors
that provide read-only access need not
- provide an implementation.
- </para>
- <para>
- Also, connectors talk to
- <emphasis>sources</emphasis>
- of information, and it's quite likely that the same connector is used to
talk to different sources. Each source contains
- the configuration details (e.g., connection information, location, properties,
options, etc.) for working with that
- particular source, as well as a reference to the connector that should be used
to establish connections to the source.
- And of course, sources can be added or removed without having to stop and
restart the federated repository.
- </para>
- </sect2>
- <sect2 id="federation_graph">
- <title>Building the unified graph</title>
- <para> The federation engine 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 not 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>
- </sect2>
- <sect2 id="federation_queries">
- <title>Searching and querying</title>
- <para> The JBoss DNA federated repository will also support queries against
the integrated and unified graph. In some
- situations the query can be determined to apply to a single source, but in most
situations the query must be planned
- (and possibly rewritten) such that it can be pushed down to all the appropriate
sources. Also, the cached results must
- be consulted prior to returning the query results, as the results from one
source might have contributions from another
- source.</para>
- <note>
- <para> It is hoped that the MetaMatrix query engine can be used for this
purpose after it is open-sourced. This engine
- implements sophisticated query planning and optimization techniques for
working efficiently with multiple sources.
- </para>
- </note>
- <para>Searching the whole federated repository is also important. This
allows users to simply supply a handful of
- search terms, and to get results that are ranked based upon how close each
result is to the search terms. (Searching is
- very different from querying, which involves specifying the exact semantics of
what is to be searched and how the
- information is to be compared.) JBoss DNA will incorporate a search engine
(e.g., likely to be Lucene) and will populate
- the engine's indexes using the federated content and the cached
information. Notifications of changing information will
- be reflected in the indexes, but some sources may want to explicitly allow or
disallow periodic crawling of their
- content.</para>
- </sect2>
- <sect2 id="federation_updates">
- <title>Updating content</title>
- <para>
- The JBoss DNA federated repositories also make it possible for client
applications to make changes to the unified graph
- within the context of distributed transactions. According to the JCR API,
client applications use the Java Transaction
- API (JTA) to control the boundaries of their transactions. Meanwhile, the
federated repository uses a
- <ulink
url="http://www.jboss.org/jbosstm/">distributed
transaction service</ulink>
- to coordinate the XA resources provided by the connectors.
- </para>
- <para> It is quite possible that clients add properties to nodes in the
unified graph, and that this information cannot be
- handled by the same underlying source that contributed to the node. In this
case, the federated repository can be
- configured with a fallback source that will be used used to store this
"extra" information.</para>
- <para>
- It is a goal that non-XA sources (i.e., sources that use connectors without XA
resources) can participate in distributed
- transactions through the use of
- <emphasis>compensating transactions</emphasis>
- . Because the JBoss DNA federation engine implements the JCR observation
system, it is capable of recording all of the
- changes made to the distributed graph (and those changes sent to each updatable
source). Therefore, if a non-XA source
- is involved in a distributed transaction that must be rolled back, any changes
made to non-XA sources can be undone. (Of
- course, this does not make the underlying source transactional:
non-transactional sources still may expose the interim
- changes to other clients.)
- </para>
- </sect2>
- <sect2 id="federation_events">
- <title>Observing changes</title>
- <para> The JCR API supports observing a repository to receive notifications
of additions, changes and deletions of nodes
- and properties. The JBoss DNA federated repository will support this API
through two primary means.</para>
- <para> When the changes are made through the federated repository, the
JBoss DNA federation engine is well aware of the
- set of changes that have been (or are being) made to the unified graph. These
events are directly propagated to
- listeners.</para>
- <para> Sources have the ability to publish events, making it possible for
the JBoss DNA federation engine and clients that
- have registered listeners to be notified of changes in the information managed
by that source. These events are first
- processed by the federation engine and possibly altered based upon
contributions from other sources. (The federation
- engine also uses these events to update or purge information in the cache,
which may add to the event set.) The
- resulting (and possibly altered) event set is then sent to all client
listeners.</para>
- </sect2>
- </sect1>
- </chapter>
- <!--
====================================================================================================
- Chapter
-
====================================================================================================
-->
- <chapter id="downloading_and_running">
- <title>Running the example application</title>
- <para>
- This chapter provides instructions for downloading and running a sample application
that demonstrates how JBoss DNA works
- with a JCR repository to automatically sequence changing content to extract useful
information. So read on to get the simple
- application running, and then in the
- <link linkend="using_dna">next chapter</link>
- we'll dive into the source code for the example and show how to use JBoss DNA
in your own applications.
- </para>
- <para>JBoss DNA uses Maven 2 for its build system, as is this example. Using
Maven 2 has several advantages, including
- the ability to manage dependencies. If a library is needed, Maven automatically
finds and downloads that library, plus
- everything that library needs. This means that it's very easy to build the
examples - or even create a maven project that
- depends on the JBoss DNA JARs.</para>
- <note>
- <para>
- To use Maven with JBoss DNA, you'll need to have
- <ulink
url="http://java.sun.com/javase/downloads/index_jdk5.jsp">JDK 5 or
6</ulink>
- and Maven 2.0.7 (or higher).
- </para>
- <para>
- Maven can be downloaded from
- <ulink
url="http://maven.apache.org/">http://maven.apache.org/</...
- , and is installed by unzipping the
- <code>maven-2.0.7-bin.zip</code>
- file to a convenient location on your local disk. Simply add
- <code>$MAVEN_HOME/bin</code>
- to your path and add the following profile to your
- <code>~/.m2/settings.xml</code>
- file:
- <programlisting role="XML"
language="xml"><settings>
- <profiles>
- <profile>
- <id>jboss.repository</id>
- <activation>
- <property>
- <name>!jboss.repository.off</name>
- </property>
- </activation>
- <repositories>
- <repository>
- <id>snapshots.jboss.org</id>
- <url>http://snapshots.jboss.org/maven2</url>
- <snapshots>
- <enabled>true</enabled>
- </snapshots>
- </repository>
- <repository>
- <id>repository.jboss.org</id>
- <url>http://repository.jboss.org/maven2</url>
- <snapshots>
- <enabled>false</enabled>
- </snapshots>
- </repository>
- </repositories>
- <pluginRepositories>
- <pluginRepository>
- <id>repository.jboss.org</id>
- <url>http://repository.jboss.org/maven2</url>
- <snapshots>
- <enabled>false</enabled>
- </snapshots>
- </pluginRepository>
- <pluginRepository>
- <id>snapshots.jboss.org</id>
- <url>http://snapshots.jboss.org/maven2</url>
- <snapshots>
- <enabled>true</enabled>
- </snapshots>
- </pluginRepository>
- </pluginRepositories>
- </profile>
- </profiles>
-</settings></programlisting>
- This profile informs Maven of the two JBoss repositories (snapshots and releases)
that contain
- all of the JARs for JBoss DNA and all dependent libraries.</para>
- </note>
- <sect1 id="downloading">
- <title>Downloading and compiling</title>
- <para>The next step is to <ulink
url="http://www.jboss.org/file-access/default/members/dna/downloads/...
- the example for this Getting Started guide, and extract the contents to a
convenient location on your local disk.
- You'll find the example contains the following files, which are organized
according to the standard Maven directory structure:
- <programlisting>
-examples/pom.xml
- sequencers/pom.xml
- /src/main/assembly
- /config
- /java
- /resources
- /test/java
- /resources
- </programlisting>
- </para>
- <para>There are essentially two Maven projects: a
<code>sequencers</code> project and a parent project. All of the source
- for the example is located in the <code>sequencers</code> subdirectory.
And you may have noticed that none
- of the JBoss DNA libraries are there. This is where Maven comes in. The two
<code>pom.xml</code> files tell
- Maven everything it needs to know about what libraries are required and how to
build the example.</para>
- <para>In a terminal, go to the <code>examples</code> directory
and run <emphasis role="strong"><code>mvn
install</code></emphasis>.
- This command downloads all of the JARs necessary to compile and build the example,
including the JBoss DNA libraries,
- the libraries they depend on, and any missing Maven components. (These are
downloaded from the JBoss repositories
- only once and saved on your machine. This means that the next time you run Maven,
all the libraries will
- already be available locally, and the build will run much faster.) The command
then continues by compiling the example's source
- code (and unit tests) and running the unit tests. The build is successful if you
see the following:
- <programlisting language="bash">$ mvn install
-...
-[INFO] ------------------------------------------------------------------------
-[INFO] Reactor Summary:
-[INFO] ------------------------------------------------------------------------
-[INFO] Getting Started examples .............................. SUCCESS [2.106s]
-[INFO] Sequencer Examples .................................... SUCCESS [9.768s]
-[INFO] ------------------------------------------------------------------------
-[INFO] ------------------------------------------------------------------------
-[INFO] BUILD SUCCESSFUL
-[INFO] ------------------------------------------------------------------------
-[INFO] Total time: 12 seconds
-[INFO] Finished at: Wed May 07 12:00:06 CDT 2008
-[INFO] Final Memory: 14M/28M
-[INFO] ------------------------------------------------------------------------
-$ </programlisting>
- If there are errors, check whether you have the correct version of Maven installed
and that you've correctly updated
- your Maven settings as described above.</para>
- <para>If you've successfully built the examples, there will be a
<code>examples/sequencers/target/dna-example-sequencers-basic.dir/</code>
- directory that contains the following:
- <itemizedlist>
- <listitem>
- <para><emphasis
role="strong"><code>run.sh</code></emphasis> is the *nix
shell script that will run the example.</para>
- </listitem>
- <listitem>
- <para>
- <emphasis role="strong">
- <code>log4j.properties</code>
- </emphasis>
- is the Log4J configuration file.
- </para>
- </listitem>
- <listitem>
- <para>
- <emphasis role="strong">
- <code>jackrabbitConfig.xml</code>
- </emphasis>
- is the Jackrabbit configuration file, which is set up to use a transient
in-memory repository.
- </para>
- </listitem>
- <listitem>
- <para>
- <emphasis role="strong">
- <code>jackrabbitNodeTypes.cnd</code>
- </emphasis>
- defines the additional JCR node types used by this example.
- </para>
- </listitem>
- <listitem>
- <para>
- <emphasis role="strong">
- <code>sample1.mp3</code>
- </emphasis>
- is a sample MP3 audio file you'll use later to upload into the repository.
- </para>
- </listitem>
- <listitem>
- <para>
- <emphasis role="strong">
- <code>caution.gif</code>
- </emphasis>, <emphasis role="strong">
- <code>caution.png</code>
- </emphasis>, and <emphasis role="strong">
- <code>caution.jpg</code>
- </emphasis>
- are images that you'll use later and upload into the repository.
- </para>
- </listitem>
- <listitem>
- <para>
- <emphasis role="strong">
- <code>lib</code>
- </emphasis>
- subdirectory contains the JARs for all of the JBoss DNA artifacts as well as
those for other libraries required
- by JBoss DNA and the example.
- </para>
- </listitem>
- </itemizedlist>
- <note>
- <para>JBoss DNA 0.1 and the examples are currently tested with <ulink
url="http://jackrabbit.apache.org/">Apache Jackrabbit</ulink> version
1.3.3.
- This version is stable and used by a number of other projects and applications.
However, you should be able to use a newer
- version of Jackrabbit, as long as that version uses the same JCR API. For example,
version 1.4.2 was released on March 26, 2008 and
- should be compatible.
- </para>
- <para>Just remember, if the version of Jackrabbit you want to use for these
examples is not in the Maven repository,
- you'll have to either add it or add it locally. For more information, see the
<ulink
url="http://maven.apache.org/">Maven documentation</ulink>.
- </para>
- </note>
- </para>
- </sect1>
- <sect1 id="running">
- <title>Running the example</title>
- <para>This example consists of a client application that sets up an in-memory
JCR repository and that allows a user to
- upload files into that repository. The client also sets up the DNA services with
two sequencers so that if any of the
- uploaded files are PNG, JPEG, GIF, BMP or other images, DNA will automatically
extract the image's metadata (e.g., image
- format, physical size, pixel density, etc.) and store that in the repository.
Alternatively, if the uploaded file
- is an MP3 audio file, DNA will extract some of the ID3 metadata (e.g., the author,
title, album, year and comment)
- and store that in the repository.</para>
- <para>
- To run the client application, go to the
- <code>examples/sequencers/target/dna-example-sequencers-basic.dir/
- </code>
- directory and type
- <code>./run.sh</code>
- . You should see the command-line client and its menus in your terminal:
- <figure id="xample-sequencer-cli-client">
- <title>Example Client</title>
- <graphic align="center" scale="100"
fileref="images/example-sequencer-cli-client.png" />
- </figure>
- From this menu, you can upload a file into the repository, search for media in the
repository, print sequencing statistics,
- or quit the application.
- </para>
- <para>
- The first step is to upload one of the example images. If you type 'u' and
press return, you'll be prompted to supply the
- path to the file you want to upload. Since the application is running from within
the
- <code>examples/sequencers/target/dna-example-sequencers-basic.dir/
- </code>
- directory, you can specify any of the files in that directory without specifying
the path:
- <figure id="example-sequencer-upload">
- <title>Uploading an image using the Example Client</title>
- <graphic align="center" scale="100"
fileref="images/example-sequencer-upload.png" />
- </figure>
- You can specify any fully-qualified or relative path. The application will notify
you if it cannot find the file you
- specified. The example client configures JBoss DNA to sequence and MP3 audio files
and image files with one of
- the following extensions (technically, nodes that have names ending in the
following):
- <code>jpg</code>
- ,
- <code>jpeg</code>
- ,
- <code>gif</code>
- ,
- <code>bmp</code>
- ,
- <code>pcx</code>
- ,
- <code>png</code>
- ,
- <code>iff</code>
- ,
- <code>ras</code>
- ,
- <code>pbm</code>
- ,
- <code>pgm</code>
- ,
- <code>ppm</code>
- , and
- <code>psd</code>
- . Files with other extensions in the repository path will be ignored. For your
convenience, the example provides several
- files that will be sequenced (
- <code>caution.png</code>
- ,
- <code>caution.jpg</code>
- ,
- <code>caution.gif</code>
- , and
- <code>sample1.mp3</code>
- ) and one image that will not be sequenced (
- <code>caution.pict</code>
- ). Feel free to try other files.
- </para>
- <para>
- After you have specified the file you want to upload, the example application asks
you where in the repository you'd like to
- place the file. (If you want to use the suggested location, just press
- <code>return</code>
- .) The client application uses the JCR API to upload the file to that location in
the repository, creating any nodes (of
- type
- <code>nt:folder</code>
- ) for any directories that don't exist, and creating a node (of type
- <code>nt:file</code>
- ) for the file. And, per the JCR specification, the application creates a
- <code>jcr:content</code>
- node (of type
- <code>nt:resource</code>
- ) under the file node. The file contents are placed on this
- <code>jcr:content</code>
- node in the
- <code>jcr:data</code>
- property. For example, if you specify
- <code>/a/b/caution.png</code>
- , the following structure will be created in the repository:<programlisting>
- /a (nt:folder)
- /b (nt:folder)
- /caution.png (nt:file)
- /jcr:content (nt:resource)
- @jcr:data = {contents of the file}
- @jcr:mimeType = {mime type of the file}
- @jcr:lastModified = {now}
- </programlisting>
- Other kinds of files are treated in a similar way.
- </para>
- <para>
- When the client uploads the file using the JCR API, DNA gets notified of the changes,
consults the sequencers to see whether
- any of them are interested in the new or updated content, and if so runs those
sequencers. The image sequencer processes image
- files for metadata, and any metadata found is stored under the
- <code>/images</code>
- branch of the repository. The MP3 sequencer processes MP3 audio files for metadata,
and any metadata found is stored under the
- <code>/mp3s</code>
- branch of the repository. All of this happens asynchronously, so any DNA activity
doesn't impede or slow down the client
- activities.
- </para>
- <para>
- So, after the file is uploaded, you can search the repository for the image metadata
using the "s" menu option:
- <figure id="example-sequencer-search">
- <title>Searching for media using the Example Client</title>
- <graphic align="center" scale="100"
fileref="images/example-sequencer-search.png" />
- </figure>
- Here are the search results after the <code>sample1.mp3</code> audio file
has been uploaded (to the <code>/a/b/sample1.mp3</code> location):
- <figure id="example-sequencer-search-with-mp3">
- <title>Searching for media using the Example Client</title>
- <graphic align="center" scale="100"
fileref="images/example-sequencer-search-with-mp3.png" />
- </figure>
- You can also display the sequencing statistics using the "d" menu option:
- <figure id="example-sequencer-statistics">
- <title>Sequencing statistics using the Example Client</title>
- <graphic align="center" scale="100"
fileref="images/example-sequencer-statistics.png" />
- </figure>
- These stats show how many nodes were sequenced, and how many nodes were skipped
because they didn't apply to the sequencer's
- criteria.
- </para>
- <note>
- <para>
- There will probably be more nodes skipped than sequenced, since there are more
- <code>nt:folder</code>
- and
- <code>nt:resource</code>
- nodes than there are
- <code>nt:file</code>
- nodes with acceptable names.
- </para>
- </note>
- <para>You can repeat this process with other files. Any file that isn't an
image or MP3 files (as recognized by the sequencing configurations
- that we'll describe later) will not be sequenced.</para>
- </sect1>
- <sect1 id="downloading_and_running_review">
- <title>Summarizing what we just did</title>
- <para>In this chapter you downloaded and installed the example application and
used it to upload files into a
- JCR repository. JBoss DNA automatically sequenced the image and/or MP3 files you
uploaded, extracted the metadata from the
- files, and stored that metadata inside the repository. The application allowed you
to see this metadata
- and the sequencing statistics.</para>
- <para>This application was very simplistic. In fact, running through the
example probably only took you a minute or two.
- So while this application won't win any awards, it does show the basics of what
JBoss DNA can do.</para>
- <para>In the <link linkend="using_dna">next
chapter</link> we'll venture into the code to get an understanding
- of how JBoss DNA actually works and how you can use it in your own
applications.</para>
- </sect1>
- </chapter>
-
- <!--
====================================================================================================
- Chapter
-
====================================================================================================
-->
-<chapter id="using_dna">
- <title>Using JBoss DNA</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>
- <note>
- <para>Configuring JBoss DNA sequencers is a bit more manual than is ideal. As
you'll see, JBoss DNA uses dependency
- injection to allow a great deal of flexibility in how it can be configured and
customized. However, the next release will
- provide a much easier mechanism for configuring not only the sequencer service but
also the upcoming federation engine and
- JCR implementation.</para>
- </note>
- <sect1 id="sequencing_service">
- <title>Configuring the Sequencing Service</title>
- <para>
- The JBoss DNA <emphasis>sequencing service</emphasis> is the component
that manages the <emphasis>sequencers</emphasis>
- , reacting to changes in JCR repositories and then running the appropriate
sequencers.
- This involves processing the changes on a node, determining which (if any)
sequencers should be run on that node,
- and for each sequencer constructing the execution environment, calling the
sequencer, and saving the information
- generated by the sequencer.</para>
- <para>To set up the sequencing service, an instance is created, and dependent
components are injected into
- the object. This includes among other things:
- <itemizedlist>
- <listitem>
- <para>An <emphasis>execution context</emphasis> that defines
the context in which the service runs, including
- a factory for JCR sessions given names of the repository and workspace. This
factory must be configured,
- and is how JBoss DNA knows about your JCR repositories and how to connect to
them. More on this a bit later.</para>
- </listitem>
- <listitem>
- <para>An optional <emphasis>factory for class
loaders</emphasis> used to load sequencers. If no factory is supplied,
- the service uses the current thread's context class loader (or if that is
null, the class loader that loaded the
- sequencing service class).</para>
- </listitem>
- <listitem>
- <para>An <code>java.util.concurrent.ExecutorService</code>
used to execute the sequencing activites. If none
- is supplied, a new single-threaded executor is created by calling
<code>Executors.newSingleThreadExecutor()</code>.
- (This can easily be changed by subclassing and overriding the
<code>SequencerService.createDefaultExecutorService()</code>
method.)</para>
- </listitem>
- <listitem>
- <para>Filters for sequencers and events. By default, all sequencers are
considered for "node added", "property added"
- and "property changed" events.</para>
- </listitem>
- </itemizedlist>
- </para>
- <para>As mentioned above, the <code>ExecutionContext</code>
provides access to a <code>SessionFactory</code> that is used
- by JBoss DNA to establish sessions to your JCR repositories. Two implementations
are available:
- <itemizedlist>
- <listitem>
- <para>The <code>JndiSessionFactory</code> looks up JCR
<code>Repository</code> instances in JNDI using
- names that are supplied when creating sessions. This implementation also has
methods to set the
- JCR <code>Credentials</code> for a given workspace
name.</para>
- </listitem>
- <listitem>
- <para>The <code>SimpleSessionFactory</code> has methods to
register the JCR <code>Repository</code> instances
- with names, as well as methods to set the JCR
<code>Credentials</code> for a given workspace name.</para>
- </listitem>
- </itemizedlist>
- You can use the <code>SimpleExecutionContext</code> implementation of
<code>ExecutionContext</code> and supply
- a <code>SessionFactory</code> instance, or you can provide your own
implementation.
- </para>
- <para>Here's an example of how to instantiate and configure the
SequencingService:
- <programlisting>
-SimpleSessionFactory sessionFactory = new SimpleSessionFactory();
-sessionFactory.registerRepository("Main Repository", this.repository);
-Credentials credentials = new SimpleCredentials("jsmith",
"secret".toCharArray());
-sessionFactory.registerCredentials("Main Repository/Workspace1", credentials);
-ExecutionContext executionContext = new SimpleExecutionContext(sessionFactory);
-
-// Create the sequencing service, passing in the execution context ...
-SequencingService sequencingService = new SequencingService();
-sequencingService.setExecutionContext(executionContext);</programlisting>
- </para>
- <para>After the sequencing service is created and configured, it must be
started. The SequencingService
- has an <emphasis>administration object</emphasis> (that is an instance
of <code>ServiceAdministrator</code>)
- with <code>start()</code>, <code>pause()</code>, and
<code>shutdown()</code> methods. The latter method will
- close the queue for sequencing, but will allow sequencing operations already
running to complete normally.
- To wait until all sequencing operations have completed, simply call the
<code>awaitTermination</code> method
- and pass it the maximum amount of time you want to wait.</para>
- <para>
- <programlisting>
-sequencingService.getAdministrator().start();</programlisting>
- </para>
- <para>The sequencing service must also be configured with the sequencers that
it will use. This is done using the
- <code>addSequencer(SequencerConfig)</code> method and passing a
<code>SequencerConfig</code> instance that
- you create. Here's an example:
- <programlisting>
-String name = "Image Sequencer";
-String desc = "Sequences image files to extract the characteristics of the
image";
-String classname = "org.jboss.dna.sequencer.images.ImageMetadataSequencer";
-String[] classpath = null; // Use the current classpath
-String[] pathExpressions =
{"//(*.(jpg|jpeg|gif|bmp|pcx|png))[*]/jcr:content[@jcr:data] =>
/images/$1"};
-SequencerConfig imageSequencerConfig = new SequencerConfig(name, desc, classname,
classpath, pathExpressions);
-sequencingService.addSequencer(imageSequencerConfig);
-
-name = "Mp3 Sequencer";
-desc = "Sequences mp3 files to extract the id3 tags of the audio file";
-classname = "org.jboss.dna.sequencer.mp3.Mp3MetadataSequencer";
-String[] mp3PathExpressions = {"//(*.mp3)[*]/jcr:content[@jcr:data] =>
/mp3s/$1"};
-SequencerConfig mp3SequencerConfig = new SequencerConfig(name, desc, classname,
classpath, mp3PathExpressions);
-sequencingService.addSequencer(mp3SequencerConfig);</programlisting>
- This is pretty self-explanatory, except for the <code>classpath</code>
and <code>pathExpression</code> parameters.
- The classpath parameter defines the classpath that is passed to the class loader
factory mentioned above.
- Our sequencer is on the classpath, so we can simply use
<code>null</code> here.
- </para>
- <para>The path expression is more complicated. Sequencer path expressions
are used by the sequencing service to
- determine whether a particular changed node should be sequenced. The expressions
consist of two parts: a selection
- criteria and an output expression. Here's a simple example:
- <programlisting>
-/a/b/c@title => /d/e/f</programlisting>
- Here, the <code>/a/b/c@title</code> is the selection criteria that
applies when the <code>/a/b/c</code> node has a <code>title</code>
property
- that is added or changed. When the selection criteria matches a change event, the
sequencer will be run
- and any generated output will be inserted into the repository described by the
output expression. In this example,
- the generated output would be placed at the <code>/d/e/f</code> node.
- </para>
- <note>
- <para>Sequencer path expressions can be fairly complex and may use
wildcards, specificy same-name sibling indexes,
- provide optional and choice elements, and may capture parts of the selection
criteria for use in the output expression.
- The path expression used in the image sequencer configuration example above shows
a more complex example:
- <programlisting>
-//(*.(jpg|jpeg|gif|bmp|pcx|png))[*]/jcr:content[@jcr:data] =>
/images/$1</programlisting>
- This uses "//" to select any node at any level in the repository whose
name ends with "." and one of the extensions (e.g., ".jpg",
".jpeg", etc.)
- and that has a child node named "jcr:content" that has a
"jcr:data" property. It also selects the file name
- as the first capture group (the first set of parentheses) for use in the output
expression.
- In this example, any sequencer output is placed on a node with that same file
name under the "/images" node.
- </para>
- <para></para>
- <para>Other things are possible, too. For example, the name of the
repository/workspace (as used by the <code>SessionFactory</code>)
- may be specified at the beginning of the select criteria and/or the output
expression. This means it's possible to place
- the sequencer output in a different repository than the node being
sequenced.</para>
- <para>For more detail about sequencer path expressions, see the
<code>org.jboss.dna.repository.sequencer.SequencerPathExpression</code>
- class and the corresponding
<code>org.jboss.dna.repository.sequencer.SequencerPathExpressionTest</code>
test case.</para>
- </note>
- <para>After the service is started, it is ready to start reacting to changes
in the repository. But it first
- must be wired to the repositories using a listener. This is accomplished using the
<code>ObservationService</code>
- described in the <link linkend="observation_service">next
section</link>.</para>
- </sect1>
- <sect1 id="observation_service">
- <title>Configuring the Observation Service</title>
- <para>The JBoss DNA <code>ObservationService</code> is responsible
for listening to one or more JCR repositories
- and multiplexing the events to its listeners. Unlike JCR events, this framework
embeds in the events the
- name of the repository and workspace that can be passed to a
<code>SessionFactory</code> to obtain a session
- to the repository in which the change occurred. This simple design makes it very
easy for JBoss DNA to
- concurrently work with multiple JCR repositories.</para>
- <para>Configuring an observation service is pretty easy, especially if you
reuse the same <code>SessionFactory</code>
- supplied to the sequencing service. Here's an example:
- <programlisting>
-this.observationService = new ObservationService(sessionFactory);
-this.observationService.getAdministrator().start();</programlisting>
- </para>
- <note>
- <para>Both <code>ObservationService</code> and
<code>SequencingService</code> implement
- <code>AdministeredService</code>, which has a
<code>ServiceAdministrator</code> used to start, pause, and shutdown the
- service. In other words, the lifecycle of the services are managed in the same
way.</para>
- </note>
- <para>
- After the observation service is started, listeners can be added. The
<code>SequencingService</code> implements the required
- interface, and so it may be registered directly:
- <programlisting>
-observationService.addListener(sequencingService);</programlisting>
- </para>
- <para>Finally, the observation service must be wired to monitor one of your JCR
repositories. This is done with
- one of the <code>monitor(...)</code> methods:
- <programlisting>
-int eventTypes = Event.NODE_ADDED | Event.PROPERTY_ADDED | Event.PROPERTY_CHANGED;
-observationService.monitor("Main Repository/Workspace1",
eventTypes);</programlisting>
- </para>
- <para>At this point, the observation service is listening to a JCR repository
and forwarding the appropriate events
- to the sequencing service, which will asynchronously process the changes and sequence
the information added to or changed in the repository.
- </para>
- </sect1>
- <sect1 id="shutting_down">
- <title>Shutting down JBoss DNA services</title>
- <para>The JBoss DNA services are utilizing resources and threads that must be
released before your application is ready to shut down.
- The safe way to do this is to simply obtain the
<code>ServiceAdministrator</code> for each service (via the
<code>getServiceAdministrator()</code> method)
- and call <code>shutdown()</code>. As previously mentioned, the shutdown
method will simply prevent new work from being processed
- and will not wait for existing work to be completed. If you want to wait until the
service completes all its work, you must wait
- until the service terminates. Here's an example that shows how this is done:
- <programlisting>
-// Shut down the service and wait until it's all shut down ...
-sequencingService.getAdministrator().shutdown();
-sequencingService.getAdministrator().awaitTermination(5, TimeUnit.SECONDS);
-
-// Shut down the observation service ...
-observationService.getAdministrator().shutdown();
-observationService.getAdministrator().awaitTermination(5,
TimeUnit.SECONDS);</programlisting>
- </para>
- <para>At this point, we've covered how to configure and use the JBoss DNA
services in your application.
- The next chapter goes back to the <link
linkend="downloading_and_running">sample application</link> to show how
all these pieces fit together.</para>
- </sect1>
- <sect1 id="example_application_review">
- <title>Reviewing the example application</title>
- <para>Recall that the example application consists of a client application that
sets up an in-memory JCR repository and
- that allows a user to upload files into that repository. The client also sets up
the DNA services with an image sequencer so
- that if any of the uploaded files are PNG, JPEG, GIF, BMP or other images, DNA will
automatically extract the image's
- metadata (e.g., image format, physical size, pixel density, etc.) and store that in
the repository. Or, if the client uploads
- MP3 audio files, the title, author, album, year, and comment are extracted from the
audio file and stored in the repository.</para>
- <para>
- The example is comprised of 3 classes and 1 interface, located in the
- <code>src/main/java</code>
- directory:
- <programlisting>
- org/jboss/example/dna/sequencers/ConsoleInput.java
- /MediaInfo.java
- /SequencingClient.java
- /UserInterface.java</programlisting>
- </para>
- <para>
- <code>SequencingClient</code>
- is the class that contains the main application.
- <code>MediaInfo</code>
- is a simple Java object that encapsulates metadata about a media file (as generated
by the sequencer), and used by the client to
- pass information to the
- <code>UserInterface</code>
- , which is an interface with methods that will be called at runtime to request data
from the user.
- <code>ConsoleInput</code>
- is an implementation of this that creates a text user interface, allowing the user
to operate the client from the command-line.
- We can easily create a graphical implementation of
- <code>UserInterface</code>
- at a later date. We can also create a mock implementation for testing purposes that
simulates a user entering data. This
- allows us to check the behavior of the client automatically using conventional
JUnit test cases, as demonstrated by the
- code in the
- <code>src/test/java</code>
- directory:
- <programlisting>
- org/jboss/example/dna/sequencers/SequencingClientTest.java
- /MockUserInterface.java</programlisting>
- </para>
- <para>
- If we look at the
- <code>SequencingClient</code>
- code, there are a handful of methods that encapsulate the various activities.
- </para>
- <note>
- <para>To keep the code shown in this book as readable as possible, some of
the comments and error handling
- have been removed.</para>
- </note>
- <para>
- The
- <code>startRepository()</code>
- method starts up an in-memory Jackrabbit JCR repository. The bulk of this method is
simply gathering and passing the
- information required by Jackrabbit. Because Jackrabbit's
- <code>TransientRepository</code>
- implementation shuts down after the last session is closed, the application
maintains a session to ensure that the
- repository remains open throughout the application's lifetime. And finally, the
node type needed by the image sequencer is
- registered with Jackrabbit.
- </para>
- <programlisting>
-public void startRepository() throws Exception {
- if (this.repository == null) {
- try {
-
- // Load the Jackrabbit configuration ...
- File configFile = new File(this.jackrabbitConfigPath);
- String pathToConfig = configFile.getAbsolutePath();
-
- // Find the directory where the Jackrabbit repository data will be stored
...
- File workingDirectory = new File(this.workingDirectory);
- String workingDirectoryPath = workingDirectory.getAbsolutePath();
-
- // Get the Jackrabbit custom node definition (CND) file ...
- URL cndFile =
Thread.currentThread().getContextClassLoader().getResource("jackrabbitNodeTypes.cnd");
-
- // Create the Jackrabbit repository instance and establish a session to keep
the repository alive ...
- this.repository = new TransientRepository(pathToConfig,
workingDirectoryPath);
- if (this.username != null) {
- Credentials credentials = new SimpleCredentials(this.username,
this.password);
- this.keepAliveSession = this.repository.login(credentials,
this.workspaceName);
- } else {
- this.keepAliveSession = this.repository.login();
- }
-
- try {
- // Register the node types (only valid the first time) ...
- JackrabbitNodeTypeManager mgr =
(JackrabbitNodeTypeManager)this.keepAliveSession.getWorkspace().getNodeTypeManager();
- mgr.registerNodeTypes(cndFile.openStream(),
JackrabbitNodeTypeManager.TEXT_X_JCR_CND);
- } catch (RepositoryException e) {
- if (!e.getMessage().contains("already exists")) throw e;
- }
-
- } catch (Exception e) {
- this.repository = null;
- this.keepAliveSession = null;
- throw e;
- }
- }
-}</programlisting>
- <para>As you can see, this method really has nothing to do with JBoss DNA,
other than setting up a JCR repository that JBoss
- DNA will use.</para>
- <para>
- The
- <code>shutdownRepository()</code>
- method shuts down the Jackrabbit transient repository by closing the
"keep-alive session". Again, this method really does
- nothing specifically with JBoss DNA, but is needed to manage the JCR repository
that JBoss DNA uses.
- <programlisting>
-public void shutdownRepository() throws Exception {
- if (this.repository != null) {
- try {
- this.keepAliveSession.logout();
- } finally {
- this.repository = null;
- this.keepAliveSession = null;
- }
- }
-}</programlisting>
- </para>
- <para>
- The
- <code>startDnaServices()</code>
- method first starts the JCR repository (if it was not already started), and
proceeds to create and configure the
- <code>SequencingService</code>
- as described
- <link linkend="sequencing_service">earlier</link>
- . This involes setting up the
- <code>SessionFactory</code>
- and
- <code>ExecutionContext</code>
- , creating the
- <code>SequencingService</code>
- instance, and configuring the image sequencer. The method then continues by setting
up the
- <code>ObservationService</code>
- as described
- <link linkend="observation_service">earlier</link>
- and starting the service.
- <programlisting>
-public void startDnaServices() throws Exception {
- if (this.repository == null) this.startRepository();
- if (this.sequencingService == null) {
-
- SimpleSessionFactory sessionFactory = new SimpleSessionFactory();
- sessionFactory.registerRepository(this.repositoryName, this.repository);
- if (this.username != null) {
- Credentials credentials = new SimpleCredentials(this.username,
this.password);
- sessionFactory.registerCredentials(this.repositoryName + "/" +
this.workspaceName, credentials);
- }
- this.executionContext = new SimpleExecutionContext(sessionFactory);
-
- // Create the sequencing service, passing in the execution context ...
- this.sequencingService = new SequencingService();
- this.sequencingService.setExecutionContext(executionContext);
-
- // Configure the sequencers.
- 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 imageSequencerConfig = new SequencerConfig(name, desc, classname,
classpath, pathExpressions);
- this.sequencingService.addSequencer(imageSequencerConfig);
-
- // Set up the MP3 sequencer ...
- name = "Mp3 Sequencer";
- desc = "Sequences mp3 files to extract the id3 tags of the audio
file";
- classname = "org.jboss.dna.sequencer.mp3.Mp3MetadataSequencer";
- String[] mp3PathExpressions = {"//(*.mp3)[*]/jcr:content[@jcr:data] =>
/mp3s/$1"};
- SequencerConfig mp3SequencerConfig = new SequencerConfig(name, desc, classname,
classpath, mp3PathExpressions);
- this.sequencingService.addSequencer(mp3SequencerConfig);
-
- // Use the DNA observation service to listen to the JCR repository (or multiple
ones), and
- // then register the sequencing service as a listener to this observation
service...
- this.observationService = new
ObservationService(this.executionContext.getSessionFactory());
- this.observationService.getAdministrator().start();
- this.observationService.addListener(this.sequencingService);
- this.observationService.monitor(this.repositoryName + "/" +
this.workspaceName, Event.NODE_ADDED | Event.PROPERTY_ADDED | Event.PROPERTY_CHANGED);
- }
- // Start up the sequencing service ...
- this.sequencingService.getAdministrator().start();
-}</programlisting>
- </para>
- <para>
- The
- <code>shutdownDnaServices()</code>
- method is pretty straightforward: it just calls shutdown on each of the services
and waits until they terminate.
- <programlisting>
-public void shutdownDnaServices() throws Exception {
- if (this.sequencingService == null) return;
-
- // Shut down the service and wait until it's all shut down ...
- this.sequencingService.getAdministrator().shutdown();
- this.sequencingService.getAdministrator().awaitTermination(5, TimeUnit.SECONDS);
-
- // Shut down the observation service ...
- this.observationService.getAdministrator().shutdown();
- this.observationService.getAdministrator().awaitTermination(5, TimeUnit.SECONDS);
-}</programlisting>
- </para>
- <para>None of the other methods really do anything with JBoss DNA
<emphasis>per se</emphasis>. Instead, they merely work with the repository
- using the JCR API.</para>
- <para>
- The <code>main</code> method of the
<code>SequencingClient</code> class creates a
<code>SequencingClient</code> instance,
- and passes a new <code>ConsoleInput</code> instance:
- <programlisting>
-public static void main( String[] args ) throws Exception {
- SequencingClient client = new SequencingClient();
- client.setRepositoryInformation("repo", "default",
"jsmith", "secret".toCharArray());
- client.setUserInterface(new ConsoleInput(client));
-}</programlisting>
- </para>
- <para>If we look at the
- <code>ConsoleInput</code>
- constructor, it starts the repository, the DNA services, and a thread for the user
interface. At this point, the constructor
- returns, but the main application continues under the user interface thread. When
the user requests to quit,
- the user interface thread also shuts down the DNA services and JCR repository.
- <programlisting>
-public ConsoleInput( SequencerClient client ) {
- try {
- client.startRepository();
- client.startDnaServices();
-
- System.out.println(getMenu());
- Thread eventThread = new Thread(new Runnable() {
- private boolean quit = false;
- public void run() {
- try {
- while (!quit) {
- // Display the prompt and process the requested operation ...
- }
- } finally {
- try {
- // Terminate ...
- client.shutdownDnaServices();
- client.shutdownRepository();
- } catch (Exception err) {
- System.out.println("Error shutting down sequencing service and
repository: " + err.getLocalizedMessage());
- err.printStackTrace(System.err);
- }
- }
- }
- });
- eventThread.start();
- } catch (Exception err) {
- System.out.println("Error: " + err.getLocalizedMessage());
- err.printStackTrace(System.err);
- }
-}</programlisting>
- </para>
- <para>At this point, we've reviewed all of the interesting code in the
example application. However, feel free
- to play with the application, trying different things.
- </para>
- </sect1>
- <sect1 id="using_dna_review">
- <title>Summarizing what we just did</title>
- <para>In this chapter we covered the different JBoss DNA components and how
they can be used in your application.
- Specifically, we described how the <code>SequencingService</code> and
<code>ObservationService</code> can
- be configured and used. And we ended the chapter by reviewing the example
application, which not only uses
- JBoss DNA, but also the repository via the JCR API.
- </para>
- </sect1>
-</chapter>
-
-<!--
====================================================================================================
- Chapter
-
====================================================================================================
-->
-<chapter id="custom_sequencers">
- <title>Creating custom sequencers</title>
- <para>The current release of JBoss DNA comes with two sequencers: one that
extracts metadata from a variety of image file formats,
- and another that extracts some of the ID3 metadata from MP3 audio files. However,
it's very easy to create your own
- sequencers and to then configure JBoss DNA to use them in your own application.
- </para>
- <para>
- Creating a custom sequencer involves the following steps:
- <itemizedlist>
- <listitem>
- <para>Create a Maven 2 project for your sequencer;</para>
- </listitem>
- <listitem>
- <para>Implement the
<code>org.jboss.dna.graph.sequencers.StreamSequencer</code> interface with
your own implementation, and create unit tests to verify
- the functionality and expected behavior;</para>
- </listitem>
- <listitem>
- <para>Add the sequencer configuration to the JBoss DNA
<code>SequencingService</code> in your application
- as described in the <link linkend="using_dna">previous
chapter</link>; and</para>
- </listitem>
- <listitem>
- <para>Deploy the JAR file with your implementation (as well as any
dependencies), and make them available to JBoss DNA
- in your application.</para>
- </listitem>
- </itemizedlist>
- It's that simple.
- </para>
- <sect1 id="custom_sequencer_project">
- <title>Creating the Maven 2 project</title>
- <para>The first step is to create the Maven 2 project that you can use to
compile your code and build the JARs.
- Maven 2 automates a lot of the work, and since you're already <link
linkend="downloading_and_running">set up to use Maven</link>,
- using Maven for your project will save you a lot of time and effort. Of course, you
don't have to use Maven 2, but then you'll
- have to get the required libraries and manage the compiling and building process
yourself.</para>
- <note>
- <para>JBoss DNA may provide in the future a Maven archetype for creating
sequencer projects. If you'd find this useful
- and would like to help create it, please <link
linkend="preface">join the community</link>.</para>
- </note>
- <note>
- <para>The <code>dna-sequencer-images</code> project is a small,
self-contained sequencer implementation that
- has only the minimal dependencies. 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="http://anonsvn.jboss.org/repos/dna/trunk/sequencers/dna-sequenc...
- </para>
- </note>
- <para>You can create your Maven project any way you'd like. For examples,
see the <ulink
url="http://maven.apache.org/guides/getting-started/index.html#How_d...
2 documentation</ulink>.
- Once you've done that, just add the dependencies in your project's
<code>pom.xml</code> dependencies section:
- <programlisting>
-<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>
- 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:
-<programlisting>
-<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>
- Testing JBoss DNA sequencers does not require a JCR repository or the JBoss DNA
services. (For more detail,
- see the <link linkend="testing_custom_sequencers">testing
section</link>.) However, if you want to do
- integration testing with a JCR repository and the JBoss DNA services, you'll need
additional dependencies for these libraries.
-<programlisting>
-<dependency>
- <groupId>org.jboss.dna</groupId>
- <artifactId>dna-repository</artifactId>
- <version>0.1</version>
- <scope>test</scope>
-</dependency>
-<!-- Java Content Repository API -->
-<dependency>
- <groupId>javax.jcr</groupId>
- <artifactId>jcr</artifactId>
- <version>1.0.1</version>
- <scope>test</scope>
-</dependency>
-<!-- Apache Jackrabbit (JCR Implementation) -->
-<dependency>
- <groupId>org.apache.jackrabbit</groupId>
- <artifactId>jackrabbit-api</artifactId>
- <version>1.3.3</version>
- <scope>test</scope>
- <!-- Exclude these since they are included in JDK 1.5 -->
- <exclusions>
- <exclusion>
- <groupId>xml-apis</groupId>
- <artifactId>xml-apis</artifactId>
- </exclusion>
- <exclusion>
- <groupId>xerces</groupId>
- <artifactId>xercesImpl</artifactId>
- </exclusion>
- </exclusions>
-</dependency>
-<dependency>
- <groupId>org.apache.jackrabbit</groupId>
- <artifactId>jackrabbit-core</artifactId>
- <version>1.3.3</version>
- <scope>test</scope>
- <!-- Exclude these since they are included in JDK 1.5 -->
- <exclusions>
- <exclusion>
- <groupId>xml-apis</groupId>
- <artifactId>xml-apis</artifactId>
- </exclusion>
- <exclusion>
- <groupId>xerces</groupId>
- <artifactId>xercesImpl</artifactId>
- </exclusion>
- </exclusions>
-</dependency>
-</programlisting>
- </para>
- <para>At this point, your project should be set up correctly, and you're
ready to move on to
- <link linkend="custom_sequencer_implementation">writing the Java
implementation</link> for your sequencer.
- </para>
- </sect1>
- <sect1 id="custom_sequencer_implementation">
- <title>Implementing the StreamSequencer interface</title>
- <para>After creating the project and setting up the dependencies, the next step
is to create a Java class that implements
- the <code>org.jboss.dna.graph.sequencers.StreamSequencer</code>
interface. This interface is very straightforward
- and involves a single method:
- <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 progressMonitor the progress monitor that should be kept
- * updated with the sequencer's progress and that should be
- * frequently consulted as to whether this operation has been cancelled.
- */
- void sequence( InputStream stream, SequencerOutput output,
- ProgressMonitor progressMonitor );</programlisting>
- </para>
- <para>The job of a stream sequencer is to process the data in the supplied
stream, and place into the <code>SequencerOutput</code>
- any information that is to go into the JCR repository. JBoss DNA figures out when
your sequencer should be called
- (of course, using the sequencing configuration you'll add in a bit), and then
makes sure the generated information
- is saved in the correct place in the repository.
- </para>
- <para>The <code>SequencerOutput</code> class is fairly easy to use.
There are basically two methods you need to call.
- One method sets the property values, while the other sets references to other nodes
in the repository. Use these
- methods to describe the properties of the nodes you want to create, using relative
paths for the nodes and
- valid JCR property names for properties and references. JBoss DNA will ensure that
nodes are created or updated
- whenever they're needed.
- <programlisting>
-public interface SequencerOutput {
-
- /**
- * Set the supplied property on the supplied node. The allowable
- * values are any of the following:
- * - primitives (which will be autoboxed)
- * - String instances
- * - String arrays
- * - byte arrays
- * - InputStream instances
- * - Calendar instances
- *
- * @param nodePath the path to the node containing the property;
- * may not be null
- * @param property the name of the property to be set
- * @param values the value(s) for the property; may be empty if
- * any existing property is to be removed
- */
- void setProperty( String nodePath, String property,
- Object... values );
-
- /**
- * Set the supplied reference on the supplied node.
- *
- * @param nodePath the path to the node containing the property;
- * may not be null
- * @param property the name of the property to be set
- * @param paths the paths to the referenced property, which may be
- * absolute paths or relative to the sequencer output node;
- * may be empty if any existing property is to be removed
- */
- void setReference( String nodePath, String property,
- String... paths );
-}</programlisting>
- </para>
- <para>JBoss DNA will create nodes of type
<code>nt:unstructured</code> unless you specify the value for the
- <code>jcr:primaryType</code> property. You can also specify the values
for the <code>jcr:mixinTypes</code> property
- if you want to add mixins to any node.
- </para>
- <para>For a complete example of a sequencer, let's look at the
<code>org.jboss.dna.sequencers.image.ImageMetadataSequencer</code>
implementation:
- <programlisting>
-public class ImageMetadataSequencer implements StreamSequencer {
-
- public static final String METADATA_NODE = "image:metadata";
- public static final String IMAGE_PRIMARY_TYPE = "jcr:primaryType";
- public static final String IMAGE_MIXINS = "jcr:mixinTypes";
- public static final String IMAGE_MIME_TYPE = "jcr:mimeType";
- public static final String IMAGE_ENCODING = "jcr:encoding";
- public static final String IMAGE_FORMAT_NAME = "image:formatName";
- public static final String IMAGE_WIDTH = "image:width";
- public static final String IMAGE_HEIGHT = "image:height";
- public static final String IMAGE_BITS_PER_PIXEL = "image:bitsPerPixel";
- public static final String IMAGE_PROGRESSIVE = "image:progressive";
- public static final String IMAGE_NUMBER_OF_IMAGES =
"image:numberOfImages";
- public static final String IMAGE_PHYSICAL_WIDTH_DPI =
"image:physicalWidthDpi";
- public static final String IMAGE_PHYSICAL_HEIGHT_DPI =
"image:physicalHeightDpi";
- public static final String IMAGE_PHYSICAL_WIDTH_INCHES =
"image:physicalWidthInches";
- public static final String IMAGE_PHYSICAL_HEIGHT_INCHES =
"image:physicalHeightInches";
-
- /**
- * {@inheritDoc}
- */
- public void sequence( InputStream stream, SequencerOutput output,
- ProgressMonitor progressMonitor ) {
- progressMonitor.beginTask(10, ImageSequencerI18n.sequencerTaskName);
-
- 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;
- }
- progressMonitor.worked(5);
- if (progressMonitor.isCancelled()) return;
-
- // Generate the output graph if we found useful metadata ...
- if (metadata != null) {
- // Place the image metadata into the output map ...
- output.setProperty(METADATA_NODE, IMAGE_PRIMARY_TYPE,
"image:metadata");
- // output.psetProperty(METADATA_NODE, IMAGE_MIXINS, "");
- output.setProperty(METADATA_NODE, IMAGE_MIME_TYPE, metadata.getMimeType());
- // output.setProperty(METADATA_NODE, IMAGE_ENCODING, "");
- output.setProperty(METADATA_NODE, IMAGE_FORMAT_NAME,
metadata.getFormatName());
- output.setProperty(METADATA_NODE, IMAGE_WIDTH, metadata.getWidth());
- output.setProperty(METADATA_NODE, IMAGE_HEIGHT, metadata.getHeight());
- output.setProperty(METADATA_NODE, IMAGE_BITS_PER_PIXEL,
metadata.getBitsPerPixel());
- output.setProperty(METADATA_NODE, IMAGE_PROGRESSIVE,
metadata.isProgressive());
- output.setProperty(METADATA_NODE, IMAGE_NUMBER_OF_IMAGES,
metadata.getNumberOfImages());
- output.setProperty(METADATA_NODE, IMAGE_PHYSICAL_WIDTH_DPI,
metadata.getPhysicalWidthDpi());
- output.setProperty(METADATA_NODE, IMAGE_PHYSICAL_HEIGHT_DPI,
metadata.getPhysicalHeightDpi());
- output.setProperty(METADATA_NODE, IMAGE_PHYSICAL_WIDTH_INCHES,
metadata.getPhysicalWidthInch());
- output.setProperty(METADATA_NODE, IMAGE_PHYSICAL_HEIGHT_INCHES,
metadata.getPhysicalHeightInch());
- }
-
- progressMonitor.done();
- }
-}</programlisting>
- </para>
- <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>
- <para>Also note how the progress monitor is used. Reporting progress through
the supplied <code>ProgressMonitor</code> is very easy, and it ensures that
JBoss DNA
- can accurately monitor and report the status of sequencing activities to the users.
At the beginning of the operation, call
- <code>beginTask(...)</code> with a meaningful message describing
- the operation and a total for the amount of work that will be done by this
sequencer. Then perform the sequencing work,
- periodically reporting work by specifying the incremental amount of work with the
<code>worked(double)</code> method, or
- by creating a subtask with the <code>createSubtask(double)</code> method
and reporting work against that subtask
- monitor.
- </para>
- <para>Your method should periodically use the ProgressMonitor's
<code>isCancelled()</code> method to check whether the operation has been
- cancelled.. If this method returns true, the implementation should abort all work
as
- soon as possible and close any resources that were acquired or opened.
- </para>
- <para>
- Finally, when your sequencing operation is completed, it should call
<code>done()</code> on the progress monitor.
- </para>
- </sect1>
- <sect1 id="testing_custom_sequencers">
- <title>Testing custom sequencers</title>
- <para>The sequencing framework was designed to make testing sequencers much
easier. In particular, the
- <code>StreamSequencer</code> interface does not make use of the JCR API.
So instead of requiring a fully-configured
- JCR repository and JBoss DNA system, unit tests for a sequencer can focus on testing
that the content is
- processed correctly and the desired output graph is generated.</para>
- <note>
- <para>For a complete example of a sequencer unit test, see the
<code>ImageMetadataSequencerTest</code> unit test
- in the <code>org.jboss.dna.sequencer.images</code> package of the
<code>dna-sequencers-image</code> project.
- </para>
- </note>
- <para>The following code fragment shows one way of testing a sequencer, using
JUnit 4.4 assertions and
- some of the classes made available by JBoss DNA. Of course,
- this example code does not do any error handling and does not make all the
assertions a real test would.
- <programlisting>
-Sequencer sequencer = new ImageMetadataSequencer();
-MockSequencerOutput output = new MockSequencerOutput();
-ProgressMonitor progress = new SimpleProgressMonitor("Test activity");
-InputStream stream = null;
-try {
- stream =
this.getClass().getClassLoader().getResource("caution.gif").openStream();
- sequencer.sequence(stream,output,progress); // 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>
- <para>It's also useful to test that a sequencer produces no output for
something it should not understand:
- <programlisting>
-Sequencer sequencer = new ImageMetadataSequencer();
-MockSequencerOutput output = new MockSequencerOutput();
-ProgressMonitor progress = new SimpleProgressMonitor("Test activity");
-InputStream stream = null;
-try {
- stream =
this.getClass().getClassLoader().getResource("caution.pict").openStream();
- sequencer.sequence(stream,output,progress); // writes to 'output'
- assertThat(output.hasProperties(), is(false));
- assertThat(output.hasReferences(), is(false));
-} finally {
- stream.close();
-}</programlisting>
- </para>
- <para>These are just two simple tests that show ways of testing a sequencer.
Some tests may get quite involved,
- especially if a lot of output data is produced.
- </para>
- <para>It may also be useful to create some integration tests
- that <link linkend="using_dna">configure JBoss DNA</link> to
use a custom sequencer, and to then upload
- content using the JCR API, verifying that the custom sequencer did run. However,
remember that JBoss DNA
- runs sequencers asynchronously in the background, and you must sychronize your tests
to ensure that the
- sequencers have a chance to run before checking the results. (One way of doing this
(although, granted, not always reliable) is to wait for a second
- after uploading your content, shutdown the <code>SequencingService</code>
and await its termination,
- and then check that the sequencer output has been saved to the JCR repository. For
an example of this technique,
- see the <code>SequencingClientTest</code> unit test in the example
application.)
- </para>
- </sect1>
- <sect1 id="deploying_custom_sequencers">
- <title>Deploying custom sequencers</title>
- <para>The first step of deploying a sequencer consists of adding/changing the
sequencer configuration (e.g., <code>SequencerConfig</code>)
- in the <code>SequencingService</code>. This was covered in the <link
linkend="sequencing_service">previous chapter</link>.
- </para>
- <para>
- The second step is to make the sequencer implementation available to JBoss DNA. At
this time, the JAR containing
- your new sequencer, as well as any JARs that your sequencer depends on, should be
placed on your application classpath.</para>
- <note>
- <para>A future goal of JBoss DNA is to allow sequencers, connectors, and
other extensions to be easily deployed into
- a runtime repository. This process will not only be much simpler, but it will
also provide JBoss DNA
- with the information necessary to update configurations and create the
appropriate class loaders for each extension.
- Having separate class loaders for each extension helps prevent the pollution of
the common classpath,
- facilitates an isolated runtime environment to eliminate any dependency
conflicts, and may potentially
- enable hot redeployment of newer extension versions.
- </para>
- </note>
- </sect1>
-</chapter>
-
-<!--
====================================================================================================
- Chapter
-
====================================================================================================
-->
-<chapter id="future_directions">
- <title>Looking to the future</title>
- <para>What's next for JBoss DNA? Well, the sequencing system is just the
beginning. With this release, the sequencing system
- is stable enough so that more <link
linkend="sequencers">sequencers</link> can be developed and used within
your own applications.
- If you're interested in getting involved with the JBoss DNA project, consider
picking up one of the sequencers on our
- <ulink
url="http://jira.jboss.org/jira/browse/DNA?report=com.atlassian.jira...;.
- Or, check out <ulink
url="http://jira.jboss.org/jira/secure/IssueNavigator.jspa?reset=tru...
- for the list of sequencers we've thought of. If you think of one that's not
there, please add it to JIRA!
- </para>
- <para>
- The next release will focus on creating the <link
linkend="federation">federation engine</link> and connectors
- for several popular and ubiquitous systems. The 0.2 release will likely only federate
information in a read-only manner,
- but updates will soon follow. Also, during the early part of the next release, the
JBoss DNA project will switch to use JDK 6.
- Java 5 is being end-of-lifed, so we want to move to a supported JDK. However, a number
of JBoss projects and products continue to
- require Java 5, so our next release will most likely use JDK 6 with Java 5
compatibility.</para>
- <para>
- Other components on our roadmap include a web user interface, a REST-ful server, and a
view system that allows domain-specific
- views of information in the repository. These components are farther out on our
roadmap, and at this time have not been
- targeted to a particular release. If any of these are of interest to you, please
<link linkend="preface">get involved</link> in the community.
- </para>
-</chapter>
-</book>
\ No newline at end of file
Deleted: trunk/dna-graph/src/test/resources/plugin.xml
===================================================================
--- trunk/dna-graph/src/test/resources/plugin.xml 2008-10-29 17:48:23 UTC (rev 597)
+++ trunk/dna-graph/src/test/resources/plugin.xml 2008-10-29 18:23:01 UTC (rev 598)
@@ -1,91 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<?eclipse version="3.0"?>
-<!-- (c) 2007 Varsity Gateway LLC. All rights reserved. -->
-<plugin
- id="com.metamatrix.metamodels.transformation"
- name="%pluginName"
- version="5.5.1"
- provider-name="%providerName"
- class="com.metamatrix.metamodels.transformation.TransformationPlugin">
-
- <runtime>
- <library name="metamodelsTransformation.jar">
- <export name="*"/>
- </library>
- </runtime>
- <requires>
- <import plugin="org.eclipse.emf.mapping" export="true"/>
- <import plugin="org.eclipse.emf.ecore" export="true"/>
- <import plugin="org.eclipse.emf.edit" export="true"/>
- <import plugin="org.eclipse.emf.ecore.edit"
export="true"/>
- <import plugin="org.eclipse.core.runtime"
export="true"/>
- <import plugin="com.metamatrix.core" export="true"/>
- <import plugin="com.metamatrix.metamodels.core"
export="true"/>
- </requires>
-
-
-<!--
- Each extension below represents a single metamodel. Each metmodel is
- registered using under the specified <uri> value which must be the same
- eNS_URI value defined in the EPackage class for that metamodel.
- -->
- <extension
- id="transformation"
- name="%metamodelName"
- point="com.metamatrix.modeler.core.metamodel">
- <uri>
-
http://www.metamatrix.com/metamodels/Transformation
- </uri>
- <alternateUri>
- mtkplugin:///com.metamatrix.metamodels.Transformation
- </alternateUri>
- <packageClass
-
name="com.metamatrix.metamodels.transformation.TransformationPackage">
- </packageClass>
- <adapterClass
-
name="com.metamatrix.metamodels.transformation.provider.TransformationItemProviderAdapterFactory">
- </adapterClass>
- <properties
- createAsPhysical="false"
- requiresProxies="false"
- participatoryOnly="true"
- createAsVirtual="false"
- supportsDiagrams="false"
- supportsExtension="true">
- </properties>
- </extension>
- <extension
- point="org.eclipse.emf.ecore.generated_package">
- <package
-
uri="http://www.metamatrix.com/metamodels/Transformation"
-
class="com.metamatrix.metamodels.transformation.TransformationPackage">
- </package>
- </extension>
-
- <extension
- id="mapping"
- name="%mappingMetamodelName"
- point="com.metamatrix.modeler.core.metamodel">
- <uri>
-
http://www.eclipse.org/emf/2002/Mapping
- </uri>
- <alternateUri>
-
mtkplugin:///www.eclipse.org/emf/2002/Mapping
- </alternateUri>
- <packageClass
- name="org.eclipse.emf.mapping.MappingPackage">
- </packageClass>
- <adapterClass
-
name="org.eclipse.emf.mapping.provider.MappingItemProviderAdapterFactory">
- </adapterClass>
- <properties
- createAsPhysical="false"
- requiresProxies="false"
- participatoryOnly="true"
- createAsVirtual="false"
- supportsDiagrams="false"
- supportsExtension="false">
- </properties>
- </extension>
-
-</plugin>
Property changes on: trunk/extensions/dna-sequencer-xml
___________________________________________________________________
Name: svn:ignore
+ target
Added: trunk/extensions/dna-sequencer-xml/.classpath
===================================================================
--- trunk/extensions/dna-sequencer-xml/.classpath (rev 0)
+++ trunk/extensions/dna-sequencer-xml/.classpath 2008-10-29 18:23:01 UTC (rev 598)
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+ <classpathentry kind="src" path="src/main/java"/>
+ <classpathentry kind="src" path="src/main/resources"/>
+ <classpathentry kind="src" output="target/test-classes"
path="src/test/java"/>
+ <classpathentry kind="src" output="target/test-classes"
path="src/test/resources"/>
+ <classpathentry kind="con"
path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
+ <classpathentry kind="con"
path="org.maven.ide.eclipse.MAVEN2_CLASSPATH_CONTAINER"/>
+ <classpathentry kind="output" path="target/classes"/>
+</classpath>
Added: trunk/extensions/dna-sequencer-xml/.project
===================================================================
--- trunk/extensions/dna-sequencer-xml/.project (rev 0)
+++ trunk/extensions/dna-sequencer-xml/.project 2008-10-29 18:23:01 UTC (rev 598)
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>dna-sequencer-xml</name>
+ <comment></comment>
+ <projects>
+ </projects>
+ <buildSpec>
+ <buildCommand>
+ <name>org.maven.ide.eclipse.maven2Builder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.jdt.core.javabuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ </buildSpec>
+ <natures>
+ <nature>org.eclipse.jdt.core.javanature</nature>
+ <nature>org.maven.ide.eclipse.maven2Nature</nature>
+ </natures>
+</projectDescription>
Added: trunk/extensions/dna-sequencer-xml/pom.xml
===================================================================
--- trunk/extensions/dna-sequencer-xml/pom.xml (rev 0)
+++ trunk/extensions/dna-sequencer-xml/pom.xml 2008-10-29 18:23:01 UTC (rev 598)
@@ -0,0 +1,92 @@
+<project
xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/maven-v4_0_0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <groupId>org.jboss.dna</groupId>
+ <artifactId>dna</artifactId>
+ <version>0.3-SNAPSHOT</version>
+ <relativePath>../..</relativePath>
+ </parent>
+ <!-- The groupId and version values are inherited from parent -->
+ <artifactId>dna-sequencer-xml</artifactId>
+ <packaging>jar</packaging>
+ <name>JBoss DNA XML Sequencer</name>
+ <description>JBoss DNA Sequencer that processes XML files</description>
+ <
url>http://labs.jboss.org/dna</url>
+
+ <!--
+ Define the dependencies. Note that all version and scopes default to those
+ defined in the dependencyManagement section of the parent pom.
+ -->
+ <dependencies>
+ <!--
+ Common
+ -->
+ <dependency>
+ <groupId>org.jboss.dna</groupId>
+ <artifactId>dna-common</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.jboss.dna</groupId>
+ <artifactId>dna-graph</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.jboss.dna</groupId>
+ <artifactId>dna-common</artifactId>
+ <version>${pom.version}</version>
+ <type>test-jar</type>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.jboss.dna</groupId>
+ <artifactId>dna-graph</artifactId>
+ <version>${pom.version}</version>
+ <type>test-jar</type>
+ <scope>test</scope>
+ </dependency>
+ <!--
+ Testing (note the scope)
+ -->
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.hamcrest</groupId>
+ <artifactId>hamcrest-library</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.mockito</groupId>
+ <artifactId>mockito-all</artifactId>
+ </dependency>
+ <!--
+ Logging (require SLF4J API for compiling, but use Log4J and its SLF4J binding for
testing)
+ -->
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-api</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-log4j12</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>log4j</groupId>
+ <artifactId>log4j</artifactId>
+ </dependency>
+ <!--
+ Java Concurrency in Practice annotations
+ -->
+ <dependency>
+ <groupId>net.jcip</groupId>
+ <artifactId>jcip-annotations</artifactId>
+ </dependency>
+ </dependencies>
+ <reporting>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-surefire-report-plugin</artifactId>
+ </plugin>
+ </plugins>
+ </reporting>
+</project>
\ No newline at end of file
Added:
trunk/extensions/dna-sequencer-xml/src/main/java/org/jboss/dna/sequencer/xml/DnaDtdLexicon.java
===================================================================
---
trunk/extensions/dna-sequencer-xml/src/main/java/org/jboss/dna/sequencer/xml/DnaDtdLexicon.java
(rev 0)
+++
trunk/extensions/dna-sequencer-xml/src/main/java/org/jboss/dna/sequencer/xml/DnaDtdLexicon.java 2008-10-29
18:23:01 UTC (rev 598)
@@ -0,0 +1,42 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2008, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * 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.
+ *
+ * This software 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.sequencer.xml;
+
+import org.jboss.dna.graph.properties.Name;
+import org.jboss.dna.graph.properties.basic.BasicName;
+
+/**
+ * @author Randall Hauch
+ */
+public class DnaDtdLexicon {
+
+ public static class Namespace {
+ public static final String URI = "http://www.jboss.org/dna/dtd/1.0";
+ public static final String PREFIX = "dnadtd";
+ }
+
+ public static final Name NAME = new BasicName(Namespace.URI, "name");
+ public static final Name PUBLIC_ID = new BasicName(Namespace.URI,
"publicId");
+ public static final Name SYSTEM_ID = new BasicName(Namespace.URI,
"systemId");
+ public static final Name VALUE = new BasicName(Namespace.URI, "value");
+ public static final Name ENTITY = new BasicName(Namespace.URI, "entity");
+}
Added:
trunk/extensions/dna-sequencer-xml/src/main/java/org/jboss/dna/sequencer/xml/DnaXmlLexicon.java
===================================================================
---
trunk/extensions/dna-sequencer-xml/src/main/java/org/jboss/dna/sequencer/xml/DnaXmlLexicon.java
(rev 0)
+++
trunk/extensions/dna-sequencer-xml/src/main/java/org/jboss/dna/sequencer/xml/DnaXmlLexicon.java 2008-10-29
18:23:01 UTC (rev 598)
@@ -0,0 +1,46 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2008, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * 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.
+ *
+ * This software 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.sequencer.xml;
+
+import org.jboss.dna.graph.properties.Name;
+import org.jboss.dna.graph.properties.basic.BasicName;
+
+/**
+ * @author Randall Hauch
+ */
+public class DnaXmlLexicon {
+
+ public static class Namespace {
+ public static final String URI = "http://www.jboss.org/dna/xml/1.0";
+ public static final String PREFIX = "dnaxml";
+ }
+
+ public static final Name CDATA = new BasicName(Namespace.URI, "cData");
+ public static final Name CDATA_CONTENT = new BasicName(Namespace.URI,
"cDataContent");
+ public static final Name COMMENT = new BasicName(Namespace.URI,
"comment");
+ public static final Name COMMENT_CONTENT = new BasicName(Namespace.URI,
"commentContent");
+ public static final Name DOCUMENT = new BasicName(Namespace.URI,
"document");
+ public static final Name ELEMENT_CONTENT = new BasicName(Namespace.URI,
"elementContent");
+ public static final Name PROCESSING_INSTRUCTION = new BasicName(Namespace.URI,
"processingInstruction");
+ public static final Name PROCESSING_INSTRUCTION_CONTENT = new
BasicName(Namespace.URI, "processingInstructionContent");
+ public static final Name TARGET = new BasicName(Namespace.URI, "target");
+}
Added:
trunk/extensions/dna-sequencer-xml/src/main/java/org/jboss/dna/sequencer/xml/XmlSequencer.java
===================================================================
---
trunk/extensions/dna-sequencer-xml/src/main/java/org/jboss/dna/sequencer/xml/XmlSequencer.java
(rev 0)
+++
trunk/extensions/dna-sequencer-xml/src/main/java/org/jboss/dna/sequencer/xml/XmlSequencer.java 2008-10-29
18:23:01 UTC (rev 598)
@@ -0,0 +1,702 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2008, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * 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.
+ *
+ * This software 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.sequencer.xml;
+
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import org.jboss.dna.common.monitor.ProgressMonitor;
+import org.jboss.dna.common.util.StringUtil;
+import org.jboss.dna.graph.JcrLexicon;
+import org.jboss.dna.graph.properties.Name;
+import org.jboss.dna.graph.properties.NameFactory;
+import org.jboss.dna.graph.properties.NamespaceRegistry;
+import org.jboss.dna.graph.properties.Path;
+import org.jboss.dna.graph.sequencers.SequencerContext;
+import org.jboss.dna.graph.sequencers.SequencerOutput;
+import org.jboss.dna.graph.sequencers.StreamSequencer;
+import org.xml.sax.Attributes;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+import org.xml.sax.SAXNotRecognizedException;
+import org.xml.sax.SAXNotSupportedException;
+import org.xml.sax.SAXParseException;
+import org.xml.sax.XMLReader;
+import org.xml.sax.ext.DefaultHandler2;
+import org.xml.sax.helpers.XMLReaderFactory;
+
+/**
+ * @author John Verhaeg
+ */
+public class XmlSequencer implements StreamSequencer {
+
+ private static final String DEFAULT_PRIMARY_TYPE = "nt:unstructured";
+ private static final String DECL_HANDLER_FEATURE =
"http://xml.org/sax/properties/declaration-handler";
+ private static final String ENTITY_RESOLVER_2_FEATURE =
"http://xml.org/sax/features/use-entity-resolver2";
+ private static final String LEXICAL_HANDLER_FEATURE =
"http://xml.org/sax/properties/lexical-handler";
+ private static final String RESOLVE_DTD_URIS_FEATURE =
"http://xml.org/sax/features/resolve-dtd-uris";
+ private static final String LOAD_EXTERNAL_DTDS_FEATURE =
"http://apache.org/xml/features/nonvalidating/load-external-dtd";
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.sequencers.StreamSequencer#sequence(InputStream,
SequencerOutput, SequencerContext,
+ * ProgressMonitor)
+ */
+ public void sequence( InputStream stream,
+ SequencerOutput output,
+ SequencerContext context,
+ ProgressMonitor monitor ) {
+ monitor.beginTask(100.0, XmlSequencerI18n.sequencingXmlDocument);
+ XMLReader reader;
+ try {
+ reader = XMLReaderFactory.createXMLReader();
+ Handler handler = new Handler(output, context, monitor);
+ reader.setContentHandler(handler);
+ reader.setErrorHandler(handler);
+ // Ensure handler acting as entity resolver 2
+ reader.setProperty(DECL_HANDLER_FEATURE, handler);
+ // Ensure handler acting as lexical handler
+ reader.setProperty(LEXICAL_HANDLER_FEATURE, handler);
+ // Ensure handler acting as entity resolver 2
+ setFeature(reader, ENTITY_RESOLVER_2_FEATURE, true);
+ // Prevent loading of external DTDs
+ setFeature(reader, LOAD_EXTERNAL_DTDS_FEATURE, false);
+ // Prevent the resolving of DTD entities into fully-qualified URIS
+ setFeature(reader, RESOLVE_DTD_URIS_FEATURE, false);
+ // Parse XML document
+ reader.parse(new InputSource(stream));
+ } catch (Exception error) {
+ context.getLogger(getClass()).error(error,
XmlSequencerI18n.fatalErrorSequencingXmlDocument, error);
+ monitor.getProblems().addError(error,
XmlSequencerI18n.fatalErrorSequencingXmlDocument, error);
+ } finally {
+ monitor.done();
+ }
+ }
+
+ /**
+ * Sets the reader's named feature to the supplied value, only if the feature is
not already set to that value. This method
+ * does nothing if the feature is not known to the reader.
+ *
+ * @param reader the reader; may not be null
+ * @param featureName the name of the feature; may not be null
+ * @param value the value for the feature
+ */
+ private void setFeature( XMLReader reader,
+ String featureName,
+ boolean value ) {
+ try {
+ if (reader.getFeature(featureName) != value) {
+ reader.setFeature(featureName, value);
+ }
+ } catch (SAXNotRecognizedException meansFeatureNotRecognized) {
+ } catch (SAXNotSupportedException meansFeatureNotSupported) {
+ }
+ }
+
+ private final class Handler extends DefaultHandler2 {
+
+ private final SequencerOutput output;
+ private final SequencerContext context;
+ private final ProgressMonitor monitor;
+
+ private double progress;
+
+ private Path path; // The DNA path of the node representing the current XML
element
+
+ // Cached instances of the name factory and commonly referenced names
+ private final NameFactory nameFactory;
+ private Name defaultPrimaryType;
+
+ // Recursive map used to track the number of occurrences of names for elements
under a particular path
+ private Map<Name, List<IndexedName>> nameToIndexedNamesMap = new
HashMap<Name, List<IndexedName>>();
+
+ // The stack of recursive maps being processed, with the head entry being the map
for the current path
+ private final LinkedList<Map<Name, List<IndexedName>>>
nameToIndexedNamesMapStack = new LinkedList<Map<Name,
List<IndexedName>>>();
+
+ // The stack of XML namespace in scope, with the head entry being namespace of
the closest ancestor element declaring a
+ // namespace.
+ private final LinkedList<String> nsStack = new LinkedList<String>();
+
+ // Builder used to concatenate concurrent lines of CDATA into a single value.
+ private StringBuilder cDataBuilder;
+
+ // Builder used to concatenate concurrent lines of element content and entity
evaluations into a single value.
+ private StringBuilder contentBuilder;
+
+ // The entity being processed
+ private String entity;
+
+ Handler( SequencerOutput output,
+ SequencerContext context,
+ ProgressMonitor monitor ) {
+ assert output != null;
+ assert monitor != null;
+ assert context != null;
+ this.output = output;
+ this.context = context;
+ this.monitor = monitor;
+ // Initialize path to a an empty path relative to the SequencerOutput's
target path.
+ path = context.getValueFactories().getPathFactory().createRelativePath();
+ // Cache name factory since it is frequently used
+ nameFactory = context.getValueFactories().getNameFactory();
+ }
+
+ /**
+ * <p>
+ * {@inheritDoc}
+ * </p>
+ *
+ * @see org.xml.sax.ext.DefaultHandler2#attributeDecl(java.lang.String,
java.lang.String, java.lang.String,
+ * java.lang.String, java.lang.String)
+ */
+ @Override
+ public void attributeDecl( String name,
+ String name2,
+ String type,
+ String mode,
+ String value ) throws SAXException {
+ stopIfCancelled();
+ }
+
+ /**
+ * <p>
+ * {@inheritDoc}
+ * </p>
+ *
+ * @see org.xml.sax.helpers.DefaultHandler#characters(char[], int, int)
+ */
+ @Override
+ public void characters( char[] ch,
+ int start,
+ int length ) throws SAXException {
+ stopIfCancelled();
+ String content = String.valueOf(ch, start, length);
+ // Check if data should be appended to previously parsed CDATA
+ if (cDataBuilder == null) {
+ // If content is for an entity, replace with entity reference
+ if (entity != null) {
+ content = '&' + entity + ';';
+ }
+ // Check if first line of content
+ if (contentBuilder == null) {
+ contentBuilder = new StringBuilder(content);
+ } else {
+ // Append additional lines or entity evaluations to previous content,
separated by a space
+ if (entity == null) {
+ contentBuilder.append(' ');
+ }
+ contentBuilder.append(content);
+ // Text within builder will be output when another element or CDATA
is encountered
+ }
+ } else {
+ cDataBuilder.append(ch, start, length);
+ // Text within builder will be output at the end of CDATA
+ }
+ updateProgress();
+ }
+
+ /**
+ * <p>
+ * {@inheritDoc}
+ * </p>
+ *
+ * @see org.xml.sax.ext.DefaultHandler2#comment(char[], int, int)
+ */
+ @Override
+ public void comment( char[] ch,
+ int start,
+ int length ) throws SAXException {
+ stopIfCancelled();
+ // Output separate nodes for each comment since multiple are allowed
+ startElement(DnaXmlLexicon.COMMENT);
+ output.setProperty(path, JcrLexicon.PRIMARY_TYPE, DnaXmlLexicon.COMMENT);
+ output.setProperty(path, DnaXmlLexicon.COMMENT_CONTENT, String.valueOf(ch,
start, length));
+ endElement();
+ updateProgress();
+ }
+
+ /**
+ * <p>
+ * {@inheritDoc}
+ * </p>
+ *
+ * @see org.xml.sax.ext.DefaultHandler2#elementDecl(java.lang.String,
java.lang.String)
+ */
+ @Override
+ public void elementDecl( String name,
+ String model ) throws SAXException {
+ stopIfCancelled();
+ }
+
+ /**
+ * <p>
+ * {@inheritDoc}
+ * </p>
+ *
+ * @see org.xml.sax.ext.DefaultHandler2#endCDATA()
+ */
+ @Override
+ public void endCDATA() throws SAXException {
+ stopIfCancelled();
+ // Output CDATA built in characters() method
+ output.setProperty(path, DnaXmlLexicon.CDATA_CONTENT,
cDataBuilder.toString());
+ endElement();
+ // Null-out builder to free memory
+ cDataBuilder = null;
+ updateProgress();
+ }
+
+ private void endContent() {
+ if (contentBuilder != null) {
+ // Normalize content
+ String content = StringUtil.normalize(contentBuilder.toString());
+ // Null-out builder to setup for subsequent content.
+ // Must be done before call to startElement below to prevent infinite
loop.
+ contentBuilder = null;
+ // Skip if nothing in content but whitespace
+ if (content.length() > 0) {
+ // Create separate node for each content entry since entries can be
interspersed amongst child elements
+ startElement(DnaXmlLexicon.ELEMENT_CONTENT);
+ output.setProperty(path, JcrLexicon.PRIMARY_TYPE,
DnaXmlLexicon.ELEMENT_CONTENT);
+ output.setProperty(path, DnaXmlLexicon.ELEMENT_CONTENT, content);
+ endElement();
+ }
+ }
+ }
+
+ /**
+ * <p>
+ * {@inheritDoc}
+ * </p>
+ *
+ * @see org.xml.sax.helpers.DefaultHandler#endDocument()
+ */
+ @Override
+ public void endDocument() throws SAXException {
+ stopIfCancelled();
+ }
+
+ /**
+ * <p>
+ * {@inheritDoc}
+ * </p>
+ *
+ * @see org.xml.sax.ext.DefaultHandler2#endDTD()
+ */
+ @Override
+ public void endDTD() throws SAXException {
+ stopIfCancelled();
+ }
+
+ private void endElement() {
+ // Recover parent's path, namespace, and indexedName map, clearing the
ended element's map to free memory
+ path = path.getParent();
+ nameToIndexedNamesMap.clear();
+ nameToIndexedNamesMap = nameToIndexedNamesMapStack.removeFirst();
+ nsStack.removeFirst();
+ }
+
+ /**
+ * <p>
+ * {@inheritDoc}
+ * </p>
+ *
+ * @see org.xml.sax.helpers.DefaultHandler#endElement(java.lang.String,
java.lang.String, java.lang.String)
+ */
+ @Override
+ public void endElement( String uri,
+ String localName,
+ String name ) throws SAXException {
+ stopIfCancelled();
+ // Check if content still needs to be output
+ endContent();
+ endElement();
+ updateProgress();
+ }
+
+ /**
+ * <p>
+ * {@inheritDoc}
+ * </p>
+ *
+ * @see org.xml.sax.ext.DefaultHandler2#endEntity(java.lang.String)
+ */
+ @Override
+ public void endEntity( String name ) throws SAXException {
+ stopIfCancelled();
+ entity = null;
+ updateProgress();
+ }
+
+ /**
+ * <p>
+ * {@inheritDoc}
+ * </p>
+ *
+ * @see org.xml.sax.helpers.DefaultHandler#error(org.xml.sax.SAXParseException)
+ */
+ @Override
+ public void error( SAXParseException error ) {
+ context.getLogger(XmlSequencer.class).error(error,
XmlSequencerI18n.errorSequencingXmlDocument, error);
+ monitor.getProblems().addError(error,
XmlSequencerI18n.errorSequencingXmlDocument, error);
+ }
+
+ /**
+ * <p>
+ * {@inheritDoc}
+ * </p>
+ *
+ * @see org.xml.sax.ext.DefaultHandler2#externalEntityDecl(java.lang.String,
java.lang.String, java.lang.String)
+ */
+ @Override
+ public void externalEntityDecl( String name,
+ String publicId,
+ String systemId ) throws SAXException {
+ stopIfCancelled();
+ // Add "synthetic" entity container to path to help prevent name
collisions with XML elements
+ Name entityName = DnaDtdLexicon.ENTITY;
+ startElement(entityName);
+ output.setProperty(path, JcrLexicon.PRIMARY_TYPE, entityName);
+ output.setProperty(path, nameFactory.create(DnaDtdLexicon.NAME), name);
+ output.setProperty(path, nameFactory.create(DnaDtdLexicon.PUBLIC_ID),
publicId);
+ output.setProperty(path, nameFactory.create(DnaDtdLexicon.SYSTEM_ID),
systemId);
+ endElement();
+ updateProgress();
+ }
+
+ /**
+ * <p>
+ * {@inheritDoc}
+ * </p>
+ *
+ * @see
org.xml.sax.helpers.DefaultHandler#fatalError(org.xml.sax.SAXParseException)
+ */
+ @Override
+ public void fatalError( SAXParseException error ) {
+ context.getLogger(XmlSequencer.class).error(error,
XmlSequencerI18n.fatalErrorSequencingXmlDocument, error);
+ monitor.getProblems().addError(error,
XmlSequencerI18n.fatalErrorSequencingXmlDocument, error);
+ }
+
+ private Name getDefaultPrimaryType() {
+ if (defaultPrimaryType == null) {
+ defaultPrimaryType = nameFactory.create(DEFAULT_PRIMARY_TYPE);
+ }
+ return defaultPrimaryType;
+ }
+
+ /**
+ * <p>
+ * {@inheritDoc}
+ * </p>
+ *
+ * @see org.xml.sax.helpers.DefaultHandler#ignorableWhitespace(char[], int, int)
+ */
+ @Override
+ public void ignorableWhitespace( char[] ch,
+ int start,
+ int length ) throws SAXException {
+ stopIfCancelled();
+ }
+
+ /**
+ * <p>
+ * {@inheritDoc}
+ * </p>
+ *
+ * @see org.xml.sax.ext.DefaultHandler2#internalEntityDecl(java.lang.String,
java.lang.String)
+ */
+ @Override
+ public void internalEntityDecl( String name,
+ String value ) throws SAXException {
+ stopIfCancelled();
+ // Add "synthetic" entity container to path to help prevent name
collisions with XML elements
+ Name entityName = DnaDtdLexicon.ENTITY;
+ startElement(entityName);
+ output.setProperty(path, JcrLexicon.PRIMARY_TYPE, entityName);
+ output.setProperty(path, DnaDtdLexicon.NAME, name);
+ output.setProperty(path, DnaDtdLexicon.VALUE, value);
+ endElement();
+ updateProgress();
+ }
+
+ /**
+ * <p>
+ * {@inheritDoc}
+ * </p>
+ *
+ * @see org.xml.sax.helpers.DefaultHandler#notationDecl(java.lang.String,
java.lang.String, java.lang.String)
+ */
+ @Override
+ public void notationDecl( String name,
+ String publicId,
+ String systemId ) throws SAXException {
+ stopIfCancelled();
+ }
+
+ /**
+ * <p>
+ * {@inheritDoc}
+ * </p>
+ *
+ * @see
org.xml.sax.helpers.DefaultHandler#processingInstruction(java.lang.String,
java.lang.String)
+ */
+ @Override
+ public void processingInstruction( String target,
+ String data ) throws SAXException {
+ stopIfCancelled();
+ // Output separate nodes for each instruction since multiple are allowed
+ Name name = DnaXmlLexicon.PROCESSING_INSTRUCTION;
+ startElement(name);
+ output.setProperty(path, JcrLexicon.PRIMARY_TYPE, name);
+ output.setProperty(path, DnaXmlLexicon.TARGET, target);
+ output.setProperty(path, DnaXmlLexicon.PROCESSING_INSTRUCTION_CONTENT,
data);
+ endElement();
+ updateProgress();
+ }
+
+ /**
+ * <p>
+ * {@inheritDoc}
+ * </p>
+ *
+ * @see org.xml.sax.helpers.DefaultHandler#skippedEntity(java.lang.String)
+ */
+ @Override
+ public void skippedEntity( String name ) throws SAXException {
+ stopIfCancelled();
+ }
+
+ /**
+ * <p>
+ * {@inheritDoc}
+ * </p>
+ *
+ * @see org.xml.sax.ext.DefaultHandler2#startCDATA()
+ */
+ @Override
+ public void startCDATA() throws SAXException {
+ stopIfCancelled();
+ // Output separate nodes for each CDATA since multiple are allowed
+ startElement(DnaXmlLexicon.CDATA);
+ // Prepare builder for concatenating consecutive lines of CDATA
+ cDataBuilder = new StringBuilder();
+ updateProgress();
+ }
+
+ /**
+ * <p>
+ * {@inheritDoc}
+ * </p>
+ *
+ * @see org.xml.sax.helpers.DefaultHandler#startDocument()
+ */
+ @Override
+ public void startDocument() throws SAXException {
+ stopIfCancelled();
+ output.setProperty(path, JcrLexicon.PRIMARY_TYPE, DnaXmlLexicon.DOCUMENT);
+ updateProgress();
+ }
+
+ /**
+ * <p>
+ * {@inheritDoc}
+ * </p>
+ *
+ * @see org.xml.sax.ext.DefaultHandler2#startDTD(java.lang.String,
java.lang.String, java.lang.String)
+ */
+ @Override
+ public void startDTD( String name,
+ String publicId,
+ String systemId ) throws SAXException {
+ stopIfCancelled();
+ output.setProperty(path, DnaDtdLexicon.NAME, name);
+ output.setProperty(path, DnaDtdLexicon.PUBLIC_ID, publicId);
+ output.setProperty(path, DnaDtdLexicon.SYSTEM_ID, systemId);
+ updateProgress();
+ }
+
+ private void startElement( Name name ) {
+ // Check if content still needs to be output
+ endContent();
+ // Add name to list of indexed names for this element to ensure we use the
correct index (which is the size of the
+ // list)
+ List<IndexedName> indexedNames = nameToIndexedNamesMap.get(name);
+ if (indexedNames == null) {
+ indexedNames = new ArrayList<IndexedName>();
+ nameToIndexedNamesMap.put(name, indexedNames);
+ }
+ IndexedName indexedName = new IndexedName();
+ indexedNames.add(indexedName);
+ // Add element name and the appropriate index to the path.
+ // Per the JCR spec, the index must be relative to same-name sibling nodes
+ path = context.getValueFactories().getPathFactory().create(path, name,
indexedNames.size());
+ path = path.getNormalizedPath();
+ // Add the indexed name map to the stack and set the current map to the new
element's map
+ nameToIndexedNamesMapStack.addFirst(nameToIndexedNamesMap);
+ nameToIndexedNamesMap = indexedName.nameToIndexedNamesMap;
+ // Set the current namespace to whatever is declared by this element, or if
not declared, to its nearest ancestor that
+ // does declare a namespace.
+ String ns = name.getNamespaceUri();
+ if (ns.length() == 0) {
+ nsStack.addFirst(nsStack.isEmpty() ? "" : nsStack.getFirst());
+ } else {
+ nsStack.addFirst(ns);
+ }
+ }
+
+ /**
+ * <p>
+ * {@inheritDoc}
+ * </p>
+ *
+ * @see org.xml.sax.helpers.DefaultHandler#startElement(java.lang.String,
java.lang.String, java.lang.String,
+ * org.xml.sax.Attributes)
+ */
+ @Override
+ public void startElement( String uri,
+ String localName,
+ String name,
+ Attributes attributes ) throws SAXException {
+ stopIfCancelled();
+ // Look for the "jcr:name" attribute, and use that if it's
there
+ Name type = getDefaultPrimaryType();
+ Name nameObj = nameFactory.create(name);
+ for (int ndx = 0, len = attributes.getLength(); ndx < len; ++ndx) {
+ String ns = attributes.getURI(ndx);
+ String attrLocalName = attributes.getLocalName(ndx);
+ Object value = attributes.getValue(ndx);
+ String jcrNsUri =
context.getNamespaceRegistry().getNamespaceForPrefix("jcr");
+ if (jcrNsUri != null && jcrNsUri.equals(ns) &&
attrLocalName.equals("name")) {
+ nameObj = nameFactory.create(value);
+ break;
+ }
+ }
+ startElement(nameObj);
+ output.setProperty(path, JcrLexicon.PRIMARY_TYPE, type);
+ // Output this element's attributes using the attribute's namespace,
if supplied, or the current namespace in scope.
+ String inheritedNs = nsStack.getFirst();
+ for (int ndx = 0, len = attributes.getLength(); ndx < len; ++ndx) {
+ String ns = attributes.getURI(ndx);
+ String attrLocalName = attributes.getLocalName(ndx);
+ Object value = attributes.getValue(ndx);
+ String jcrNsUri =
context.getNamespaceRegistry().getNamespaceForPrefix("jcr");
+ if (jcrNsUri != null && jcrNsUri.equals(ns) &&
attrLocalName.equals("primaryType")) {
+ value = nameFactory.create(value);
+ }
+ if (jcrNsUri != null && jcrNsUri.equals(ns) &&
attrLocalName.equals("name")) {
+ continue;
+ }
+ output.setProperty(path, nameFactory.create(ns.length() == 0 ?
inheritedNs : ns, attrLocalName), value);
+ }
+ updateProgress();
+ }
+
+ /**
+ * <p>
+ * {@inheritDoc}
+ * </p>
+ *
+ * @see org.xml.sax.ext.DefaultHandler2#startEntity(java.lang.String)
+ */
+ @Override
+ public void startEntity( String name ) throws SAXException {
+ stopIfCancelled();
+ entity = name;
+ updateProgress();
+ }
+
+ /**
+ * <p>
+ * {@inheritDoc}
+ * </p>
+ *
+ * @see org.xml.sax.helpers.DefaultHandler#startPrefixMapping(java.lang.String,
java.lang.String)
+ */
+ @Override
+ public void startPrefixMapping( String prefix,
+ String uri ) throws SAXException {
+ stopIfCancelled();
+ // Register any unregistered namespaces
+ NamespaceRegistry registry = context.getNamespaceRegistry();
+ if (!registry.isRegisteredNamespaceUri(uri)) {
+ registry.register(prefix, uri);
+ }
+ updateProgress();
+ }
+
+ private void stopIfCancelled() throws SAXException {
+ if (monitor.isCancelled()) {
+ throw new
SAXException(XmlSequencerI18n.canceledSequencingXmlDocument.text());
+ }
+ }
+
+ /**
+ * <p>
+ * {@inheritDoc}
+ * </p>
+ *
+ * @see org.xml.sax.helpers.DefaultHandler#unparsedEntityDecl(java.lang.String,
java.lang.String, java.lang.String,
+ * java.lang.String)
+ */
+ @Override
+ public void unparsedEntityDecl( String name,
+ String publicId,
+ String systemId,
+ String notationName ) throws SAXException {
+ stopIfCancelled();
+ }
+
+ private void updateProgress() {
+ if (progress == 100.0) {
+ progress = 1;
+ } else {
+ progress++;
+ }
+ monitor.worked(progress);
+ }
+
+ /**
+ * <p>
+ * {@inheritDoc}
+ * </p>
+ *
+ * @see
org.xml.sax.helpers.DefaultHandler#warning(org.xml.sax.SAXParseException)
+ */
+ @Override
+ public void warning( SAXParseException warning ) {
+ context.getLogger(XmlSequencer.class).warn(warning,
XmlSequencerI18n.warningSequencingXmlDocument);
+ monitor.getProblems().addWarning(warning,
XmlSequencerI18n.warningSequencingXmlDocument, warning);
+ }
+ }
+
+ private class IndexedName {
+
+ Map<Name, List<IndexedName>> nameToIndexedNamesMap = new
HashMap<Name, List<IndexedName>>();
+
+ IndexedName() {
+ }
+ }
+}
Added:
trunk/extensions/dna-sequencer-xml/src/main/java/org/jboss/dna/sequencer/xml/XmlSequencerI18n.java
===================================================================
---
trunk/extensions/dna-sequencer-xml/src/main/java/org/jboss/dna/sequencer/xml/XmlSequencerI18n.java
(rev 0)
+++
trunk/extensions/dna-sequencer-xml/src/main/java/org/jboss/dna/sequencer/xml/XmlSequencerI18n.java 2008-10-29
18:23:01 UTC (rev 598)
@@ -0,0 +1,60 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2008, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * 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.
+ *
+ * This software 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.sequencer.xml;
+
+import java.util.Locale;
+import java.util.Set;
+import org.jboss.dna.common.CommonI18n;
+import org.jboss.dna.common.i18n.I18n;
+
+/**
+ * @author Randall Hauch
+ * @author John Verhaeg
+ */
+public final class XmlSequencerI18n {
+
+ public static I18n errorSequencingXmlDocument;
+ public static I18n fatalErrorSequencingXmlDocument;
+ public static I18n sequencingXmlDocument;
+ public static I18n canceledSequencingXmlDocument;
+ public static I18n warningSequencingXmlDocument;
+
+ static {
+ try {
+ I18n.initialize(XmlSequencerI18n.class);
+ } catch (final Exception err) {
+ System.err.println(err);
+ }
+ }
+
+ public static Set<Locale> getLocalizationProblemLocales() {
+ return I18n.getLocalizationProblemLocales(CommonI18n.class);
+ }
+
+ public static Set<String> getLocalizationProblems() {
+ return I18n.getLocalizationProblems(CommonI18n.class);
+ }
+
+ public static Set<String> getLocalizationProblems( Locale locale ) {
+ return I18n.getLocalizationProblems(CommonI18n.class, locale);
+ }
+}
Added:
trunk/extensions/dna-sequencer-xml/src/main/resources/org/jboss/dna/sequencer/xml/XmlSequencerI18n.properties
===================================================================
---
trunk/extensions/dna-sequencer-xml/src/main/resources/org/jboss/dna/sequencer/xml/XmlSequencerI18n.properties
(rev 0)
+++
trunk/extensions/dna-sequencer-xml/src/main/resources/org/jboss/dna/sequencer/xml/XmlSequencerI18n.properties 2008-10-29
18:23:01 UTC (rev 598)
@@ -0,0 +1,27 @@
+#
+# JBoss, Home of Professional Open Source.
+# Copyright 2008, Red Hat Middleware LLC, and individual contributors
+# as indicated by the @author tags. See the copyright.txt file in the
+# distribution for a full listing of individual contributors.
+#
+# This is free software; you can redistribute it and/or modify it
+# 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.
+#
+# This software 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.
+#
+errorSequencingXmlDocument = An error was received while sequencing XML: {0}
+fatalErrorSequencingXmlDocument = A fatal error was received while sequencing XML: {0}
+sequencingXmlDocument = Sequencing XML
+canceledSequencingXmlDocument = Canceled sequencing XML
+warningSequencingXmlDocument = A warning was received while sequencing XML: {0}
+
Added:
trunk/extensions/dna-sequencer-xml/src/main/resources/org/jboss/dna/sequencer/xml/xml.cnd
===================================================================
---
trunk/extensions/dna-sequencer-xml/src/main/resources/org/jboss/dna/sequencer/xml/xml.cnd
(rev 0)
+++
trunk/extensions/dna-sequencer-xml/src/main/resources/org/jboss/dna/sequencer/xml/xml.cnd 2008-10-29
18:23:01 UTC (rev 598)
@@ -0,0 +1,68 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2008, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * 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.
+ *
+ * This software 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.
+ */
+
+//------------------------------------------------------------------------------
+// N A M E S P A C E S
+//------------------------------------------------------------------------------
+<jcr='http://www.jcp.org/jcr/1.0'>
+<nt='http://www.jcp.org/jcr/nt/1.0'>
+<mix='http://www.jcp.org/jcr/mix/1.0'>
+<dnaxml='http://www.jboss.org/dna/xml/1.0'>
+<dnadtd='http://www.jboss.org/dna/dtd/1.0'>
+
+// ----------------------------------------------------------
+// JCR Pre-defined Mixin Types that are not loaded by default
+// ----------------------------------------------------------
+
+[mix:mimeType] mixin
+ - jcr:mimeType (string)
+ - jcr:encoding (string)
+
+
+//------------------------------------------------------------------------------
+// N O D E T Y P E S
+//------------------------------------------------------------------------------
+
+[dnaxml:document] > nt:unstructured, mix:mimeTyped
+ - dnaxml:cDataContent (string)
+
+[dnaxml:comment] > nt:unstructured, mix:mimeTyped
+ - dnaxml:commentContent (string)
+
+[dnaxml:element] > nt:unstructured, mix:mimeTyped
+ - dnaxml:elementContent (string)
+
+[dnaxml:cData] > nt:unstructured, mix:mimeTyped
+ - dnaxml:cDataContent (string)
+
+[dnaxml:processingInstruction] > nt:unstructured, mix:mimeTyped
+ - dnaxml:processingInstruction (string)
+ - dnaxml:target (string)
+
+
+[dnadtd:entity] > nt:unstructured, mix:mimeTyped
+ - dnaxml:name (string)
+ - dnaxml:value (string)
+ - dnaxml:publicId (string)
+ - dnaxml:systemId (string)
+
+
\ No newline at end of file
Added:
trunk/extensions/dna-sequencer-xml/src/test/java/org/jboss/dna/sequencer/xml/XmlSequencerTest.java
===================================================================
---
trunk/extensions/dna-sequencer-xml/src/test/java/org/jboss/dna/sequencer/xml/XmlSequencerTest.java
(rev 0)
+++
trunk/extensions/dna-sequencer-xml/src/test/java/org/jboss/dna/sequencer/xml/XmlSequencerTest.java 2008-10-29
18:23:01 UTC (rev 598)
@@ -0,0 +1,222 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2008, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * 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.
+ *
+ * This software 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.sequencer.xml;
+
+import static org.hamcrest.core.Is.is;
+import static org.hamcrest.core.IsInstanceOf.instanceOf;
+import static org.hamcrest.core.IsNull.notNullValue;
+import static org.junit.Assert.assertThat;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import org.jboss.dna.common.monitor.ProgressMonitor;
+import org.jboss.dna.common.monitor.SimpleProgressMonitor;
+import org.jboss.dna.graph.properties.Name;
+import org.jboss.dna.graph.sequencers.MockSequencerContext;
+import org.jboss.dna.graph.sequencers.MockSequencerOutput;
+import org.jboss.dna.graph.sequencers.SequencerContext;
+import org.jboss.dna.sequencer.xml.XmlSequencer;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * @author John Verhaeg
+ */
+public class XmlSequencerTest {
+
+ private static final String CDATA = "dnaxml:cData";
+ private static final String CDATA_CONTENT = "dnaxml:cDataContent";
+ private static final String COMMENT = "dnaxml:comment";
+ private static final String COMMENT_CONTENT = "dnaxml:commentContent";
+ private static final String DOCUMENT = "dnaxml:document";
+ private static final String DTD_NAME = "dnadtd:name";
+ private static final String DTD_PUBLIC_ID = "dnadtd:publicId";
+ private static final String DTD_SYSTEM_ID = "dnadtd:systemId";
+ private static final String DTD_VALUE = "dnadtd:value";
+ private static final String ELEMENT_CONTENT = "dnaxml:elementContent";
+ private static final String ENTITY = "dnadtd:entity";
+ private static final String PI = "dnaxml:processingInstruction";
+ private static final String PI_CONTENT =
"dnaxml:processingInstructionContent";
+ private static final String TARGET = "dnaxml:target";
+
+ private XmlSequencer sequencer;
+ private InputStream stream;
+ private MockSequencerOutput output;
+ private ProgressMonitor monitor;
+ private URL xml1;
+ private URL xml2;
+ private URL xml3;
+ private URL xml4;
+ private URL xsd;
+ private SequencerContext context;
+
+ @Before
+ public void beforeEach() {
+ sequencer = new XmlSequencer();
+ context = new MockSequencerContext();
+ output = new MockSequencerOutput(context);
+ monitor = new SimpleProgressMonitor("Test activity");
+ xml1 =
this.getClass().getClassLoader().getResource("jackrabbitInMemoryTestRepositoryConfig.xml");
+ assertThat(xml1, is(notNullValue()));
+ xml2 = this.getClass().getClassLoader().getResource("master.xml");
+ assertThat(xml2, is(notNullValue()));
+ xml3 =
this.getClass().getClassLoader().getResource("CurrencyFormatterExample.mxml");
+ assertThat(xml3, is(notNullValue()));
+ xml4 = this.getClass().getClassLoader().getResource("plugin.xml");
+ assertThat(xml4, is(notNullValue()));
+ xsd =
this.getClass().getClassLoader().getResource("Descriptor.1.0.xsd");
+ assertThat(xsd, is(notNullValue()));
+ }
+
+ @After
+ public void afterEach() throws Exception {
+ if (stream != null) {
+ try {
+ stream.close();
+ } finally {
+ stream = null;
+ }
+ }
+ }
+
+ @Test
+ public void shouldSequenceXml() throws IOException {
+ verifyDocument(xml1);
+ verifyName(COMMENT + "[1]", "jcr:primaryType", COMMENT);
+ String text = verify(COMMENT + "[1]", COMMENT_CONTENT, String.class);
+ assertThat(text.startsWith("\n Licensed to the Apache Software Foundation
(ASF)"), is(true));
+ assertThat(text.endsWith(" limitations under the License.\n"),
is(true));
+ verifyString("/", DTD_NAME, "Repository");
+ verifyString("/", DTD_PUBLIC_ID, "-//The Apache Software
Foundation//DTD Jackrabbit 1.2//EN");
+ verifyString("/", DTD_SYSTEM_ID,
"http://jackrabbit.apache.org/dtd/repository-1.2.dtd");
+ verifyName(COMMENT + "[2]", "jcr:primaryType", COMMENT);
+ verifyString(COMMENT + "[2]", COMMENT_CONTENT, " Example
Repository Configuration File ");
+ verifyName("Repository[1]", "jcr:primaryType",
"nt:unstructured");
+ verifyName("Repository[1]/" + COMMENT + "[1]",
"jcr:primaryType", COMMENT);
+ }
+
+ @Test
+ public void shouldHandleNamespaces() throws IOException {
+ verifyDocument(xml2);
+ verifyName("book[1]/bookinfo[1]/xi:include[1]",
"jcr:primaryType", "nt:unstructured");
+ verifyString("book[1]/bookinfo[1]/xi:include[1]", "xi:href",
"Author_Group.xml");
+ verifyName("book[1]/bookinfo[1]/xi:include[2]",
"jcr:primaryType", "nt:unstructured");
+ verifyString("book[1]/bookinfo[1]/xi:include[2]", "xi:href",
"Legal_Notice.xml");
+ }
+
+ @Test
+ public void shouldSequenceEntityDeclarations() throws IOException {
+ verifyDocument(xml2);
+ verifyName(ENTITY + "[1]", "jcr:primaryType", ENTITY);
+ verifyString(ENTITY + "[1]", DTD_NAME, "%RH-ENTITIES");
+ verifyString(ENTITY + "[1]", DTD_SYSTEM_ID,
"Common_Config/rh-entities.ent");
+ verifyName(ENTITY + "[2]", "jcr:primaryType", ENTITY);
+ verifyString(ENTITY + "[2]", DTD_NAME, "versionNumber");
+ verifyString(ENTITY + "[2]", DTD_VALUE, "0.1");
+ verifyName(ENTITY + "[3]", "jcr:primaryType", ENTITY);
+ verifyString(ENTITY + "[3]", DTD_NAME, "copyrightYear");
+ verifyString(ENTITY + "[3]", DTD_VALUE, "2008");
+ }
+
+ @Test
+ public void shouldSequenceElementContent() throws IOException {
+ verifyDocument(xml2);
+ verifyString("book[1]/chapter[4]/sect1[1]/para[8]/" + ELEMENT_CONTENT +
"[1]",
+ ELEMENT_CONTENT,
+ "The path expression is more complicated."
+ + " Sequencer path expressions are used by the sequencing
service to determine whether a particular changed node should be sequenced."
+ + " The expressions consist of two parts: a selection criteria
and an output expression."
+ + " Here's a simple example:");
+ verifyString("book[1]/chapter[4]/sect1[1]/para[8]/programlisting[1]/" +
ELEMENT_CONTENT + "[1]",
+ ELEMENT_CONTENT,
+ "/a/b/c@title => /d/e/f");
+ }
+
+ @Test
+ public void shouldSequenceCData() throws IOException {
+ verifyDocument(xml3);
+ verifyString("mx:Application[1]/mx:Script[1]/" + CDATA +
"[1]",
+ CDATA_CONTENT,
+ "\n\n" + " import
mx.events.ValidationResultEvent;\t\t\t\n"
+ + " private var
vResult:ValidationResultEvent;\n" + "\t\t\t\n"
+ + " // Event handler to validate and format
input.\n"
+ + " private function Format():void {\n" +
" \n"
+ + " vResult = numVal.validate();\n\n"
+ + " if
(vResult.type==ValidationResultEvent.VALID) {\n"
+ + " var
temp:Number=Number(priceUS.text); \n"
+ + " formattedUSPrice.text=
usdFormatter.format(temp);\n" + " }\n"
+ + " \n" + "
else {\n"
+ + "
formattedUSPrice.text=\"\";\n" + " }\n" +
" }\n"
+ + " ");
+ }
+
+ @Test
+ public void shouldSequenceProcessingInstructions() throws IOException {
+ verifyDocument(xml4);
+ verifyName(PI + "[1]", "jcr:primaryType", PI);
+ verifyString(PI + "[1]", TARGET, "eclipse");
+ verifyString(PI + "[1]", PI_CONTENT,
"version=\"3.0\"");
+ }
+
+ @Test
+ public void shouldSequenceXsds() throws IOException {
+ verifyDocument(xsd);
+ verifyName("xs:schema[1]", "jcr:primaryType",
"nt:unstructured");
+ verifyString("xs:schema[1]", "xs:targetNamespace",
"http://ns.adobe.com/air/application/1.0");
+ verifyString("xs:schema[1]", "xs:elementFormDefault",
"qualified");
+ verifyName("xs:schema[1]/xs:element[1]", "jcr:primaryType",
"nt:unstructured");
+ verifyString("xs:schema[1]/xs:element[1]", "xs:name",
"application");
+ }
+
+ private <T> T verify( String nodePath,
+ String property,
+ Class<T> expectedClass ) {
+ Object[] values = output.getPropertyValues(nodePath.length() == 0 ? "."
: nodePath, property);
+ assertThat(values, notNullValue());
+ assertThat(values.length, is(1));
+ Object value = values[0];
+ assertThat(value, instanceOf(expectedClass));
+ return expectedClass.cast(value);
+ }
+
+ private void verifyDocument( URL url ) throws IOException {
+ stream = url.openStream();
+ assertThat(stream, is(notNullValue()));
+ sequencer.sequence(stream, output, context, monitor);
+ verifyName("", "jcr:primaryType", DOCUMENT);
+ }
+
+ private void verifyName( String nodePath,
+ String property,
+ String expectedName ) {
+ Name name = verify(nodePath, property, Name.class);
+ assertThat(name,
is(context.getValueFactories().getNameFactory().create(expectedName)));
+ }
+
+ private void verifyString( String nodePath,
+ String property,
+ String expectedString ) {
+ String string = verify(nodePath, property, String.class);
+ assertThat(string, is(expectedString));
+ }
+}
Added:
trunk/extensions/dna-sequencer-xml/src/test/resources/CurrencyFormatterExample.mxml
===================================================================
--- trunk/extensions/dna-sequencer-xml/src/test/resources/CurrencyFormatterExample.mxml
(rev 0)
+++
trunk/extensions/dna-sequencer-xml/src/test/resources/CurrencyFormatterExample.mxml 2008-10-29
18:23:01 UTC (rev 598)
@@ -0,0 +1,54 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Simple example to demonstrate the CurrencyFormatter. -->
+<mx:Application
xmlns:mx="http://www.adobe.com/2006/mxml">
+
+ <mx:Script>
+ <![CDATA[
+
+ import mx.events.ValidationResultEvent;
+ private var vResult:ValidationResultEvent;
+
+ // Event handler to validate and format input.
+ private function Format():void {
+
+ vResult = numVal.validate();
+
+ if (vResult.type==ValidationResultEvent.VALID) {
+ var temp:Number=Number(priceUS.text);
+ formattedUSPrice.text= usdFormatter.format(temp);
+ }
+
+ else {
+ formattedUSPrice.text="";
+ }
+ }
+ ]]>
+ </mx:Script>
+
+ <mx:CurrencyFormatter id="usdFormatter" precision="2"
+ currencySymbol="$" decimalSeparatorFrom="."
+ decimalSeparatorTo="." useNegativeSign="true"
+ useThousandsSeparator="true" alignSymbol="left"/>
+
+ <mx:NumberValidator id="numVal" source="{priceUS}"
property="text"
+ allowNegative="true" domain="real"/>
+
+ <mx:Panel title="CurrencyFormatter Example" width="75%"
height="75%"
+ paddingTop="10" paddingLeft="10" paddingRight="10"
paddingBottom="10">
+
+ <mx:Form>
+ <mx:FormItem label="Enter U.S. dollar amount:">
+ <mx:TextInput id="priceUS" text=""
width="50%"/>
+ </mx:FormItem>
+
+ <mx:FormItem label="Formatted amount: ">
+ <mx:TextInput id="formattedUSPrice" text=""
width="50%" editable="false"/>
+ </mx:FormItem>
+
+ <mx:FormItem>
+ <mx:Button label="Validate and Format"
click="Format();"/>
+ </mx:FormItem>
+ </mx:Form>
+
+ </mx:Panel>
+</mx:Application>
Added: trunk/extensions/dna-sequencer-xml/src/test/resources/Descriptor.1.0.xsd
===================================================================
--- trunk/extensions/dna-sequencer-xml/src/test/resources/Descriptor.1.0.xsd
(rev 0)
+++ trunk/extensions/dna-sequencer-xml/src/test/resources/Descriptor.1.0.xsd 2008-10-29
18:23:01 UTC (rev 598)
@@ -0,0 +1,127 @@
+<?xml version="1.0"?>
+<xs:schema
+
xmlns:xs="http://www.w3.org/2001/XMLSchema"
+
targetNamespace="http://ns.adobe.com/air/application/1.0"
+
xmlns="http://ns.adobe.com/air/application/1.0"
+ elementFormDefault="qualified"
+>
+ <xs:element name="application">
+ <xs:complexType>
+ <xs:all>
+ <!-- About this application -->
+ <xs:element name="id">
+ <xs:simpleType>
+ <xs:restriction base="xs:string">
+ <xs:pattern value="[A-Za-z0-9\-\.]{1,212}"/>
+ </xs:restriction>
+ </xs:simpleType>
+ </xs:element>
+ <xs:element name="name" type="xs:string"
minOccurs="0"/>
+ <xs:element name="version" type="xs:string"/>
+ <xs:element name="filename">
+ <xs:simpleType>
+ <xs:restriction base="xs:string">
+ <!-- name cannot begin with a ' ' (space), have any of these
characters: *"/:<>?\|, and end with a . (dot) or ' ' (space) -->
+ <xs:pattern value='[^\*"/:><\?\\\|\.
]|[^\*"/:><\?\\\|
][^\*"/:><\?\\\|]*[^\*"/:><\?\\\|\. ]'/>
+ </xs:restriction>
+ </xs:simpleType>
+ </xs:element>
+ <xs:element name="description" type="xs:string"
minOccurs="0"/>
+ <xs:element name="copyright" type="xs:string"
minOccurs="0"/>
+ <xs:element name="icon" type="IconType"
minOccurs="0"/>
+
+ <!-- How to start this application -->
+ <xs:element name="initialWindow">
+ <xs:complexType>
+ <xs:all>
+ <xs:element name="content" type="xs:anyURI"
minOccurs="1" />
+ <xs:element name="title" type="xs:string"
minOccurs="0" />
+
+ <xs:element name="systemChrome" minOccurs="0" >
+ <xs:simpleType>
+ <xs:restriction base="xs:string">
+ <xs:enumeration value="none"/>
+ <xs:enumeration value="standard"/>
+ </xs:restriction>
+ </xs:simpleType>
+ </xs:element>
+ <xs:element name="transparent" type="xs:boolean"
minOccurs="0" />
+ <xs:element name="visible" type="xs:boolean"
minOccurs="0" />
+
+ <xs:element name="minimizable" type="xs:boolean"
minOccurs="0" />
+ <xs:element name="maximizable" type="xs:boolean"
minOccurs="0" />
+ <xs:element name="resizable" type="xs:boolean"
minOccurs="0" />
+
+ <xs:element name="x" type="xs:int"
minOccurs="0" />
+ <xs:element name="y" type="xs:int"
minOccurs="0" />
+ <xs:element name="width" type="xs:unsignedInt"
minOccurs="0" />
+ <xs:element name="height" type="xs:unsignedInt"
minOccurs="0" />
+ <xs:element name="minSize" type="BoundsSizeType"
minOccurs="0" />
+ <xs:element name="maxSize" type="BoundsSizeType"
minOccurs="0" />
+ </xs:all>
+ </xs:complexType>
+ </xs:element>
+
+ <!-- About installing this application -->
+ <xs:element name="installFolder" minOccurs="0">
+ <xs:simpleType>
+ <xs:restriction base="xs:string">
+ <!-- installFolder cannot begin with a / (forward-slash) or a '
' (space), have any of these characters: *":<>?\|, and end with a . (dot)
or ' ' (space) -->
+ <xs:pattern value='[^\*"/:><\?\\\|\.
]|[^\*"/:><\?\\\|
][^\*":><\?\\\|]*[^\*":><\?\\\|\. ]'/>
+ </xs:restriction>
+ </xs:simpleType>
+ </xs:element>
+ <xs:element name="programMenuFolder" minOccurs="0">
+ <xs:simpleType>
+ <xs:restriction base="xs:string">
+ <!-- programMenuFolder cannot begin with a / (forward-slash) or a '
' (space), have any of these characters: *":<>?\|, and end with a . (dot)
or ' ' (space) -->
+ <xs:pattern value='[^\*"/:><\?\\\|\.
]|[^\*"/:><\?\\\|
][^\*":><\?\\\|]*[^\*":><\?\\\|\. ]'/>
+ </xs:restriction>
+ </xs:simpleType>
+ </xs:element>
+
+ <!-- Features this application can opt in to -->
+ <xs:element name="customUpdateUI" type="xs:boolean"
minOccurs="0"/>
+ <xs:element name="allowBrowserInvocation"
type="xs:boolean" minOccurs="0"/>
+ <xs:element name="fileTypes" minOccurs="0">
+ <xs:complexType>
+ <xs:sequence>
+ <xs:element name="fileType" minOccurs="0"
maxOccurs="unbounded">
+ <xs:complexType>
+ <xs:all>
+ <xs:element name="name" type="xs:string"/>
+ <xs:element name="extension"
type="xs:string"/>
+ <xs:element name="description" type="xs:string"
minOccurs="0"/>
+ <xs:element name="contentType" type="xs:string"
minOccurs="0"/>
+ <xs:element name="icon" type="IconType"
minOccurs="0"/>
+ </xs:all>
+ </xs:complexType>
+ </xs:element>
+ </xs:sequence>
+ </xs:complexType>
+ </xs:element>
+ </xs:all>
+
+ <!-- About the runtime version required -->
+ <xs:attribute name="minimumPatchLevel"
type="xs:unsignedInt"/>
+ </xs:complexType>
+ </xs:element>
+
+ <!-- Type definitions -->
+ <xs:complexType name="IconType">
+ <xs:all>
+ <xs:element name="image16x16" type="xs:anyURI"
minOccurs="0"/>
+ <xs:element name="image32x32" type="xs:anyURI"
minOccurs="0"/>
+ <xs:element name="image48x48" type="xs:anyURI"
minOccurs="0"/>
+ <xs:element name="image128x128" type="xs:anyURI"
minOccurs="0"/>
+ </xs:all>
+ </xs:complexType>
+ <xs:simpleType name="UnsignedIntListType">
+ <xs:list itemType="xs:unsignedInt"/>
+ </xs:simpleType>
+ <xs:simpleType name="BoundsSizeType">
+ <xs:restriction base="UnsignedIntListType">
+ <xs:length value="2"/>
+ </xs:restriction>
+ </xs:simpleType>
+</xs:schema>
Added:
trunk/extensions/dna-sequencer-xml/src/test/resources/jackrabbitInMemoryTestRepositoryConfig.xml
===================================================================
---
trunk/extensions/dna-sequencer-xml/src/test/resources/jackrabbitInMemoryTestRepositoryConfig.xml
(rev 0)
+++
trunk/extensions/dna-sequencer-xml/src/test/resources/jackrabbitInMemoryTestRepositoryConfig.xml 2008-10-29
18:23:01 UTC (rev 598)
@@ -0,0 +1,116 @@
+<?xml version="1.0"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+
http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<!DOCTYPE Repository PUBLIC "-//The Apache Software Foundation//DTD Jackrabbit
1.2//EN"
+
"http://jackrabbit.apache.org/dtd/repository-1.2.dtd">
+<!-- Example Repository Configuration File -->
+<Repository>
+ <!--
+ virtual file system where the repository stores global state
+ (e.g. registered namespaces, custom node types, etc.)
+ -->
+ <FileSystem
class="org.apache.jackrabbit.core.fs.mem.MemoryFileSystem">
+ <param name="path" value="${rep.home}/repository"/>
+ </FileSystem>
+
+ <!--
+ security configuration
+ -->
+ <Security appName="Jackrabbit">
+ <!--
+ access manager:
+ class: FQN of class implementing the AccessManager interface
+ -->
+ <AccessManager
class="org.apache.jackrabbit.core.security.SimpleAccessManager">
+ <!-- <param name="config"
value="${rep.home}/access.xml"/> -->
+ </AccessManager>
+
+ <LoginModule
class="org.apache.jackrabbit.core.security.SimpleLoginModule">
+ <!-- anonymous user name ('anonymous' is the default value) -->
+ <param name="anonymousId" value="anonymous"/>
+ <!--
+ default user name to be used instead of the anonymous user
+ when no login credentials are provided (unset by default)
+ -->
+ <!-- <param name="defaultUserId"
value="superuser"/> -->
+ </LoginModule>
+ </Security>
+
+ <!--
+ location of workspaces root directory and name of default workspace
+ -->
+ <Workspaces rootPath="${rep.home}/workspaces"
defaultWorkspace="default"/>
+ <!--
+ workspace configuration template:
+ used to create the initial workspace if there's no workspace yet
+ -->
+ <Workspace name="Jackrabbit Core">
+ <!--
+ virtual file system of the workspace:
+ class: FQN of class implementing the FileSystem interface
+ -->
+
+ <FileSystem
class="org.apache.jackrabbit.core.fs.mem.MemoryFileSystem">
+ <param name="path" value="${wsp.home}"/>
+ </FileSystem>
+ <!--
+ persistence manager of the workspace:
+ class: FQN of class implementing the PersistenceManager interface
+ -->
+ <PersistenceManager
class="org.apache.jackrabbit.core.persistence.mem.InMemPersistenceManager">
+ <param name="persistent" value="false"/>
+ </PersistenceManager>
+ <!--
+ Search index and the file system it uses.
+ class: FQN of class implementing the QueryHandler interface
+ -->
+ <SearchIndex
class="org.apache.jackrabbit.core.query.lucene.SearchIndex">
+ <param name="path" value="${wsp.home}/index"/>
+ </SearchIndex>
+ </Workspace>
+
+ <!--
+ Configures the versioning
+ -->
+ <Versioning rootPath="${rep.home}/version">
+ <!--
+ Configures the filesystem to use for versioning for the respective
+ persistence manager
+ -->
+ <FileSystem
class="org.apache.jackrabbit.core.fs.mem.MemoryFileSystem">
+ <param name="path" value="${rep.home}/version" />
+ </FileSystem>
+
+ <!--
+ Configures the persistence manager to be used for persisting version state.
+ Please note that the current versioning implementation is based on
+ a 'normal' persistence manager, but this could change in future
+ implementations.
+ -->
+ <PersistenceManager
class="org.apache.jackrabbit.core.persistence.mem.InMemPersistenceManager">
+ <param name="persistent" value="false"/>
+ </PersistenceManager>
+ </Versioning>
+
+ <!--
+ Search index for content that is shared repository wide
+ (/jcr:system tree, contains mainly versions)
+ -->
+ <SearchIndex
class="org.apache.jackrabbit.core.query.lucene.SearchIndex">
+ <param name="path"
value="${rep.home}/repository/index"/>
+ </SearchIndex>
+</Repository>
Added: trunk/extensions/dna-sequencer-xml/src/test/resources/log4j.properties
===================================================================
--- trunk/extensions/dna-sequencer-xml/src/test/resources/log4j.properties
(rev 0)
+++ trunk/extensions/dna-sequencer-xml/src/test/resources/log4j.properties 2008-10-29
18:23:01 UTC (rev 598)
@@ -0,0 +1,12 @@
+# Direct log messages to stdout
+log4j.appender.stdout=org.apache.log4j.ConsoleAppender
+log4j.appender.stdout.Target=System.out
+log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
+log4j.appender.stdout.layout.ConversionPattern=%d{ABSOLUTE} %5p %m%n
+
+# Root logger option
+log4j.rootLogger=INFO, stdout
+
+# Set up the default logging to be INFO level, then override specific units
+log4j.logger.org.jboss.dna=INFO
+
Added: trunk/extensions/dna-sequencer-xml/src/test/resources/master.xml
===================================================================
--- trunk/extensions/dna-sequencer-xml/src/test/resources/master.xml
(rev 0)
+++ trunk/extensions/dna-sequencer-xml/src/test/resources/master.xml 2008-10-29 18:23:01
UTC (rev 598)
@@ -0,0 +1,1890 @@
+<!--
+ ~ JBoss, Home of Professional Open Source.
+ ~
+ ~ Copyright (c) 2008, Red Hat Middleware LLC or third-party contributors as
+ ~ indicated by the @author tags or express copyright attribution
+ ~ statements applied by the authors. All third-party contributions are
+ ~ distributed under license by Red Hat Middleware LLC.
+ ~
+ ~ This copyrighted material is made available to anyone wishing to use, modify,
+ ~ copy, or redistribute it subject to the terms and conditions of the GNU
+ ~ Lesser General Public License, as published by the Free Software Foundation.
+ ~
+ ~ This program 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 book PUBLIC "-//OASIS//DTD DocBook XML V4.4//EN"
"http://www.oasis-open.org/docbook/xml/4.4/docbookx.dtd" [
+<!ENTITY % RH-ENTITIES SYSTEM "Common_Config/rh-entities.ent">
+<!ENTITY versionNumber "0.1">
+<!ENTITY copyrightYear "2008">
+<!ENTITY copyrightHolder "Red Hat Middleware, LLC.">]>
+<book>
+ <bookinfo>
+ <title>JBoss DNA</title>
+ <subtitle>Getting Started Guide</subtitle>
+ <releaseinfo>&versionNumber;
+ </releaseinfo>
+ <productnumber>&versionNumber;
+ </productnumber>
+ <issuenum>1</issuenum>
+ <mediaobject>
+ <imageobject role="fo">
+ <imagedata fileref="images/dna-logo.png" align="center"
/>
+ </imageobject>
+ <imageobject role="pdf">
+ <imagedata fileref="images/dna-logo.png" scale="75"
align="center" />
+ </imageobject>
+ <imageobject role="html">
+ <imagedata fileref="images/dna-logo.png" depth="3cm"
/>
+ </imageobject>
+ <imageobject role="xhtml">
+ <imagedata fileref="images/dna-logo.png" depth="3cm"
/>
+ </imageobject>
+ <imageobject role="xhtml_single">
+ <imagedata fileref="images/dna-logo.png" depth="3cm"
/>
+ </imageobject>
+ </mediaobject>
+ <xi:include
xmlns:xi="http://www.w3.org/2001/XInclude"
href="Author_Group.xml" />
+ <xi:include
xmlns:xi="http://www.w3.org/2001/XInclude"
href="Legal_Notice.xml" />
+ </bookinfo>
+ <preface id="preface" revision="1">
+ <title>What this book covers</title>
+ <para>The goal of this book is to help you learn about JBoss DNA and how you
can use it in your own applications to get the
+ most out of your JCR repositories.</para>
+ <para>The first part of the book starts out with an introduction to content
repositories and an overview of the JCR API,
+ both of which are important aspects of JBoss DNA. This is followed by an overview
of the JBoss DNA project, its
+ architecture, and a basic roadmap for what's coming next.</para>
+ <para>The next part of the book covers how to download and build the examples,
how to use JBoss DNA with existing
+ repositories, and how to build and use custom sequencers.</para>
+ <para>
+ If you have any questions or comments, please feel free to contact JBoss DNA's
+ <ulink url="mailto:dna-users@jboss.org">user mailing
list</ulink>
+ or use the
+ <ulink
url="http://www.jboss.com/index.html?module=bb&op=viewforum&...
forums</ulink>
+ . If you'd like to get involved on the project, join the
+ <ulink
url="http://www.jboss.org/dna/lists.html">mailing
lists</ulink>
+ ,
+ <ulink
url="http://www.jboss.org/dna/subversion.html">download the
code</ulink>
+ and get it building, and visit our
+ <ulink
url="http://jira.jboss.org/jira/browse/DNA">JIRA issue
management system</ulink>
+ . If there's something in particular you're interested in, talk with the
community - there may be others interested in the
+ same thing.
+ </para>
+ </preface>
+ <chapter id="introduction">
+ <title>Introduction</title>
+ <para>There are a lot of choices for how applications can store information
persistently so that it can be accessed at a
+ later time and by other processes. The challenge developers face is how to use an
approach that most closely matches the
+ needs of their application. This choice becomes more important as developers choose
to focus their efforts on
+ application-specific logic, delegating much of the responsibilities for persistence
to libraries and frameworks.</para>
+ <para>
+ Perhaps one of the easiest techniques is to simply store information in
+ <emphasis>files</emphasis>
+ . The Java language makes working with files relatively easy, but Java really
doesn't provide many bells and whistles. So
+ using files is an easy choice when the information is either not complicated (for
example property files), or when users may
+ need to read or change the information outside of the application (for example log
files or configuration files). But using
+ files to persist information becomes more difficult as the information becomes more
complex, as the volume of it increases,
+ or if it needs to be accessed by multiple processes. For these situations, other
techniques often offer better choices.
+ </para>
+ <para>
+ Another technique built into the Java language is
+ <emphasis>Java serialization</emphasis>
+ , which is capable of persisting the state of an object graph so that it can be
read back in at a later time. However, Java
+ serialization can quickly become tricky if the classes are changed, and so it's
beneficial usually when the information is
+ persisted for a very short period of time. For example, serialization is sometimes
used to send an object graph from one
+ process to another.
+ </para>
+ <para>
+ One of the more popular persistence technologies is the
+ <emphasis>relational database</emphasis>
+ . Relational database management systems have been around for decades and are very
capable. The Java Database Connectivity
+ (JDBC) API provides a standard interface for connecting to and interacting with
relational databases. However, it is a
+ low-level API that requires a lot of code to use correctly, and it still
doesn't abstract away the DBMS-specific SQL
+ grammar. Also, working with relational data in an object-oriented language can feel
somewhat unnatural, so many developers
+ map this data to classes that fit much more cleanly into their application. The
problem is that manually creating this
+ mapping layer requires a lot of repetitive and non-trivial JDBC code.
+ </para>
+ <para>
+ <emphasis>Object-relational mapping</emphasis>
+ libraries automate the creation of this mapping layer and result in far less code
that is much more maintainable with
+ performance that is often as good as (if not better than) handwritten JDBC code.
The new
+ <ulink
url="http://java.sun.com/developer/technicalArticles/J2EE/jpa/"...
Persistence API (JPA)</ulink>
+ provide a standard mechanism for defining the mappings (through annotations) and
working with these entity objects. Several
+ commercial and open-source libraries implement JPA, and some even offer additional
capabilities and features that go beyond
+ JPA. For example,
+ <ulink url="http://www.hibernate.org">Hibernate</ulink>
+ is one of the most feature-rich JPA implementations and offers object caching,
statement caching, extra association
+ mappings, and other features that help to improve performance and usefulness.
+ </para>
+ <para>
+ While relational databases and JPA are solutions that work for many applications,
they become more limited in cases when the
+ information structure is highly flexible, is not known
+ <emphasis>a priori</emphasis>
+ , or is subject to frequent change and customization. In these situations,
+ <emphasis>content repositories</emphasis>
+ may offer a better choice for persistence. Content repositories are almost a hybrid
between relational databases and file
+ systems, and typically provide other capabilities as well, including versioning,
indexing, search, access control,
+ transactions, and observation. Because of this, content repositories are used by
content management systems (CMS), document
+ management systems (DMS), and other applications that manage electronic files
(e.g., documents, images, multi-media, web
+ content, etc.) and metadata associated with them (e.g., author, date, status,
security information, etc.). The
+ <ulink
url="http://www.jcp.org/en/jsr/detail?id=170">Content
Repository for Java technology API</ulink>
+ provides a standard Java API for working with content repositories. Abbreviated
"JCR", this API was developed as part of the
+ Java Community Process under
+ <ulink
url="http://www.jcp.org/en/jsr/detail?id=170">JSR-170</ul...
+ and is being revised under
+ <ulink
url="http://www.jcp.org/en/jsr/detail?id=283">JSR-283</ul...
+ .
+ </para>
+ <para>
+ The
+ <emphasis>JBoss DNA project</emphasis>
+ is building the tools and services that surround content repositories. Nearly all
of these capabilities are to be hidden
+ below the JCR API and involve automated processing of the information in the
repository. Thus, JBoss DNA can add value to
+ existing repository implementations. For example, JCR repositories offer the
ability to upload files into the repository and
+ have the file content indexed for search purposes. JBoss DNA also defines a library
for "sequencing" content - to extract
+ meaningful information from that content and store it in the repository, where it
can then be searched, accessed, and
+ analyzed using the JCR API.
+ </para>
+ <para> JBoss DNA is building other features as well. One goal of JBoss DNA is
to create federated repositories that
+ dynamically merge the information from multiple databases, services, applications,
and other JCR repositories. Another is to
+ create customized views based upon the type of data and the role of the user that
is accessing the data. And yet another is
+ to create a REST-ful API to allow the JCR content to be accessed easily by other
applications written in other languages.
+ </para>
+ <para>
+ The
+ <link linkend="jboss_dna">next chapter</link>
+ in this book goes into more detail about JBoss DNA and its architecture, the
different components, what's available now, and
+ what's coming in future releases.
+ <link linkend="downloading_and_running">Chapter 3</link>
+ then provides instructions for downloading and running the sequencer examples for
the current release.
+ <link linkend="using_dna">Chapter 4</link>
+ walks through how to use JBoss DNA in your applications, while
+ <link linkend="custom_sequencers">Chapter 5</link>
+ goes over how to create custom sequencers. Finally,
+ <link linkend="future_directions">Chapter 6</link>
+ wraps things up with a discussion about the future of JBoss DNA.
+ </para>
+ </chapter>
+ <chapter id="jboss_dna">
+ <title>Understanding JBoss DNA</title>
+ <sect1 id="jboss_dna_overview">
+ <title>Overview</title>
+ <para>JBoss DNA is a repository and set of tools that make it easy to
capture, version, analyze, and understand the
+ fundamental building blocks of information. As models, service and process
definitions, schemas, source code, and other
+ artifacts are added to the repository, JBoss DNA "sequences" the makeup
of these components and extracts their structure
+ and interdependencies. The JBoss DNA web application allows end users to access,
visualize, and edit this information in
+ the terminology and structure they are familiar with. Such domain-specific
solutions can be easily created with little or
+ no programming.</para>
+ <para> JBoss DNA supports the Java Content Repository (JCR) standard and is
able to provide a single integrated view of
+ multiple repositories, external databases, services, and applications, ensuring
that JBoss DNA has access to the latest
+ and most reliable master data. For instance, DNA could provide in a single view
valuable insight into the business
+ processes and process-level services impacted by a change to in an intermediary
web server operation defined via WSDL.
+ Similarly, a user could quickly view and navigate the dependencies between the
data source models and transformation
+ information stored within a content repository, the code base stored within a
version control system, and the database
+ schemas used by an application.</para>
+ </sect1>
+ <sect1 id="architecture">
+ <title>Architecture</title>
+ <para>The architecture for JBoss DNA consists of several major components
that will be built on top of standard APIs,
+ including JCR, JDBC, JNDI and HTTP. The goal is to allow these components to be
assembled as needed and add value on top
+ of other DNA components or third-party systems that support these standard
APIs.</para>
+ <mediaobject>
+ <imageobject role="fo">
+ <imagedata align="center"
fileref="images/dna-architecture.png" />
+ </imageobject>
+ <imageobject role="html">
+ <imagedata align="center"
fileref="images/dna-architecture.png" />
+ </imageobject>
+ </mediaobject>
+ <para>
+ As shown in the diagram above, the major components are (starting at the top):
+ <itemizedlist>
+ <listitem>
+ <para>
+ <emphasis role="strong">DNA Eclipse
Plugins</emphasis>
+ enable Eclipse users to access the contents of a JBoss DNA repository.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <emphasis role="strong">DNA JDBC Driver</emphasis>
+ provides a driver implementation, allowing JDBC-aware applications to
connect to and use a JBoss DNA repository.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <emphasis role="strong">DNA Remote JCR</emphasis>
+ is a client-side component for accessing remote JCR repositories.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <emphasis role="strong">DNA Web
Application</emphasis>
+ is used by end users and domain experts to visualize, search, edit, change
and tag the repository content. The web
+ application uses views to define how different types of information are to
be presented and edited in
+ domain-specific ways. The goal is that this web application is easily
customized and branded for inclusion into
+ other solutions and application systems. The DNA Web Application operates
upon any JCR-compliant repository,
+ although it does rely upon the DNA analysis and templating services.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <emphasis role="strong">DNA Publishing
Server</emphasis>
+ allows content to be downloaded, uploaded, and edited using the Atom
Publishing Protocol. With the DNA Publishing
+ Server, the content of the repository can easily be created, read, edited,
and deleted using the standard HTTP
+ operations of POST, GET, PUT, and DELETE (respectively). More and more
tools are being created that support working
+ with Atom Publishing servers. The DNA Publishing Server operates upon any
JCR-compliant repository.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <emphasis role="strong">DNA WebDAV Server</emphasis>
+ allows clients such as Microsoft Windows and Apple OS X to connect to,
read, and edit the content in the repository
+ using the WebDAV standard. Since WebDAV is an extension of HTTP, web
browsers are able to read (but not modify) the
+ content served by a WebDAV compliant server. The DNA WebDAV Server operates
upon any JCR-compliant repository.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <emphasis role="strong">DNA Sequencers</emphasis>
+ are pluggable components that make it possible for content to be uploaded
to the repository and automatically
+ processed to extract meaningful structure and place that structure in the
repository. Once this information is in
+ the repository, it can be viewed, edited, analyzed, searched, and related
to other content. DNA defines a Java
+ interface that sequencers must implement. DNA sequencers operate upon any
JCR-compliant repository.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <emphasis role="strong">DNA Analyses</emphasis>
+ are pluggable components that analyze content and the relationships between
content to generate reports or to answer
+ queries. DNA will include some standard analyzers, like dependency analysis
and similarity analysis, that are
+ commonly needed by many different solutions. DNA analyzers operate upon any
JCR-compliant repository.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <emphasis role="strong">DNA Views</emphasis>
+ are definitions of how types of information are to be presented in a user
interface to allow for creation, reading,
+ editing, and deletion of information. DNA view definitions consist of data
stored in a JCR repository, and as such
+ views can be easily added, changed or removed entirely by using the DNA Web
Application, requiring no programming.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <emphasis role="strong">DNA Federation</emphasis>
+ is an implementation of the JCR API that builds the content within the
repository by accessing and integrating
+ information from multiple sources. DNA Federation allows the integration of
external systems, like other JCR
+ repositories, databases, applications, and services.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <emphasis role="strong">DNA Connectors</emphasis>
+ are used to communicate with these external sources of information. In the
federation engine, each source is able to
+ contribute node structure and node properties to any part of the federated
graph, although typically many connectors
+ will contribute most of their information to isolated subgraphs. The result
is that integration from a wide range of
+ systems can be integrated and accessed through the DNA Web Application, DNA
Publishing Server, and DNA WebDAV
+ Server. Connectors also may optionally participate in distributed
transactions by exposing an XAResource.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <emphasis role="strong">DNA Maven</emphasis>
+ is a classloader library compatible with Maven 2 project dependencies. This
allows the creation of Java ClassLoader
+ instances using Maven 2 style paths, and all dependencies are transitively
managed and included.
+ </para>
+ </listitem>
+ </itemizedlist>
+ </para>
+ <para>
+ Continue reading the rest of this chapter for more detail about the
+ <link linkend="sequencers">sequencing framework</link>
+ available in this release, or the
+ <link linkend="federation">federation engine</link>
+ and
+ <link linkend="federation_connectors">connectors</link>
+ that will be the focus of the next release. Or, skip to the
+ <link linkend="downloading_and_running">examples</link>
+ to see how to start using JBoss DNA &versionNumber;
+ today.
+ </para>
+ </sect1>
+ <sect1 id="sequencers">
+ <title>Sequencing content</title>
+ <para> The current JBoss DNA release contains a sequencing framework that is
designed to sequence data (typically files)
+ stored in a JCR repository to automatically extract meaningful and useful
information. This additional information is then
+ saved back into the repository, where it can be accessed and used.</para>
+ <para> In other words, you can just upload various kinds of files into a JCR
repository, and DNA automatically processes
+ those files to extract meaningful structured information. For example, load DDL
files into the repository, and let
+ sequencers extract the structure and metadata for the database schema. Load
Hibernate configuration files into the
+ repository, and let sequencers extract the schema and mapping information. Load
Java source into the repository, and let
+ sequencers extract the class structure, JavaDoc, and annotations. Load a PNG,
JPEG, or other image into the repository,
+ and let sequencers extract the metadata from the image and save it in the
repository. The same with XSDs, WSDL, WS
+ policies, UML, MetaMatrix models, etc.</para>
+ <para>
+ JBoss DNA sequencers sit on top of existing JCR repositories (including federated
repositories) - they basically extract
+ more useful information from what's already stored in the repository. And
they use the existing JCR versioning system. Each
+ sequencer typically processes a single kind of file format or a single kind of
content. </para>
+ <para>The following sequencers are included in JBoss DNA:
+ <itemizedlist>
+ <listitem>
+ <para>
+ <emphasis role="strong">Image sequencer</emphasis>
+ - A sequencer that processes the binary content of an image file, extracts
the metadata for the image, and then
+ writes that image metadata to the repository. It gets the file format,
image resolution, number of bits per pixel
+ (and optionally number of images), comments and physical resolution from
JPEG, GIF, BMP, PCX, PNG, IFF, RAS, PBM,
+ PGM, PPM, and PSD files. (This sequencer may be improved in the future to
also extract EXIF metadata from JPEG
+ files; see
+ <ulink
url="http://jira.jboss.org/jira/browse/DNA-26">DNA-26</ul...
+ .)
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <emphasis role="strong">MP3 sequencer</emphasis>
+ - A sequencer that processes the contents of an MP3 audio file, extracts
the metadata for the file, and then
+ writes that image metadata to the repository. It gets the title, author,
album, year, and comment.
+ (This sequencer may be improved in the future to also extract other ID3
metadata from other audio file formats; see
+ <ulink
url="http://jira.jboss.org/jira/browse/DNA-66">DNA-26</ul...
+ .)
+ </para>
+ </listitem>
+ </itemizedlist>
+ </para>
+ <para>
+ As the community develops additional sequencers, they will also be included in
JBoss DNA. Some of those that have been
+ identified as being useful include:
+ <itemizedlist>
+ <listitem>
+ <para>
+ <emphasis role="strong">XML Schema Document (XSD)
Sequencer</emphasis>
+ - Process XSD files and extract the various elements, attributes, complex
types, simple types, groups, and other
+ information. (See
+ <ulink
url="http://jira.jboss.org/jira/browse/DNA-32">DNA-32</ul...
+ )
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <emphasis role="strong">Web Service Definition Language
(WSDL) Sequencer</emphasis>
+ - Process WSDL files and extract the services, bindings, ports, operations,
parameters, and other information. (See
+ <ulink
url="http://jira.jboss.org/jira/browse/DNA-33">DNA-33</ul...
+ )
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <emphasis role="strong">Hibernate File
Sequencer</emphasis>
+ - Process Hibernate configuration (cfg.xml) and mapping (hbm.xml) files to
extract the configuration and mapping
+ information. (See
+ <ulink
url="http://jira.jboss.org/jira/browse/DNA-61">DNA-61</ul...
+ )
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <emphasis role="strong">XML Metadata Interchange (XMI)
Sequencer</emphasis>
+ - Process XMI documents that contain UML models or models using another
metamodel, extracting the model structure
+ into the repository. (See
+ <ulink
url="http://jira.jboss.org/jira/browse/DNA-31">DNA-31</ul...
+ )
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <emphasis role="strong">ZIP Archive
Sequencer</emphasis>
+ - Process ZIP archive files to extract (explode) the contents into the
repository. (See
+ <ulink
url="http://jira.jboss.org/jira/browse/DNA-63">DNA-63</ul...
+ )
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <emphasis role="strong">Java Archive (JAR)
Sequencer</emphasis>
+ - Process JAR files to extract (explode) the contents into the classes and
file resources. (See
+ <ulink
url="http://jira.jboss.org/jira/browse/DNA-64">DNA-64</ul...
+ )
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <emphasis role="strong">Java Class File
Sequencer</emphasis>
+ - Process Java class files (bytecode) to extract the class structure
(including annotations) into the repository.
+ (See
+ <ulink
url="http://jira.jboss.org/jira/browse/DNA-62">DNA-62</ul...
+ )
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <emphasis role="strong">Java Source File
Sequencer</emphasis>
+ - Process Java source files to extract the class structure (including
annotations) into the repository. (See
+ <ulink
url="http://jira.jboss.org/jira/browse/DNA-51">DNA-51</ul...
+ )
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <emphasis role="strong">PDF Sequencer</emphasis>
+ - Process PDF files to extract the document metadata, including table of
contents. (See
+ <ulink
url="http://jira.jboss.org/jira/browse/DNA-50">DNA-50</ul...
+ )
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <emphasis role="strong">Maven 2 POM
Sequencer</emphasis>
+ - Process Maven 2 Project Object Model (POM) files to extract the project
information, dependencies, plugins, and
+ other content. (See
+ <ulink
url="http://jira.jboss.org/jira/browse/DNA-24">DNA-24</ul...
+ )
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <emphasis role="strong">Data Definition Language (DDL)
Sequencer</emphasis>
+ - Process various dialects of DDL, including that from Oracle, SQL Server,
MySQL, PostgreSQL, and others. May need
+ to be split up into a different sequencer for each dialect. (See
+ <ulink
url="http://jira.jboss.org/jira/browse/DNA-26">DNA-26</ul...
+ )
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <emphasis role="strong">MP3 and MP4
Sequencer</emphasis>
+ - Process MP3 and MP4 audio files to extract the name of the song, artist,
album, track number, and other metadata.
+ (See
+ <ulink
url="http://jira.jboss.org/jira/browse/DNA-30">DNA-30</ul...
+ )
+ </para>
+ </listitem>
+ </itemizedlist>
+ </para>
+ <para>
+ The
+ <link linkend="using_dna">examples</link>
+ in this book go into more detail about how sequencers are managed and used, and
+ <link linkend="custom_sequencers">Chapter 5</link>
+ goes into detail about how to write custom sequencers.
+ </para>
+ </sect1>
+ <sect1 id="federation">
+ <title>Federating content</title>
+ <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 move the information into a JCR repository? Most likely
there are existing applications that rely upon
+ that information being where it is. If we were to move it, then all those
applications would break. Or they'd have to be
+ changed to use JCR. If the information is being used, the most practical thing is
to leave it where it is.</para>
+ <para>
+ Then why not just copy the information into a JCR repository? Actually, there are
times when it's perfectly reasonable to
+ make a copy of the data. Perhaps the system managing the existing information
cannot handle the additional load of more
+ clients. Or, perhaps the information doesn't change, or it does change and we
want snapshots that don't change. But more
+ likely, the data
+ <emphasis>does</emphasis>
+ change. So if applications are to use the most current information and we make
copies of the data, we have to keep the
+ copies synchronized with the master. That's generally a lot of work.
+ </para>
+ <para>The JBoss DNA federation engine lets us leave the information where it
is, yet lets client applications use the JCR
+ API to access all the information without caring where the information really
exists. If the underlying information
+ changes, client applications using JCR observation will be notified of the
changes. If a JBoss DNA federated repository is
+ configured to allow updates, client applications can change the information in
the repository and JBoss DNA will propagate
+ those changes down to the original source.</para>
+ <sect2 id="federation_connectors">
+ <title>Connecting to information sources</title>
+ <para>
+ The JBoss DNA federation engine will use connectors to interact with different
information sources to get at the content
+ in those systems. Some ideas for connectors include:
+ <itemizedlist>
+ <listitem>
+ <para>
+ <emphasis role="strong">JCR Repository
Connector</emphasis>
+ - Connect to and interact with other JCR repositories.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <emphasis role="strong">File System
Connector</emphasis>
+ - Expose the files and directories on a file system through JCR.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <emphasis role="strong">Maven 2 Repository
Connector</emphasis>
+ - Access and expose the contents of a Maven 2 repository (either on the
local file system or via HTTP) through
+ JCR.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <emphasis role="strong">JDBC Metadata
Connector</emphasis>
+ - Connect to relational databases via JDBC and expose their schema as
content in a repository.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <emphasis role="strong">UDDI Connector</emphasis>
+ - Interact with UDDI registries to integrate their content into a
repository.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <emphasis role="strong">SVN Connector</emphasis>
+ - Interact with Subversion software configuration management (SCM)
repositories to expose the managed resources
+ through JCR. Consider using the
+ <ulink
url="http://svnkit.com/">SVNkit</ulink>
+ (dual license) library for an API into Subversion.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <emphasis role="strong">CVS Connector</emphasis>
+ - Interact with CVS software configuration management (SCM) repositories
to expose the managed resources through
+ JCR.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <emphasis role="strong">JDBC Storage
Connector</emphasis>
+ - Store and access information in a relational database. Also useful for
persisting information in the federated
+ repository not stored elsewhere.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <emphasis role="strong">Distributed Database
Connector</emphasis>
+ - Store and access information in a
+ <ulink
url="http://www.hypertable.org/">Hypertable</ulink>
+ or
+ <ulink
url="http://hadoop.apache.org/hbase/">HBase</ulink>
+ distributed databases. Also useful for persisting information in the
federated repository not stored elsewhere.
+ </para>
+ </listitem>
+ </itemizedlist>
+ </para>
+ <para>
+ If the connectors allow the information they contribute to be updated, they
must provide an
+ <code>XAResource</code>
+ implementation that can be used with a Java Transaction Service. Connectors
that provide read-only access need not
+ provide an implementation.
+ </para>
+ <para>
+ Also, connectors talk to
+ <emphasis>sources</emphasis>
+ of information, and it's quite likely that the same connector is used to
talk to different sources. Each source contains
+ the configuration details (e.g., connection information, location, properties,
options, etc.) for working with that
+ particular source, as well as a reference to the connector that should be used
to establish connections to the source.
+ And of course, sources can be added or removed without having to stop and
restart the federated repository.
+ </para>
+ </sect2>
+ <sect2 id="federation_graph">
+ <title>Building the unified graph</title>
+ <para> The federation engine 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 not 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>
+ </sect2>
+ <sect2 id="federation_queries">
+ <title>Searching and querying</title>
+ <para> The JBoss DNA federated repository will also support queries against
the integrated and unified graph. In some
+ situations the query can be determined to apply to a single source, but in most
situations the query must be planned
+ (and possibly rewritten) such that it can be pushed down to all the appropriate
sources. Also, the cached results must
+ be consulted prior to returning the query results, as the results from one
source might have contributions from another
+ source.</para>
+ <note>
+ <para> It is hoped that the MetaMatrix query engine can be used for this
purpose after it is open-sourced. This engine
+ implements sophisticated query planning and optimization techniques for
working efficiently with multiple sources.
+ </para>
+ </note>
+ <para>Searching the whole federated repository is also important. This
allows users to simply supply a handful of
+ search terms, and to get results that are ranked based upon how close each
result is to the search terms. (Searching is
+ very different from querying, which involves specifying the exact semantics of
what is to be searched and how the
+ information is to be compared.) JBoss DNA will incorporate a search engine
(e.g., likely to be Lucene) and will populate
+ the engine's indexes using the federated content and the cached
information. Notifications of changing information will
+ be reflected in the indexes, but some sources may want to explicitly allow or
disallow periodic crawling of their
+ content.</para>
+ </sect2>
+ <sect2 id="federation_updates">
+ <title>Updating content</title>
+ <para>
+ The JBoss DNA federated repositories also make it possible for client
applications to make changes to the unified graph
+ within the context of distributed transactions. According to the JCR API,
client applications use the Java Transaction
+ API (JTA) to control the boundaries of their transactions. Meanwhile, the
federated repository uses a
+ <ulink
url="http://www.jboss.org/jbosstm/">distributed
transaction service</ulink>
+ to coordinate the XA resources provided by the connectors.
+ </para>
+ <para> It is quite possible that clients add properties to nodes in the
unified graph, and that this information cannot be
+ handled by the same underlying source that contributed to the node. In this
case, the federated repository can be
+ configured with a fallback source that will be used used to store this
"extra" information.</para>
+ <para>
+ It is a goal that non-XA sources (i.e., sources that use connectors without XA
resources) can participate in distributed
+ transactions through the use of
+ <emphasis>compensating transactions</emphasis>
+ . Because the JBoss DNA federation engine implements the JCR observation
system, it is capable of recording all of the
+ changes made to the distributed graph (and those changes sent to each updatable
source). Therefore, if a non-XA source
+ is involved in a distributed transaction that must be rolled back, any changes
made to non-XA sources can be undone. (Of
+ course, this does not make the underlying source transactional:
non-transactional sources still may expose the interim
+ changes to other clients.)
+ </para>
+ </sect2>
+ <sect2 id="federation_events">
+ <title>Observing changes</title>
+ <para> The JCR API supports observing a repository to receive notifications
of additions, changes and deletions of nodes
+ and properties. The JBoss DNA federated repository will support this API
through two primary means.</para>
+ <para> When the changes are made through the federated repository, the
JBoss DNA federation engine is well aware of the
+ set of changes that have been (or are being) made to the unified graph. These
events are directly propagated to
+ listeners.</para>
+ <para> Sources have the ability to publish events, making it possible for
the JBoss DNA federation engine and clients that
+ have registered listeners to be notified of changes in the information managed
by that source. These events are first
+ processed by the federation engine and possibly altered based upon
contributions from other sources. (The federation
+ engine also uses these events to update or purge information in the cache,
which may add to the event set.) The
+ resulting (and possibly altered) event set is then sent to all client
listeners.</para>
+ </sect2>
+ </sect1>
+ </chapter>
+ <!--
====================================================================================================
+ Chapter
+
====================================================================================================
-->
+ <chapter id="downloading_and_running">
+ <title>Running the example application</title>
+ <para>
+ This chapter provides instructions for downloading and running a sample application
that demonstrates how JBoss DNA works
+ with a JCR repository to automatically sequence changing content to extract useful
information. So read on to get the simple
+ application running, and then in the
+ <link linkend="using_dna">next chapter</link>
+ we'll dive into the source code for the example and show how to use JBoss DNA
in your own applications.
+ </para>
+ <para>JBoss DNA uses Maven 2 for its build system, as is this example. Using
Maven 2 has several advantages, including
+ the ability to manage dependencies. If a library is needed, Maven automatically
finds and downloads that library, plus
+ everything that library needs. This means that it's very easy to build the
examples - or even create a maven project that
+ depends on the JBoss DNA JARs.</para>
+ <note>
+ <para>
+ To use Maven with JBoss DNA, you'll need to have
+ <ulink
url="http://java.sun.com/javase/downloads/index_jdk5.jsp">JDK 5 or
6</ulink>
+ and Maven 2.0.7 (or higher).
+ </para>
+ <para>
+ Maven can be downloaded from
+ <ulink
url="http://maven.apache.org/">http://maven.apache.org/</...
+ , and is installed by unzipping the
+ <code>maven-2.0.7-bin.zip</code>
+ file to a convenient location on your local disk. Simply add
+ <code>$MAVEN_HOME/bin</code>
+ to your path and add the following profile to your
+ <code>~/.m2/settings.xml</code>
+ file:
+ <programlisting role="XML"
language="xml"><settings>
+ <profiles>
+ <profile>
+ <id>jboss.repository</id>
+ <activation>
+ <property>
+ <name>!jboss.repository.off</name>
+ </property>
+ </activation>
+ <repositories>
+ <repository>
+ <id>snapshots.jboss.org</id>
+ <url>http://snapshots.jboss.org/maven2</url>
+ <snapshots>
+ <enabled>true</enabled>
+ </snapshots>
+ </repository>
+ <repository>
+ <id>repository.jboss.org</id>
+ <url>http://repository.jboss.org/maven2</url>
+ <snapshots>
+ <enabled>false</enabled>
+ </snapshots>
+ </repository>
+ </repositories>
+ <pluginRepositories>
+ <pluginRepository>
+ <id>repository.jboss.org</id>
+ <url>http://repository.jboss.org/maven2</url>
+ <snapshots>
+ <enabled>false</enabled>
+ </snapshots>
+ </pluginRepository>
+ <pluginRepository>
+ <id>snapshots.jboss.org</id>
+ <url>http://snapshots.jboss.org/maven2</url>
+ <snapshots>
+ <enabled>true</enabled>
+ </snapshots>
+ </pluginRepository>
+ </pluginRepositories>
+ </profile>
+ </profiles>
+</settings></programlisting>
+ This profile informs Maven of the two JBoss repositories (snapshots and releases)
that contain
+ all of the JARs for JBoss DNA and all dependent libraries.</para>
+ </note>
+ <sect1 id="downloading">
+ <title>Downloading and compiling</title>
+ <para>The next step is to <ulink
url="http://www.jboss.org/file-access/default/members/dna/downloads/...
+ the example for this Getting Started guide, and extract the contents to a
convenient location on your local disk.
+ You'll find the example contains the following files, which are organized
according to the standard Maven directory structure:
+ <programlisting>
+examples/pom.xml
+ sequencers/pom.xml
+ /src/main/assembly
+ /config
+ /java
+ /resources
+ /test/java
+ /resources
+ </programlisting>
+ </para>
+ <para>There are essentially two Maven projects: a
<code>sequencers</code> project and a parent project. All of the source
+ for the example is located in the <code>sequencers</code> subdirectory.
And you may have noticed that none
+ of the JBoss DNA libraries are there. This is where Maven comes in. The two
<code>pom.xml</code> files tell
+ Maven everything it needs to know about what libraries are required and how to
build the example.</para>
+ <para>In a terminal, go to the <code>examples</code> directory
and run <emphasis role="strong"><code>mvn
install</code></emphasis>.
+ This command downloads all of the JARs necessary to compile and build the example,
including the JBoss DNA libraries,
+ the libraries they depend on, and any missing Maven components. (These are
downloaded from the JBoss repositories
+ only once and saved on your machine. This means that the next time you run Maven,
all the libraries will
+ already be available locally, and the build will run much faster.) The command
then continues by compiling the example's source
+ code (and unit tests) and running the unit tests. The build is successful if you
see the following:
+ <programlisting language="bash">$ mvn install
+...
+[INFO] ------------------------------------------------------------------------
+[INFO] Reactor Summary:
+[INFO] ------------------------------------------------------------------------
+[INFO] Getting Started examples .............................. SUCCESS [2.106s]
+[INFO] Sequencer Examples .................................... SUCCESS [9.768s]
+[INFO] ------------------------------------------------------------------------
+[INFO] ------------------------------------------------------------------------
+[INFO] BUILD SUCCESSFUL
+[INFO] ------------------------------------------------------------------------
+[INFO] Total time: 12 seconds
+[INFO] Finished at: Wed May 07 12:00:06 CDT 2008
+[INFO] Final Memory: 14M/28M
+[INFO] ------------------------------------------------------------------------
+$ </programlisting>
+ If there are errors, check whether you have the correct version of Maven installed
and that you've correctly updated
+ your Maven settings as described above.</para>
+ <para>If you've successfully built the examples, there will be a
<code>examples/sequencers/target/dna-example-sequencers-basic.dir/</code>
+ directory that contains the following:
+ <itemizedlist>
+ <listitem>
+ <para><emphasis
role="strong"><code>run.sh</code></emphasis> is the *nix
shell script that will run the example.</para>
+ </listitem>
+ <listitem>
+ <para>
+ <emphasis role="strong">
+ <code>log4j.properties</code>
+ </emphasis>
+ is the Log4J configuration file.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <emphasis role="strong">
+ <code>jackrabbitConfig.xml</code>
+ </emphasis>
+ is the Jackrabbit configuration file, which is set up to use a transient
in-memory repository.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <emphasis role="strong">
+ <code>jackrabbitNodeTypes.cnd</code>
+ </emphasis>
+ defines the additional JCR node types used by this example.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <emphasis role="strong">
+ <code>sample1.mp3</code>
+ </emphasis>
+ is a sample MP3 audio file you'll use later to upload into the repository.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <emphasis role="strong">
+ <code>caution.gif</code>
+ </emphasis>, <emphasis role="strong">
+ <code>caution.png</code>
+ </emphasis>, and <emphasis role="strong">
+ <code>caution.jpg</code>
+ </emphasis>
+ are images that you'll use later and upload into the repository.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <emphasis role="strong">
+ <code>lib</code>
+ </emphasis>
+ subdirectory contains the JARs for all of the JBoss DNA artifacts as well as
those for other libraries required
+ by JBoss DNA and the example.
+ </para>
+ </listitem>
+ </itemizedlist>
+ <note>
+ <para>JBoss DNA 0.1 and the examples are currently tested with <ulink
url="http://jackrabbit.apache.org/">Apache Jackrabbit</ulink> version
1.3.3.
+ This version is stable and used by a number of other projects and applications.
However, you should be able to use a newer
+ version of Jackrabbit, as long as that version uses the same JCR API. For example,
version 1.4.2 was released on March 26, 2008 and
+ should be compatible.
+ </para>
+ <para>Just remember, if the version of Jackrabbit you want to use for these
examples is not in the Maven repository,
+ you'll have to either add it or add it locally. For more information, see the
<ulink
url="http://maven.apache.org/">Maven documentation</ulink>.
+ </para>
+ </note>
+ </para>
+ </sect1>
+ <sect1 id="running">
+ <title>Running the example</title>
+ <para>This example consists of a client application that sets up an in-memory
JCR repository and that allows a user to
+ upload files into that repository. The client also sets up the DNA services with
two sequencers so that if any of the
+ uploaded files are PNG, JPEG, GIF, BMP or other images, DNA will automatically
extract the image's metadata (e.g., image
+ format, physical size, pixel density, etc.) and store that in the repository.
Alternatively, if the uploaded file
+ is an MP3 audio file, DNA will extract some of the ID3 metadata (e.g., the author,
title, album, year and comment)
+ and store that in the repository.</para>
+ <para>
+ To run the client application, go to the
+ <code>examples/sequencers/target/dna-example-sequencers-basic.dir/
+ </code>
+ directory and type
+ <code>./run.sh</code>
+ . You should see the command-line client and its menus in your terminal:
+ <figure id="xample-sequencer-cli-client">
+ <title>Example Client</title>
+ <graphic align="center" scale="100"
fileref="images/example-sequencer-cli-client.png" />
+ </figure>
+ From this menu, you can upload a file into the repository, search for media in the
repository, print sequencing statistics,
+ or quit the application.
+ </para>
+ <para>
+ The first step is to upload one of the example images. If you type 'u' and
press return, you'll be prompted to supply the
+ path to the file you want to upload. Since the application is running from within
the
+ <code>examples/sequencers/target/dna-example-sequencers-basic.dir/
+ </code>
+ directory, you can specify any of the files in that directory without specifying
the path:
+ <figure id="example-sequencer-upload">
+ <title>Uploading an image using the Example Client</title>
+ <graphic align="center" scale="100"
fileref="images/example-sequencer-upload.png" />
+ </figure>
+ You can specify any fully-qualified or relative path. The application will notify
you if it cannot find the file you
+ specified. The example client configures JBoss DNA to sequence and MP3 audio files
and image files with one of
+ the following extensions (technically, nodes that have names ending in the
following):
+ <code>jpg</code>
+ ,
+ <code>jpeg</code>
+ ,
+ <code>gif</code>
+ ,
+ <code>bmp</code>
+ ,
+ <code>pcx</code>
+ ,
+ <code>png</code>
+ ,
+ <code>iff</code>
+ ,
+ <code>ras</code>
+ ,
+ <code>pbm</code>
+ ,
+ <code>pgm</code>
+ ,
+ <code>ppm</code>
+ , and
+ <code>psd</code>
+ . Files with other extensions in the repository path will be ignored. For your
convenience, the example provides several
+ files that will be sequenced (
+ <code>caution.png</code>
+ ,
+ <code>caution.jpg</code>
+ ,
+ <code>caution.gif</code>
+ , and
+ <code>sample1.mp3</code>
+ ) and one image that will not be sequenced (
+ <code>caution.pict</code>
+ ). Feel free to try other files.
+ </para>
+ <para>
+ After you have specified the file you want to upload, the example application asks
you where in the repository you'd like to
+ place the file. (If you want to use the suggested location, just press
+ <code>return</code>
+ .) The client application uses the JCR API to upload the file to that location in
the repository, creating any nodes (of
+ type
+ <code>nt:folder</code>
+ ) for any directories that don't exist, and creating a node (of type
+ <code>nt:file</code>
+ ) for the file. And, per the JCR specification, the application creates a
+ <code>jcr:content</code>
+ node (of type
+ <code>nt:resource</code>
+ ) under the file node. The file contents are placed on this
+ <code>jcr:content</code>
+ node in the
+ <code>jcr:data</code>
+ property. For example, if you specify
+ <code>/a/b/caution.png</code>
+ , the following structure will be created in the repository:<programlisting>
+ /a (nt:folder)
+ /b (nt:folder)
+ /caution.png (nt:file)
+ /jcr:content (nt:resource)
+ @jcr:data = {contents of the file}
+ @jcr:mimeType = {mime type of the file}
+ @jcr:lastModified = {now}
+ </programlisting>
+ Other kinds of files are treated in a similar way.
+ </para>
+ <para>
+ When the client uploads the file using the JCR API, DNA gets notified of the changes,
consults the sequencers to see whether
+ any of them are interested in the new or updated content, and if so runs those
sequencers. The image sequencer processes image
+ files for metadata, and any metadata found is stored under the
+ <code>/images</code>
+ branch of the repository. The MP3 sequencer processes MP3 audio files for metadata,
and any metadata found is stored under the
+ <code>/mp3s</code>
+ branch of the repository. All of this happens asynchronously, so any DNA activity
doesn't impede or slow down the client
+ activities.
+ </para>
+ <para>
+ So, after the file is uploaded, you can search the repository for the image metadata
using the "s" menu option:
+ <figure id="example-sequencer-search">
+ <title>Searching for media using the Example Client</title>
+ <graphic align="center" scale="100"
fileref="images/example-sequencer-search.png" />
+ </figure>
+ Here are the search results after the <code>sample1.mp3</code> audio file
has been uploaded (to the <code>/a/b/sample1.mp3</code> location):
+ <figure id="example-sequencer-search-with-mp3">
+ <title>Searching for media using the Example Client</title>
+ <graphic align="center" scale="100"
fileref="images/example-sequencer-search-with-mp3.png" />
+ </figure>
+ You can also display the sequencing statistics using the "d" menu option:
+ <figure id="example-sequencer-statistics">
+ <title>Sequencing statistics using the Example Client</title>
+ <graphic align="center" scale="100"
fileref="images/example-sequencer-statistics.png" />
+ </figure>
+ These stats show how many nodes were sequenced, and how many nodes were skipped
because they didn't apply to the sequencer's
+ criteria.
+ </para>
+ <note>
+ <para>
+ There will probably be more nodes skipped than sequenced, since there are more
+ <code>nt:folder</code>
+ and
+ <code>nt:resource</code>
+ nodes than there are
+ <code>nt:file</code>
+ nodes with acceptable names.
+ </para>
+ </note>
+ <para>You can repeat this process with other files. Any file that isn't an
image or MP3 files (as recognized by the sequencing configurations
+ that we'll describe later) will not be sequenced.</para>
+ </sect1>
+ <sect1 id="downloading_and_running_review">
+ <title>Summarizing what we just did</title>
+ <para>In this chapter you downloaded and installed the example application and
used it to upload files into a
+ JCR repository. JBoss DNA automatically sequenced the image and/or MP3 files you
uploaded, extracted the metadata from the
+ files, and stored that metadata inside the repository. The application allowed you
to see this metadata
+ and the sequencing statistics.</para>
+ <para>This application was very simplistic. In fact, running through the
example probably only took you a minute or two.
+ So while this application won't win any awards, it does show the basics of what
JBoss DNA can do.</para>
+ <para>In the <link linkend="using_dna">next
chapter</link> we'll venture into the code to get an understanding
+ of how JBoss DNA actually works and how you can use it in your own
applications.</para>
+ </sect1>
+ </chapter>
+
+ <!--
====================================================================================================
+ Chapter
+
====================================================================================================
-->
+<chapter id="using_dna">
+ <title>Using JBoss DNA</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>
+ <note>
+ <para>Configuring JBoss DNA sequencers is a bit more manual than is ideal. As
you'll see, JBoss DNA uses dependency
+ injection to allow a great deal of flexibility in how it can be configured and
customized. However, the next release will
+ provide a much easier mechanism for configuring not only the sequencer service but
also the upcoming federation engine and
+ JCR implementation.</para>
+ </note>
+ <sect1 id="sequencing_service">
+ <title>Configuring the Sequencing Service</title>
+ <para>
+ The JBoss DNA <emphasis>sequencing service</emphasis> is the component
that manages the <emphasis>sequencers</emphasis>
+ , reacting to changes in JCR repositories and then running the appropriate
sequencers.
+ This involves processing the changes on a node, determining which (if any)
sequencers should be run on that node,
+ and for each sequencer constructing the execution environment, calling the
sequencer, and saving the information
+ generated by the sequencer.</para>
+ <para>To set up the sequencing service, an instance is created, and dependent
components are injected into
+ the object. This includes among other things:
+ <itemizedlist>
+ <listitem>
+ <para>An <emphasis>execution context</emphasis> that defines
the context in which the service runs, including
+ a factory for JCR sessions given names of the repository and workspace. This
factory must be configured,
+ and is how JBoss DNA knows about your JCR repositories and how to connect to
them. More on this a bit later.</para>
+ </listitem>
+ <listitem>
+ <para>An optional <emphasis>factory for class
loaders</emphasis> used to load sequencers. If no factory is supplied,
+ the service uses the current thread's context class loader (or if that is
null, the class loader that loaded the
+ sequencing service class).</para>
+ </listitem>
+ <listitem>
+ <para>An <code>java.util.concurrent.ExecutorService</code>
used to execute the sequencing activites. If none
+ is supplied, a new single-threaded executor is created by calling
<code>Executors.newSingleThreadExecutor()</code>.
+ (This can easily be changed by subclassing and overriding the
<code>SequencerService.createDefaultExecutorService()</code>
method.)</para>
+ </listitem>
+ <listitem>
+ <para>Filters for sequencers and events. By default, all sequencers are
considered for "node added", "property added"
+ and "property changed" events.</para>
+ </listitem>
+ </itemizedlist>
+ </para>
+ <para>As mentioned above, the <code>ExecutionContext</code>
provides access to a <code>SessionFactory</code> that is used
+ by JBoss DNA to establish sessions to your JCR repositories. Two implementations
are available:
+ <itemizedlist>
+ <listitem>
+ <para>The <code>JndiSessionFactory</code> looks up JCR
<code>Repository</code> instances in JNDI using
+ names that are supplied when creating sessions. This implementation also has
methods to set the
+ JCR <code>Credentials</code> for a given workspace
name.</para>
+ </listitem>
+ <listitem>
+ <para>The <code>SimpleSessionFactory</code> has methods to
register the JCR <code>Repository</code> instances
+ with names, as well as methods to set the JCR
<code>Credentials</code> for a given workspace name.</para>
+ </listitem>
+ </itemizedlist>
+ You can use the <code>SimpleExecutionContext</code> implementation of
<code>ExecutionContext</code> and supply
+ a <code>SessionFactory</code> instance, or you can provide your own
implementation.
+ </para>
+ <para>Here's an example of how to instantiate and configure the
SequencingService:
+ <programlisting>
+SimpleSessionFactory sessionFactory = new SimpleSessionFactory();
+sessionFactory.registerRepository("Main Repository", this.repository);
+Credentials credentials = new SimpleCredentials("jsmith",
"secret".toCharArray());
+sessionFactory.registerCredentials("Main Repository/Workspace1", credentials);
+ExecutionContext executionContext = new SimpleExecutionContext(sessionFactory);
+
+// Create the sequencing service, passing in the execution context ...
+SequencingService sequencingService = new SequencingService();
+sequencingService.setExecutionContext(executionContext);</programlisting>
+ </para>
+ <para>After the sequencing service is created and configured, it must be
started. The SequencingService
+ has an <emphasis>administration object</emphasis> (that is an instance
of <code>ServiceAdministrator</code>)
+ with <code>start()</code>, <code>pause()</code>, and
<code>shutdown()</code> methods. The latter method will
+ close the queue for sequencing, but will allow sequencing operations already
running to complete normally.
+ To wait until all sequencing operations have completed, simply call the
<code>awaitTermination</code> method
+ and pass it the maximum amount of time you want to wait.</para>
+ <para>
+ <programlisting>
+sequencingService.getAdministrator().start();</programlisting>
+ </para>
+ <para>The sequencing service must also be configured with the sequencers that
it will use. This is done using the
+ <code>addSequencer(SequencerConfig)</code> method and passing a
<code>SequencerConfig</code> instance that
+ you create. Here's an example:
+ <programlisting>
+String name = "Image Sequencer";
+String desc = "Sequences image files to extract the characteristics of the
image";
+String classname = "org.jboss.dna.sequencer.images.ImageMetadataSequencer";
+String[] classpath = null; // Use the current classpath
+String[] pathExpressions =
{"//(*.(jpg|jpeg|gif|bmp|pcx|png))[*]/jcr:content[@jcr:data] =>
/images/$1"};
+SequencerConfig imageSequencerConfig = new SequencerConfig(name, desc, classname,
classpath, pathExpressions);
+sequencingService.addSequencer(imageSequencerConfig);
+
+name = "Mp3 Sequencer";
+desc = "Sequences mp3 files to extract the id3 tags of the audio file";
+classname = "org.jboss.dna.sequencer.mp3.Mp3MetadataSequencer";
+String[] mp3PathExpressions = {"//(*.mp3)[*]/jcr:content[@jcr:data] =>
/mp3s/$1"};
+SequencerConfig mp3SequencerConfig = new SequencerConfig(name, desc, classname,
classpath, mp3PathExpressions);
+sequencingService.addSequencer(mp3SequencerConfig);</programlisting>
+ This is pretty self-explanatory, except for the <code>classpath</code>
and <code>pathExpression</code> parameters.
+ The classpath parameter defines the classpath that is passed to the class loader
factory mentioned above.
+ Our sequencer is on the classpath, so we can simply use
<code>null</code> here.
+ </para>
+ <para>The path expression is more complicated. Sequencer path expressions
are used by the sequencing service to
+ determine whether a particular changed node should be sequenced. The expressions
consist of two parts: a selection
+ criteria and an output expression. Here's a simple example:
+ <programlisting>
+/a/b/c@title => /d/e/f</programlisting>
+ Here, the <code>/a/b/c@title</code> is the selection criteria that
applies when the <code>/a/b/c</code> node has a <code>title</code>
property
+ that is added or changed. When the selection criteria matches a change event, the
sequencer will be run
+ and any generated output will be inserted into the repository described by the
output expression. In this example,
+ the generated output would be placed at the <code>/d/e/f</code> node.
+ </para>
+ <note>
+ <para>Sequencer path expressions can be fairly complex and may use
wildcards, specificy same-name sibling indexes,
+ provide optional and choice elements, and may capture parts of the selection
criteria for use in the output expression.
+ The path expression used in the image sequencer configuration example above shows
a more complex example:
+ <programlisting>
+//(*.(jpg|jpeg|gif|bmp|pcx|png))[*]/jcr:content[@jcr:data] =>
/images/$1</programlisting>
+ This uses "//" to select any node at any level in the repository whose
name ends with "." and one of the extensions (e.g., ".jpg",
".jpeg", etc.)
+ and that has a child node named "jcr:content" that has a
"jcr:data" property. It also selects the file name
+ as the first capture group (the first set of parentheses) for use in the output
expression.
+ In this example, any sequencer output is placed on a node with that same file
name under the "/images" node.
+ </para>
+ <para></para>
+ <para>Other things are possible, too. For example, the name of the
repository/workspace (as used by the <code>SessionFactory</code>)
+ may be specified at the beginning of the select criteria and/or the output
expression. This means it's possible to place
+ the sequencer output in a different repository than the node being
sequenced.</para>
+ <para>For more detail about sequencer path expressions, see the
<code>org.jboss.dna.repository.sequencer.SequencerPathExpression</code>
+ class and the corresponding
<code>org.jboss.dna.repository.sequencer.SequencerPathExpressionTest</code>
test case.</para>
+ </note>
+ <para>After the service is started, it is ready to start reacting to changes
in the repository. But it first
+ must be wired to the repositories using a listener. This is accomplished using the
<code>ObservationService</code>
+ described in the <link linkend="observation_service">next
section</link>.</para>
+ </sect1>
+ <sect1 id="observation_service">
+ <title>Configuring the Observation Service</title>
+ <para>The JBoss DNA <code>ObservationService</code> is responsible
for listening to one or more JCR repositories
+ and multiplexing the events to its listeners. Unlike JCR events, this framework
embeds in the events the
+ name of the repository and workspace that can be passed to a
<code>SessionFactory</code> to obtain a session
+ to the repository in which the change occurred. This simple design makes it very
easy for JBoss DNA to
+ concurrently work with multiple JCR repositories.</para>
+ <para>Configuring an observation service is pretty easy, especially if you
reuse the same <code>SessionFactory</code>
+ supplied to the sequencing service. Here's an example:
+ <programlisting>
+this.observationService = new ObservationService(sessionFactory);
+this.observationService.getAdministrator().start();</programlisting>
+ </para>
+ <note>
+ <para>Both <code>ObservationService</code> and
<code>SequencingService</code> implement
+ <code>AdministeredService</code>, which has a
<code>ServiceAdministrator</code> used to start, pause, and shutdown the
+ service. In other words, the lifecycle of the services are managed in the same
way.</para>
+ </note>
+ <para>
+ After the observation service is started, listeners can be added. The
<code>SequencingService</code> implements the required
+ interface, and so it may be registered directly:
+ <programlisting>
+observationService.addListener(sequencingService);</programlisting>
+ </para>
+ <para>Finally, the observation service must be wired to monitor one of your JCR
repositories. This is done with
+ one of the <code>monitor(...)</code> methods:
+ <programlisting>
+int eventTypes = Event.NODE_ADDED | Event.PROPERTY_ADDED | Event.PROPERTY_CHANGED;
+observationService.monitor("Main Repository/Workspace1",
eventTypes);</programlisting>
+ </para>
+ <para>At this point, the observation service is listening to a JCR repository
and forwarding the appropriate events
+ to the sequencing service, which will asynchronously process the changes and sequence
the information added to or changed in the repository.
+ </para>
+ </sect1>
+ <sect1 id="shutting_down">
+ <title>Shutting down JBoss DNA services</title>
+ <para>The JBoss DNA services are utilizing resources and threads that must be
released before your application is ready to shut down.
+ The safe way to do this is to simply obtain the
<code>ServiceAdministrator</code> for each service (via the
<code>getServiceAdministrator()</code> method)
+ and call <code>shutdown()</code>. As previously mentioned, the shutdown
method will simply prevent new work from being processed
+ and will not wait for existing work to be completed. If you want to wait until the
service completes all its work, you must wait
+ until the service terminates. Here's an example that shows how this is done:
+ <programlisting>
+// Shut down the service and wait until it's all shut down ...
+sequencingService.getAdministrator().shutdown();
+sequencingService.getAdministrator().awaitTermination(5, TimeUnit.SECONDS);
+
+// Shut down the observation service ...
+observationService.getAdministrator().shutdown();
+observationService.getAdministrator().awaitTermination(5,
TimeUnit.SECONDS);</programlisting>
+ </para>
+ <para>At this point, we've covered how to configure and use the JBoss DNA
services in your application.
+ The next chapter goes back to the <link
linkend="downloading_and_running">sample application</link> to show how
all these pieces fit together.</para>
+ </sect1>
+ <sect1 id="example_application_review">
+ <title>Reviewing the example application</title>
+ <para>Recall that the example application consists of a client application that
sets up an in-memory JCR repository and
+ that allows a user to upload files into that repository. The client also sets up
the DNA services with an image sequencer so
+ that if any of the uploaded files are PNG, JPEG, GIF, BMP or other images, DNA will
automatically extract the image's
+ metadata (e.g., image format, physical size, pixel density, etc.) and store that in
the repository. Or, if the client uploads
+ MP3 audio files, the title, author, album, year, and comment are extracted from the
audio file and stored in the repository.</para>
+ <para>
+ The example is comprised of 3 classes and 1 interface, located in the
+ <code>src/main/java</code>
+ directory:
+ <programlisting>
+ org/jboss/example/dna/sequencers/ConsoleInput.java
+ /MediaInfo.java
+ /SequencingClient.java
+ /UserInterface.java</programlisting>
+ </para>
+ <para>
+ <code>SequencingClient</code>
+ is the class that contains the main application.
+ <code>MediaInfo</code>
+ is a simple Java object that encapsulates metadata about a media file (as generated
by the sequencer), and used by the client to
+ pass information to the
+ <code>UserInterface</code>
+ , which is an interface with methods that will be called at runtime to request data
from the user.
+ <code>ConsoleInput</code>
+ is an implementation of this that creates a text user interface, allowing the user
to operate the client from the command-line.
+ We can easily create a graphical implementation of
+ <code>UserInterface</code>
+ at a later date. We can also create a mock implementation for testing purposes that
simulates a user entering data. This
+ allows us to check the behavior of the client automatically using conventional
JUnit test cases, as demonstrated by the
+ code in the
+ <code>src/test/java</code>
+ directory:
+ <programlisting>
+ org/jboss/example/dna/sequencers/SequencingClientTest.java
+ /MockUserInterface.java</programlisting>
+ </para>
+ <para>
+ If we look at the
+ <code>SequencingClient</code>
+ code, there are a handful of methods that encapsulate the various activities.
+ </para>
+ <note>
+ <para>To keep the code shown in this book as readable as possible, some of
the comments and error handling
+ have been removed.</para>
+ </note>
+ <para>
+ The
+ <code>startRepository()</code>
+ method starts up an in-memory Jackrabbit JCR repository. The bulk of this method is
simply gathering and passing the
+ information required by Jackrabbit. Because Jackrabbit's
+ <code>TransientRepository</code>
+ implementation shuts down after the last session is closed, the application
maintains a session to ensure that the
+ repository remains open throughout the application's lifetime. And finally, the
node type needed by the image sequencer is
+ registered with Jackrabbit.
+ </para>
+ <programlisting>
+public void startRepository() throws Exception {
+ if (this.repository == null) {
+ try {
+
+ // Load the Jackrabbit configuration ...
+ File configFile = new File(this.jackrabbitConfigPath);
+ String pathToConfig = configFile.getAbsolutePath();
+
+ // Find the directory where the Jackrabbit repository data will be stored
...
+ File workingDirectory = new File(this.workingDirectory);
+ String workingDirectoryPath = workingDirectory.getAbsolutePath();
+
+ // Get the Jackrabbit custom node definition (CND) file ...
+ URL cndFile =
Thread.currentThread().getContextClassLoader().getResource("jackrabbitNodeTypes.cnd");
+
+ // Create the Jackrabbit repository instance and establish a session to keep
the repository alive ...
+ this.repository = new TransientRepository(pathToConfig,
workingDirectoryPath);
+ if (this.username != null) {
+ Credentials credentials = new SimpleCredentials(this.username,
this.password);
+ this.keepAliveSession = this.repository.login(credentials,
this.workspaceName);
+ } else {
+ this.keepAliveSession = this.repository.login();
+ }
+
+ try {
+ // Register the node types (only valid the first time) ...
+ JackrabbitNodeTypeManager mgr =
(JackrabbitNodeTypeManager)this.keepAliveSession.getWorkspace().getNodeTypeManager();
+ mgr.registerNodeTypes(cndFile.openStream(),
JackrabbitNodeTypeManager.TEXT_X_JCR_CND);
+ } catch (RepositoryException e) {
+ if (!e.getMessage().contains("already exists")) throw e;
+ }
+
+ } catch (Exception e) {
+ this.repository = null;
+ this.keepAliveSession = null;
+ throw e;
+ }
+ }
+}</programlisting>
+ <para>As you can see, this method really has nothing to do with JBoss DNA,
other than setting up a JCR repository that JBoss
+ DNA will use.</para>
+ <para>
+ The
+ <code>shutdownRepository()</code>
+ method shuts down the Jackrabbit transient repository by closing the
"keep-alive session". Again, this method really does
+ nothing specifically with JBoss DNA, but is needed to manage the JCR repository
that JBoss DNA uses.
+ <programlisting>
+public void shutdownRepository() throws Exception {
+ if (this.repository != null) {
+ try {
+ this.keepAliveSession.logout();
+ } finally {
+ this.repository = null;
+ this.keepAliveSession = null;
+ }
+ }
+}</programlisting>
+ </para>
+ <para>
+ The
+ <code>startDnaServices()</code>
+ method first starts the JCR repository (if it was not already started), and
proceeds to create and configure the
+ <code>SequencingService</code>
+ as described
+ <link linkend="sequencing_service">earlier</link>
+ . This involes setting up the
+ <code>SessionFactory</code>
+ and
+ <code>ExecutionContext</code>
+ , creating the
+ <code>SequencingService</code>
+ instance, and configuring the image sequencer. The method then continues by setting
up the
+ <code>ObservationService</code>
+ as described
+ <link linkend="observation_service">earlier</link>
+ and starting the service.
+ <programlisting>
+public void startDnaServices() throws Exception {
+ if (this.repository == null) this.startRepository();
+ if (this.sequencingService == null) {
+
+ SimpleSessionFactory sessionFactory = new SimpleSessionFactory();
+ sessionFactory.registerRepository(this.repositoryName, this.repository);
+ if (this.username != null) {
+ Credentials credentials = new SimpleCredentials(this.username,
this.password);
+ sessionFactory.registerCredentials(this.repositoryName + "/" +
this.workspaceName, credentials);
+ }
+ this.executionContext = new SimpleExecutionContext(sessionFactory);
+
+ // Create the sequencing service, passing in the execution context ...
+ this.sequencingService = new SequencingService();
+ this.sequencingService.setExecutionContext(executionContext);
+
+ // Configure the sequencers.
+ 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 imageSequencerConfig = new SequencerConfig(name, desc, classname,
classpath, pathExpressions);
+ this.sequencingService.addSequencer(imageSequencerConfig);
+
+ // Set up the MP3 sequencer ...
+ name = "Mp3 Sequencer";
+ desc = "Sequences mp3 files to extract the id3 tags of the audio
file";
+ classname = "org.jboss.dna.sequencer.mp3.Mp3MetadataSequencer";
+ String[] mp3PathExpressions = {"//(*.mp3)[*]/jcr:content[@jcr:data] =>
/mp3s/$1"};
+ SequencerConfig mp3SequencerConfig = new SequencerConfig(name, desc, classname,
classpath, mp3PathExpressions);
+ this.sequencingService.addSequencer(mp3SequencerConfig);
+
+ // Use the DNA observation service to listen to the JCR repository (or multiple
ones), and
+ // then register the sequencing service as a listener to this observation
service...
+ this.observationService = new
ObservationService(this.executionContext.getSessionFactory());
+ this.observationService.getAdministrator().start();
+ this.observationService.addListener(this.sequencingService);
+ this.observationService.monitor(this.repositoryName + "/" +
this.workspaceName, Event.NODE_ADDED | Event.PROPERTY_ADDED | Event.PROPERTY_CHANGED);
+ }
+ // Start up the sequencing service ...
+ this.sequencingService.getAdministrator().start();
+}</programlisting>
+ </para>
+ <para>
+ The
+ <code>shutdownDnaServices()</code>
+ method is pretty straightforward: it just calls shutdown on each of the services
and waits until they terminate.
+ <programlisting>
+public void shutdownDnaServices() throws Exception {
+ if (this.sequencingService == null) return;
+
+ // Shut down the service and wait until it's all shut down ...
+ this.sequencingService.getAdministrator().shutdown();
+ this.sequencingService.getAdministrator().awaitTermination(5, TimeUnit.SECONDS);
+
+ // Shut down the observation service ...
+ this.observationService.getAdministrator().shutdown();
+ this.observationService.getAdministrator().awaitTermination(5, TimeUnit.SECONDS);
+}</programlisting>
+ </para>
+ <para>None of the other methods really do anything with JBoss DNA
<emphasis>per se</emphasis>. Instead, they merely work with the repository
+ using the JCR API.</para>
+ <para>
+ The <code>main</code> method of the
<code>SequencingClient</code> class creates a
<code>SequencingClient</code> instance,
+ and passes a new <code>ConsoleInput</code> instance:
+ <programlisting>
+public static void main( String[] args ) throws Exception {
+ SequencingClient client = new SequencingClient();
+ client.setRepositoryInformation("repo", "default",
"jsmith", "secret".toCharArray());
+ client.setUserInterface(new ConsoleInput(client));
+}</programlisting>
+ </para>
+ <para>If we look at the
+ <code>ConsoleInput</code>
+ constructor, it starts the repository, the DNA services, and a thread for the user
interface. At this point, the constructor
+ returns, but the main application continues under the user interface thread. When
the user requests to quit,
+ the user interface thread also shuts down the DNA services and JCR repository.
+ <programlisting>
+public ConsoleInput( SequencerClient client ) {
+ try {
+ client.startRepository();
+ client.startDnaServices();
+
+ System.out.println(getMenu());
+ Thread eventThread = new Thread(new Runnable() {
+ private boolean quit = false;
+ public void run() {
+ try {
+ while (!quit) {
+ // Display the prompt and process the requested operation ...
+ }
+ } finally {
+ try {
+ // Terminate ...
+ client.shutdownDnaServices();
+ client.shutdownRepository();
+ } catch (Exception err) {
+ System.out.println("Error shutting down sequencing service and
repository: " + err.getLocalizedMessage());
+ err.printStackTrace(System.err);
+ }
+ }
+ }
+ });
+ eventThread.start();
+ } catch (Exception err) {
+ System.out.println("Error: " + err.getLocalizedMessage());
+ err.printStackTrace(System.err);
+ }
+}</programlisting>
+ </para>
+ <para>At this point, we've reviewed all of the interesting code in the
example application. However, feel free
+ to play with the application, trying different things.
+ </para>
+ </sect1>
+ <sect1 id="using_dna_review">
+ <title>Summarizing what we just did</title>
+ <para>In this chapter we covered the different JBoss DNA components and how
they can be used in your application.
+ Specifically, we described how the <code>SequencingService</code> and
<code>ObservationService</code> can
+ be configured and used. And we ended the chapter by reviewing the example
application, which not only uses
+ JBoss DNA, but also the repository via the JCR API.
+ </para>
+ </sect1>
+</chapter>
+
+<!--
====================================================================================================
+ Chapter
+
====================================================================================================
-->
+<chapter id="custom_sequencers">
+ <title>Creating custom sequencers</title>
+ <para>The current release of JBoss DNA comes with two sequencers: one that
extracts metadata from a variety of image file formats,
+ and another that extracts some of the ID3 metadata from MP3 audio files. However,
it's very easy to create your own
+ sequencers and to then configure JBoss DNA to use them in your own application.
+ </para>
+ <para>
+ Creating a custom sequencer involves the following steps:
+ <itemizedlist>
+ <listitem>
+ <para>Create a Maven 2 project for your sequencer;</para>
+ </listitem>
+ <listitem>
+ <para>Implement the
<code>org.jboss.dna.graph.sequencers.StreamSequencer</code> interface with
your own implementation, and create unit tests to verify
+ the functionality and expected behavior;</para>
+ </listitem>
+ <listitem>
+ <para>Add the sequencer configuration to the JBoss DNA
<code>SequencingService</code> in your application
+ as described in the <link linkend="using_dna">previous
chapter</link>; and</para>
+ </listitem>
+ <listitem>
+ <para>Deploy the JAR file with your implementation (as well as any
dependencies), and make them available to JBoss DNA
+ in your application.</para>
+ </listitem>
+ </itemizedlist>
+ It's that simple.
+ </para>
+ <sect1 id="custom_sequencer_project">
+ <title>Creating the Maven 2 project</title>
+ <para>The first step is to create the Maven 2 project that you can use to
compile your code and build the JARs.
+ Maven 2 automates a lot of the work, and since you're already <link
linkend="downloading_and_running">set up to use Maven</link>,
+ using Maven for your project will save you a lot of time and effort. Of course, you
don't have to use Maven 2, but then you'll
+ have to get the required libraries and manage the compiling and building process
yourself.</para>
+ <note>
+ <para>JBoss DNA may provide in the future a Maven archetype for creating
sequencer projects. If you'd find this useful
+ and would like to help create it, please <link
linkend="preface">join the community</link>.</para>
+ </note>
+ <note>
+ <para>The <code>dna-sequencer-images</code> project is a small,
self-contained sequencer implementation that
+ has only the minimal dependencies. 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="http://anonsvn.jboss.org/repos/dna/trunk/sequencers/dna-sequenc...
+ </para>
+ </note>
+ <para>You can create your Maven project any way you'd like. For examples,
see the <ulink
url="http://maven.apache.org/guides/getting-started/index.html#How_d...
2 documentation</ulink>.
+ Once you've done that, just add the dependencies in your project's
<code>pom.xml</code> dependencies section:
+ <programlisting>
+<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>
+ 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:
+<programlisting>
+<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>
+ Testing JBoss DNA sequencers does not require a JCR repository or the JBoss DNA
services. (For more detail,
+ see the <link linkend="testing_custom_sequencers">testing
section</link>.) However, if you want to do
+ integration testing with a JCR repository and the JBoss DNA services, you'll need
additional dependencies for these libraries.
+<programlisting>
+<dependency>
+ <groupId>org.jboss.dna</groupId>
+ <artifactId>dna-repository</artifactId>
+ <version>0.1</version>
+ <scope>test</scope>
+</dependency>
+<!-- Java Content Repository API -->
+<dependency>
+ <groupId>javax.jcr</groupId>
+ <artifactId>jcr</artifactId>
+ <version>1.0.1</version>
+ <scope>test</scope>
+</dependency>
+<!-- Apache Jackrabbit (JCR Implementation) -->
+<dependency>
+ <groupId>org.apache.jackrabbit</groupId>
+ <artifactId>jackrabbit-api</artifactId>
+ <version>1.3.3</version>
+ <scope>test</scope>
+ <!-- Exclude these since they are included in JDK 1.5 -->
+ <exclusions>
+ <exclusion>
+ <groupId>xml-apis</groupId>
+ <artifactId>xml-apis</artifactId>
+ </exclusion>
+ <exclusion>
+ <groupId>xerces</groupId>
+ <artifactId>xercesImpl</artifactId>
+ </exclusion>
+ </exclusions>
+</dependency>
+<dependency>
+ <groupId>org.apache.jackrabbit</groupId>
+ <artifactId>jackrabbit-core</artifactId>
+ <version>1.3.3</version>
+ <scope>test</scope>
+ <!-- Exclude these since they are included in JDK 1.5 -->
+ <exclusions>
+ <exclusion>
+ <groupId>xml-apis</groupId>
+ <artifactId>xml-apis</artifactId>
+ </exclusion>
+ <exclusion>
+ <groupId>xerces</groupId>
+ <artifactId>xercesImpl</artifactId>
+ </exclusion>
+ </exclusions>
+</dependency>
+</programlisting>
+ </para>
+ <para>At this point, your project should be set up correctly, and you're
ready to move on to
+ <link linkend="custom_sequencer_implementation">writing the Java
implementation</link> for your sequencer.
+ </para>
+ </sect1>
+ <sect1 id="custom_sequencer_implementation">
+ <title>Implementing the StreamSequencer interface</title>
+ <para>After creating the project and setting up the dependencies, the next step
is to create a Java class that implements
+ the <code>org.jboss.dna.graph.sequencers.StreamSequencer</code>
interface. This interface is very straightforward
+ and involves a single method:
+ <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 progressMonitor the progress monitor that should be kept
+ * updated with the sequencer's progress and that should be
+ * frequently consulted as to whether this operation has been cancelled.
+ */
+ void sequence( InputStream stream, SequencerOutput output,
+ ProgressMonitor progressMonitor );</programlisting>
+ </para>
+ <para>The job of a stream sequencer is to process the data in the supplied
stream, and place into the <code>SequencerOutput</code>
+ any information that is to go into the JCR repository. JBoss DNA figures out when
your sequencer should be called
+ (of course, using the sequencing configuration you'll add in a bit), and then
makes sure the generated information
+ is saved in the correct place in the repository.
+ </para>
+ <para>The <code>SequencerOutput</code> class is fairly easy to use.
There are basically two methods you need to call.
+ One method sets the property values, while the other sets references to other nodes
in the repository. Use these
+ methods to describe the properties of the nodes you want to create, using relative
paths for the nodes and
+ valid JCR property names for properties and references. JBoss DNA will ensure that
nodes are created or updated
+ whenever they're needed.
+ <programlisting>
+public interface SequencerOutput {
+
+ /**
+ * Set the supplied property on the supplied node. The allowable
+ * values are any of the following:
+ * - primitives (which will be autoboxed)
+ * - String instances
+ * - String arrays
+ * - byte arrays
+ * - InputStream instances
+ * - Calendar instances
+ *
+ * @param nodePath the path to the node containing the property;
+ * may not be null
+ * @param property the name of the property to be set
+ * @param values the value(s) for the property; may be empty if
+ * any existing property is to be removed
+ */
+ void setProperty( String nodePath, String property,
+ Object... values );
+
+ /**
+ * Set the supplied reference on the supplied node.
+ *
+ * @param nodePath the path to the node containing the property;
+ * may not be null
+ * @param property the name of the property to be set
+ * @param paths the paths to the referenced property, which may be
+ * absolute paths or relative to the sequencer output node;
+ * may be empty if any existing property is to be removed
+ */
+ void setReference( String nodePath, String property,
+ String... paths );
+}</programlisting>
+ </para>
+ <para>JBoss DNA will create nodes of type
<code>nt:unstructured</code> unless you specify the value for the
+ <code>jcr:primaryType</code> property. You can also specify the values
for the <code>jcr:mixinTypes</code> property
+ if you want to add mixins to any node.
+ </para>
+ <para>For a complete example of a sequencer, let's look at the
<code>org.jboss.dna.sequencers.image.ImageMetadataSequencer</code>
implementation:
+ <programlisting>
+public class ImageMetadataSequencer implements StreamSequencer {
+
+ public static final String METADATA_NODE = "image:metadata";
+ public static final String IMAGE_PRIMARY_TYPE = "jcr:primaryType";
+ public static final String IMAGE_MIXINS = "jcr:mixinTypes";
+ public static final String IMAGE_MIME_TYPE = "jcr:mimeType";
+ public static final String IMAGE_ENCODING = "jcr:encoding";
+ public static final String IMAGE_FORMAT_NAME = "image:formatName";
+ public static final String IMAGE_WIDTH = "image:width";
+ public static final String IMAGE_HEIGHT = "image:height";
+ public static final String IMAGE_BITS_PER_PIXEL = "image:bitsPerPixel";
+ public static final String IMAGE_PROGRESSIVE = "image:progressive";
+ public static final String IMAGE_NUMBER_OF_IMAGES =
"image:numberOfImages";
+ public static final String IMAGE_PHYSICAL_WIDTH_DPI =
"image:physicalWidthDpi";
+ public static final String IMAGE_PHYSICAL_HEIGHT_DPI =
"image:physicalHeightDpi";
+ public static final String IMAGE_PHYSICAL_WIDTH_INCHES =
"image:physicalWidthInches";
+ public static final String IMAGE_PHYSICAL_HEIGHT_INCHES =
"image:physicalHeightInches";
+
+ /**
+ * {@inheritDoc}
+ */
+ public void sequence( InputStream stream, SequencerOutput output,
+ ProgressMonitor progressMonitor ) {
+ progressMonitor.beginTask(10, ImageSequencerI18n.sequencerTaskName);
+
+ 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;
+ }
+ progressMonitor.worked(5);
+ if (progressMonitor.isCancelled()) return;
+
+ // Generate the output graph if we found useful metadata ...
+ if (metadata != null) {
+ // Place the image metadata into the output map ...
+ output.setProperty(METADATA_NODE, IMAGE_PRIMARY_TYPE,
"image:metadata");
+ // output.psetProperty(METADATA_NODE, IMAGE_MIXINS, "");
+ output.setProperty(METADATA_NODE, IMAGE_MIME_TYPE, metadata.getMimeType());
+ // output.setProperty(METADATA_NODE, IMAGE_ENCODING, "");
+ output.setProperty(METADATA_NODE, IMAGE_FORMAT_NAME,
metadata.getFormatName());
+ output.setProperty(METADATA_NODE, IMAGE_WIDTH, metadata.getWidth());
+ output.setProperty(METADATA_NODE, IMAGE_HEIGHT, metadata.getHeight());
+ output.setProperty(METADATA_NODE, IMAGE_BITS_PER_PIXEL,
metadata.getBitsPerPixel());
+ output.setProperty(METADATA_NODE, IMAGE_PROGRESSIVE,
metadata.isProgressive());
+ output.setProperty(METADATA_NODE, IMAGE_NUMBER_OF_IMAGES,
metadata.getNumberOfImages());
+ output.setProperty(METADATA_NODE, IMAGE_PHYSICAL_WIDTH_DPI,
metadata.getPhysicalWidthDpi());
+ output.setProperty(METADATA_NODE, IMAGE_PHYSICAL_HEIGHT_DPI,
metadata.getPhysicalHeightDpi());
+ output.setProperty(METADATA_NODE, IMAGE_PHYSICAL_WIDTH_INCHES,
metadata.getPhysicalWidthInch());
+ output.setProperty(METADATA_NODE, IMAGE_PHYSICAL_HEIGHT_INCHES,
metadata.getPhysicalHeightInch());
+ }
+
+ progressMonitor.done();
+ }
+}</programlisting>
+ </para>
+ <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>
+ <para>Also note how the progress monitor is used. Reporting progress through
the supplied <code>ProgressMonitor</code> is very easy, and it ensures that
JBoss DNA
+ can accurately monitor and report the status of sequencing activities to the users.
At the beginning of the operation, call
+ <code>beginTask(...)</code> with a meaningful message describing
+ the operation and a total for the amount of work that will be done by this
sequencer. Then perform the sequencing work,
+ periodically reporting work by specifying the incremental amount of work with the
<code>worked(double)</code> method, or
+ by creating a subtask with the <code>createSubtask(double)</code> method
and reporting work against that subtask
+ monitor.
+ </para>
+ <para>Your method should periodically use the ProgressMonitor's
<code>isCancelled()</code> method to check whether the operation has been
+ cancelled.. If this method returns true, the implementation should abort all work
as
+ soon as possible and close any resources that were acquired or opened.
+ </para>
+ <para>
+ Finally, when your sequencing operation is completed, it should call
<code>done()</code> on the progress monitor.
+ </para>
+ </sect1>
+ <sect1 id="testing_custom_sequencers">
+ <title>Testing custom sequencers</title>
+ <para>The sequencing framework was designed to make testing sequencers much
easier. In particular, the
+ <code>StreamSequencer</code> interface does not make use of the JCR API.
So instead of requiring a fully-configured
+ JCR repository and JBoss DNA system, unit tests for a sequencer can focus on testing
that the content is
+ processed correctly and the desired output graph is generated.</para>
+ <note>
+ <para>For a complete example of a sequencer unit test, see the
<code>ImageMetadataSequencerTest</code> unit test
+ in the <code>org.jboss.dna.sequencer.images</code> package of the
<code>dna-sequencers-image</code> project.
+ </para>
+ </note>
+ <para>The following code fragment shows one way of testing a sequencer, using
JUnit 4.4 assertions and
+ some of the classes made available by JBoss DNA. Of course,
+ this example code does not do any error handling and does not make all the
assertions a real test would.
+ <programlisting>
+Sequencer sequencer = new ImageMetadataSequencer();
+MockSequencerOutput output = new MockSequencerOutput();
+ProgressMonitor progress = new SimpleProgressMonitor("Test activity");
+InputStream stream = null;
+try {
+ stream =
this.getClass().getClassLoader().getResource("caution.gif").openStream();
+ sequencer.sequence(stream,output,progress); // 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>
+ <para>It's also useful to test that a sequencer produces no output for
something it should not understand:
+ <programlisting>
+Sequencer sequencer = new ImageMetadataSequencer();
+MockSequencerOutput output = new MockSequencerOutput();
+ProgressMonitor progress = new SimpleProgressMonitor("Test activity");
+InputStream stream = null;
+try {
+ stream =
this.getClass().getClassLoader().getResource("caution.pict").openStream();
+ sequencer.sequence(stream,output,progress); // writes to 'output'
+ assertThat(output.hasProperties(), is(false));
+ assertThat(output.hasReferences(), is(false));
+} finally {
+ stream.close();
+}</programlisting>
+ </para>
+ <para>These are just two simple tests that show ways of testing a sequencer.
Some tests may get quite involved,
+ especially if a lot of output data is produced.
+ </para>
+ <para>It may also be useful to create some integration tests
+ that <link linkend="using_dna">configure JBoss DNA</link> to
use a custom sequencer, and to then upload
+ content using the JCR API, verifying that the custom sequencer did run. However,
remember that JBoss DNA
+ runs sequencers asynchronously in the background, and you must sychronize your tests
to ensure that the
+ sequencers have a chance to run before checking the results. (One way of doing this
(although, granted, not always reliable) is to wait for a second
+ after uploading your content, shutdown the <code>SequencingService</code>
and await its termination,
+ and then check that the sequencer output has been saved to the JCR repository. For
an example of this technique,
+ see the <code>SequencingClientTest</code> unit test in the example
application.)
+ </para>
+ </sect1>
+ <sect1 id="deploying_custom_sequencers">
+ <title>Deploying custom sequencers</title>
+ <para>The first step of deploying a sequencer consists of adding/changing the
sequencer configuration (e.g., <code>SequencerConfig</code>)
+ in the <code>SequencingService</code>. This was covered in the <link
linkend="sequencing_service">previous chapter</link>.
+ </para>
+ <para>
+ The second step is to make the sequencer implementation available to JBoss DNA. At
this time, the JAR containing
+ your new sequencer, as well as any JARs that your sequencer depends on, should be
placed on your application classpath.</para>
+ <note>
+ <para>A future goal of JBoss DNA is to allow sequencers, connectors, and
other extensions to be easily deployed into
+ a runtime repository. This process will not only be much simpler, but it will
also provide JBoss DNA
+ with the information necessary to update configurations and create the
appropriate class loaders for each extension.
+ Having separate class loaders for each extension helps prevent the pollution of
the common classpath,
+ facilitates an isolated runtime environment to eliminate any dependency
conflicts, and may potentially
+ enable hot redeployment of newer extension versions.
+ </para>
+ </note>
+ </sect1>
+</chapter>
+
+<!--
====================================================================================================
+ Chapter
+
====================================================================================================
-->
+<chapter id="future_directions">
+ <title>Looking to the future</title>
+ <para>What's next for JBoss DNA? Well, the sequencing system is just the
beginning. With this release, the sequencing system
+ is stable enough so that more <link
linkend="sequencers">sequencers</link> can be developed and used within
your own applications.
+ If you're interested in getting involved with the JBoss DNA project, consider
picking up one of the sequencers on our
+ <ulink
url="http://jira.jboss.org/jira/browse/DNA?report=com.atlassian.jira...;.
+ Or, check out <ulink
url="http://jira.jboss.org/jira/secure/IssueNavigator.jspa?reset=tru...
+ for the list of sequencers we've thought of. If you think of one that's not
there, please add it to JIRA!
+ </para>
+ <para>
+ The next release will focus on creating the <link
linkend="federation">federation engine</link> and connectors
+ for several popular and ubiquitous systems. The 0.2 release will likely only federate
information in a read-only manner,
+ but updates will soon follow. Also, during the early part of the next release, the
JBoss DNA project will switch to use JDK 6.
+ Java 5 is being end-of-lifed, so we want to move to a supported JDK. However, a number
of JBoss projects and products continue to
+ require Java 5, so our next release will most likely use JDK 6 with Java 5
compatibility.</para>
+ <para>
+ Other components on our roadmap include a web user interface, a REST-ful server, and a
view system that allows domain-specific
+ views of information in the repository. These components are farther out on our
roadmap, and at this time have not been
+ targeted to a particular release. If any of these are of interest to you, please
<link linkend="preface">get involved</link> in the community.
+ </para>
+</chapter>
+</book>
\ No newline at end of file
Added: trunk/extensions/dna-sequencer-xml/src/test/resources/plugin.xml
===================================================================
--- trunk/extensions/dna-sequencer-xml/src/test/resources/plugin.xml
(rev 0)
+++ trunk/extensions/dna-sequencer-xml/src/test/resources/plugin.xml 2008-10-29 18:23:01
UTC (rev 598)
@@ -0,0 +1,91 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<?eclipse version="3.0"?>
+<!-- (c) 2007 Varsity Gateway LLC. All rights reserved. -->
+<plugin
+ id="com.metamatrix.metamodels.transformation"
+ name="%pluginName"
+ version="5.5.1"
+ provider-name="%providerName"
+ class="com.metamatrix.metamodels.transformation.TransformationPlugin">
+
+ <runtime>
+ <library name="metamodelsTransformation.jar">
+ <export name="*"/>
+ </library>
+ </runtime>
+ <requires>
+ <import plugin="org.eclipse.emf.mapping" export="true"/>
+ <import plugin="org.eclipse.emf.ecore" export="true"/>
+ <import plugin="org.eclipse.emf.edit" export="true"/>
+ <import plugin="org.eclipse.emf.ecore.edit"
export="true"/>
+ <import plugin="org.eclipse.core.runtime"
export="true"/>
+ <import plugin="com.metamatrix.core" export="true"/>
+ <import plugin="com.metamatrix.metamodels.core"
export="true"/>
+ </requires>
+
+
+<!--
+ Each extension below represents a single metamodel. Each metmodel is
+ registered using under the specified <uri> value which must be the same
+ eNS_URI value defined in the EPackage class for that metamodel.
+ -->
+ <extension
+ id="transformation"
+ name="%metamodelName"
+ point="com.metamatrix.modeler.core.metamodel">
+ <uri>
+
http://www.metamatrix.com/metamodels/Transformation
+ </uri>
+ <alternateUri>
+ mtkplugin:///com.metamatrix.metamodels.Transformation
+ </alternateUri>
+ <packageClass
+
name="com.metamatrix.metamodels.transformation.TransformationPackage">
+ </packageClass>
+ <adapterClass
+
name="com.metamatrix.metamodels.transformation.provider.TransformationItemProviderAdapterFactory">
+ </adapterClass>
+ <properties
+ createAsPhysical="false"
+ requiresProxies="false"
+ participatoryOnly="true"
+ createAsVirtual="false"
+ supportsDiagrams="false"
+ supportsExtension="true">
+ </properties>
+ </extension>
+ <extension
+ point="org.eclipse.emf.ecore.generated_package">
+ <package
+
uri="http://www.metamatrix.com/metamodels/Transformation"
+
class="com.metamatrix.metamodels.transformation.TransformationPackage">
+ </package>
+ </extension>
+
+ <extension
+ id="mapping"
+ name="%mappingMetamodelName"
+ point="com.metamatrix.modeler.core.metamodel">
+ <uri>
+
http://www.eclipse.org/emf/2002/Mapping
+ </uri>
+ <alternateUri>
+
mtkplugin:///www.eclipse.org/emf/2002/Mapping
+ </alternateUri>
+ <packageClass
+ name="org.eclipse.emf.mapping.MappingPackage">
+ </packageClass>
+ <adapterClass
+
name="org.eclipse.emf.mapping.provider.MappingItemProviderAdapterFactory">
+ </adapterClass>
+ <properties
+ createAsPhysical="false"
+ requiresProxies="false"
+ participatoryOnly="true"
+ createAsVirtual="false"
+ supportsDiagrams="false"
+ supportsExtension="false">
+ </properties>
+ </extension>
+
+</plugin>
Modified: trunk/pom.xml
===================================================================
--- trunk/pom.xml 2008-10-29 17:48:23 UTC (rev 597)
+++ trunk/pom.xml 2008-10-29 18:23:01 UTC (rev 598)
@@ -123,6 +123,7 @@
<module>extensions/dna-sequencer-cnd</module>
<module>extensions/dna-sequencer-java</module>
<module>extensions/dna-sequencer-msoffice</module>
+ <module>extensions/dna-sequencer-xml</module>
<module>extensions/dna-sequencer-zip</module>
<module>extensions/dna-connector-federation</module>
<module>extensions/dna-connector-inmemory</module>