Author: rhauch
Date: 2009-01-07 15:00:11 -0500 (Wed, 07 Jan 2009)
New Revision: 695
Added:
trunk/dna-graph/src/main/java/org/jboss/dna/graph/cache/package-info.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/connectors/package-info.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/mimetype/package-info.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/package-info.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/properties/basic/package-info.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/properties/package-info.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/requests/package-info.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/requests/processor/package-info.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/sequencers/package-info.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/xml/package-info.java
trunk/dna-graph/src/test/java/org/jboss/dna/graph/connectors/test/
trunk/dna-graph/src/test/java/org/jboss/dna/graph/connectors/test/AbstractConnectorTest.java
trunk/dna-graph/src/test/java/org/jboss/dna/graph/connectors/test/ReadableConnectorTest.java
trunk/dna-graph/src/test/java/org/jboss/dna/graph/connectors/test/WritableConnectorTest.java
trunk/dna-graph/src/test/java/org/jboss/dna/graph/connectors/test/package-info.java
trunk/dna-graph/src/test/resources/LoremIpsum1.txt
trunk/dna-graph/src/test/resources/LoremIpsum2.txt
trunk/dna-graph/src/test/resources/LoremIpsum3.txt
trunk/extensions/dna-connector-store-jpa/src/test/java/org/jboss/dna/connector/store/jpa/JpaConnectorReadingTest.java
trunk/extensions/dna-connector-store-jpa/src/test/java/org/jboss/dna/connector/store/jpa/JpaConnectorWritingTest.java
Removed:
trunk/extensions/dna-connector-store-jpa/src/test/java/org/jboss/dna/connector/store/jpa/JpaConnectionTest.java
trunk/extensions/dna-connector-store-jpa/src/test/java/org/jboss/dna/connector/store/jpa/JpaConnectionUsingHsqldbTest.java
trunk/extensions/dna-connector-store-jpa/src/test/resources/LoremIpsum1.txt
trunk/extensions/dna-connector-store-jpa/src/test/resources/LoremIpsum2.txt
trunk/extensions/dna-connector-store-jpa/src/test/resources/LoremIpsum3.txt
Modified:
trunk/dna-graph/src/main/java/org/jboss/dna/graph/Location.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/connectors/RepositoryConnection.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/connectors/RepositoryConnectionFactory.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/connectors/RepositorySource.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/connectors/RepositorySourceCapabilities.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/connectors/RepositorySourceListener.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/properties/PathFactory.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/requests/processor/RequestProcessor.java
trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/models/basic/BasicRequestProcessor.java
trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/models/basic/LargeValueEntity.java
Log:
DNA-48 Development and testing kit for federation connectors
Added an initial set of JUnit test case classes under
"org.jboss.dna.graph.connectors.test" (in the test source) that provide a
foundation for writing simple unit test classes for a connector, since almost all of the
test logic can be inherited. Right now the tests include the WritableConnectorTest and
ReadableConnectorTest abstract classes. To use these to test a particular connector, the
connector developer would create subclass these abstract classes, implementing the two
abstract methods (one sets up the RepositorySource instance, and the other initializes the
repository content to prepare for the tests). All test methods are inherited from the
abstract classes, so with minimal effort a connector developer can easily provide a series
of (standard) benchmark functionality tests to ensure the connector is behaving correctly.
It is likely that the connector developer will want to create additional tests. There is
a common abstract base class for the WritableConnectorTest and ReadableConnectorTest
classes, and this common base class performs all the set up and initialization of the
repository source. So by extending this class, a custom connector test case class can
test the repository using any methods.
As part of this effort, I also added "package-info.java" files for each package
in the "org.jboss.dna.graph" project. These package-level JavaDocs should help
developers understand how to use the JBoss DNA Graph API. including how to write and test
a custom connector.
Finally, I changed the test cases in the JPA connector to use this test harness.
Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/Location.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/Location.java 2008-12-19 19:49:01
UTC (rev 694)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/Location.java 2009-01-07 20:00:11
UTC (rev 695)
@@ -335,6 +335,11 @@
return null;
}
+ /**
+ * Get the first UUID that is in one of the {@link #getIdProperties() identification
properties}.
+ *
+ * @return the UUID for this location, or null if there is no such identification
property
+ */
public UUID getUuid() {
Property property = getIdProperty(DnaLexicon.UUID);
if (property != null && !property.isEmpty()) {
Added: trunk/dna-graph/src/main/java/org/jboss/dna/graph/cache/package-info.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/cache/package-info.java
(rev 0)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/cache/package-info.java 2009-01-07
20:00:11 UTC (rev 695)
@@ -0,0 +1,8 @@
+/**
+ * Caching of graph content is controlled with {@link CachePolicy cache policies}, and
these are used
+ * in {@link org.jboss.dna.graph.connectors connectors} and in {@link
org.jboss.dna.graph.requests.CacheableRequest requests}
+ * (among other places).
+ */
+
+package org.jboss.dna.graph.cache;
+
Property changes on:
trunk/dna-graph/src/main/java/org/jboss/dna/graph/cache/package-info.java
___________________________________________________________________
Name: svn:mime-type
+ text/plain
Modified:
trunk/dna-graph/src/main/java/org/jboss/dna/graph/connectors/RepositoryConnection.java
===================================================================
---
trunk/dna-graph/src/main/java/org/jboss/dna/graph/connectors/RepositoryConnection.java 2008-12-19
19:49:01 UTC (rev 694)
+++
trunk/dna-graph/src/main/java/org/jboss/dna/graph/connectors/RepositoryConnection.java 2009-01-07
20:00:11 UTC (rev 695)
@@ -28,14 +28,92 @@
import org.jboss.dna.graph.cache.CachePolicy;
import org.jboss.dna.graph.properties.PathNotFoundException;
import org.jboss.dna.graph.properties.ReferentialIntegrityException;
+import org.jboss.dna.graph.requests.CompositeRequest;
import org.jboss.dna.graph.requests.Request;
+import org.jboss.dna.graph.requests.processor.RequestProcessor;
/**
* A connection to a repository source.
* <p>
* These connections need not support concurrent operations by multiple threads.
* </p>
+ * <h3>Implementing a connector</h3>
+ * <p>
+ * While most of these methods are straightforward, a few warrant additional information.
The {@link #ping(long, TimeUnit)} method
+ * allows DNA to check the connection to see if it is alive. This method can be used in a
variety of situations, ranging from
+ * verifying that a {@link RepositorySource}'s JavaBean properties are correct to
ensuring that a connection is still alive before
+ * returning the connection from a connection pool.
+ * </p>
+ * <p>
+ * DNA hasn't yet defined the event mechanism, so connectors don't have any
methods to invoke on the
+ * {@link RepositorySourceListener}. This will be defined in the next release, so feel
free to manage the listeners now. Note that
+ * by default the {@link RepositorySourceCapabilities} returns false for
supportsEvents().
+ * </p>
+ * <p>
+ * The most important method on this interface, though, is the {@link
#execute(ExecutionContext, Request)} method, which serves as
+ * the mechanism by which the component using the connector access and manipulates the
content exposed by the connector. The first
+ * parameter to this method is the {@link ExecutionContext}, which contains the
information about environment as well as the
+ * subject performing the request.
+ * </p>
+ * <p>
+ * The second parameter, however, represents a request that is to be processed by the
connector. {@link Request} objects can take
+ * many different forms, as there are different classes for each kind of request (see the
{org.jboss.dna.graph.request} package
+ * for more detail). Each request contains the information a connector needs to do the
processing, and it also is the place where
+ * the connector places the results (or the error, if one occurs).
+ * </p>
+ * <p>
+ * Although there are over a dozen different kinds of requests, we do anticipate adding
more in future releases. For example, DNA
+ * will likely support searching repository content in sources through an additional
subclass of {@link Request}. Getting the
+ * version history for a node will likely be another kind of request added in an upcoming
release.
+ * </p>
+ * <p>
+ * A connector is technically free to implement the {@link #execute(ExecutionContext,
Request)} method in any way, as long as the
+ * semantics are maintained. But DNA provides a {@link RequestProcessor} class that can
simplify writing your own connector and at
+ * the same time help insulate your connector from new kinds of requests that may be
added in the future. The
+ * {@link RequestProcessor} is an abstract class that defines a
<code>process(...)</code> method for each concrete {@link Request}
+ * subclass. In other words, there is a {@link
RequestProcessor#process(org.jboss.dna.graph.requests.CompositeRequest)} method, a
+ * {@link RequestProcessor#process(org.jboss.dna.graph.requests.ReadNodeRequest)} method,
and so on.
+ * </p>
+ * <p>
+ * To use a request processor in your connector, simply subclass {@link RequestProcessor}
and override all of the abstract methods
+ * and optionally override any of the other methods that have a default implementation.
In many cases, the default implementations
+ * of the <code>process(...)</code> methods are <i>sufficient</i>
but probably not <i>efficient or optimum.</i> If that is the
+ * case, simply provide your own methods that perform the request in a manner that is
efficient for your source. However, if
+ * performance is not a big issue, all of the concrete methods will provide the correct
behavior. And remember, you can always
+ * provide better implementations later, so it's often best to keep things simple at
first.
+ * </p>
+ * <p>
+ * Then, in your connector's {@link #execute(ExecutionContext, Request)} method,
instantiate your {@link RequestProcessor}
+ * subclass and pass the {@link #execute(ExecutionContext, Request) execute(...)}
method's Request parameter directly into the the
+ * request processor's {@link RequestProcessor#process(Request)} method, which will
determine the appropriate method given the
+ * actual Request object and will then invoke that method. For example:
*
+ * <pre>
+ * public void execute( ExecutionContext context,
+ * Request request ) throws RepositorySourceException {
+ * RequestProcessor processor = new RequestProcessor(context);
+ * try {
+ * proc.process(request);
+ * } finally {
+ * proc.close();
+ * }
+ * }
+ * </pre>
+ *
+ * If you do this, the bulk of your connector implementation will be in the
RequestProcessor implementation methods. This not only
+ * is more maintainable, it also lends itself to easier testing. And should any new
request types be added in the future, your
+ * connector may work just fine without any changes. In fact, if the {@link
RequestProcessor} class can implement meaningful
+ * methods for those new request types, your connector may "just work". Or, at
least your connector will still be binary
+ * compatible, even if your connector won't support any of the new features.
+ * </p>
+ * <p>
+ * Finally, how should the connector handle exceptions? As mentioned above, each {@link
Request} object has a
+ * {@link Request#setError(Throwable) slot} where the connector can set any exception
encountered during processing. This not only
+ * handles the exception, but in the case of a {@link CompositeRequest} it also correctly
associates the problem with the request.
+ * However, it is perfectly acceptable to throw an exception if the connection becomes
invalid (e.g., there is a communication
+ * failure) or if a fatal error would prevent subsequent requests from being processed.
+ * </p>
+ *
* @author Randall Hauch
*/
@NotThreadSafe
Modified:
trunk/dna-graph/src/main/java/org/jboss/dna/graph/connectors/RepositoryConnectionFactory.java
===================================================================
---
trunk/dna-graph/src/main/java/org/jboss/dna/graph/connectors/RepositoryConnectionFactory.java 2008-12-19
19:49:01 UTC (rev 694)
+++
trunk/dna-graph/src/main/java/org/jboss/dna/graph/connectors/RepositoryConnectionFactory.java 2009-01-07
20:00:11 UTC (rev 695)
@@ -23,7 +23,8 @@
/**
* An interface for a factory of {@link RepositoryConnection}s using the names of the
{@link RepositorySource} from which the
- * connections are to be obtained.
+ * connections are to be obtained. This interface need not be implemented by a {@link
RepositorySource}, as implementations are
+ * usually provided by the system.
*
* @author Randall Hauch
*/
Modified:
trunk/dna-graph/src/main/java/org/jboss/dna/graph/connectors/RepositorySource.java
===================================================================
---
trunk/dna-graph/src/main/java/org/jboss/dna/graph/connectors/RepositorySource.java 2008-12-19
19:49:01 UTC (rev 694)
+++
trunk/dna-graph/src/main/java/org/jboss/dna/graph/connectors/RepositorySource.java 2009-01-07
20:00:11 UTC (rev 695)
@@ -23,6 +23,8 @@
import java.io.Serializable;
import javax.naming.Referenceable;
+import org.jboss.dna.graph.ExecutionContext;
+import org.jboss.dna.graph.requests.CacheableRequest;
/**
* A repository source is a description of a resource that can be used to access or store
repository information. This class
@@ -42,9 +44,80 @@
* Naming and Directory Interface<sup><font
size=-3>TM</font></sup> (JNDI). This interface extends both {@link
Referenceable} and
* {@link Serializable} so that such objects can be stored in any JNDI naming context and
enable proper system recovery,
* </p>
+ * <h3>Pooling connections</h3>
* <p>
- * If the connections to a source are to be pooled, see {@link
RepositoryConnectionPool}.
+ * If the connections created by a RepositorySource are expensive to create, then
connection pooling is recommended. DNA provides
+ * this capability with a powerful and flexible {@link RepositoryConnectionPool} class.
This is the pooling mechanism used by
+ * JBoss DNA, but you are free to use your own pools.
* </p>
+ * <h3>Cache Policy</h3>
+ * <p>
+ * Each connector is responsible for determining whether and how long DNA is to cache the
content made available by the connector.
+ * This is referred to as the caching policy, and consists of a time to live value
representing the number of milliseconds that a
+ * piece of data may be cached. After the TTL has passed, the information is no longer
used.
+ * </p>
+ * <p>
+ * DNA allows a connector to use a flexible and powerful caching policy. First, each
connection returns the default caching policy
+ * for all information returned by that connection. Often this policy can be configured
via properties on the
+ * {@link RepositorySource} implementation. This is optional, meaning the connector can
return null if it does not wish to have a
+ * default caching policy.
+ * </p>
+ * <p>
+ * Second, the connector is able to override its default caching policy on {@link
CacheableRequest individual requests}. Again,
+ * this is optional, meaning that a null caching policy on a request implies that the
request has no overridden caching policy.
+ * </p>
+ * <p>
+ * Third, if the connector has no default caching policy and none is set on the
individual requests, DNA uses whatever caching
+ * policy is set up for that component using the connector. For example, the federating
connector allows a default caching policy
+ * to be specified, and this policy is used should the sources being federated not define
their own caching policy.
+ * </p>
+ * <p>
+ * In summary, a connector has total control over whether and for how long the
information it provides is cached.
+ * </p>
+ * <h3>Leveraging JNDI</h3>
+ * <p>
+ * Sometimes it is necessary (or easier) for a RepositorySource implementation to look up
an object in JNDI. One example of this
+ * is the JBoss Cache connector: while the connector can instantiate a new JBoss Cache
instance, more interesting use cases
+ * involve JBoss Cache instances that are set up for clustering and replication,
something that is generally difficult to
+ * configure in a single JavaBean. Therefore the JBossCacheSource has optional JavaBean
properties that define how it is to look
+ * up a JBoss Cache instance in JNDI.
+ * </p>
+ * <p>
+ * This is a simple pattern that you may find useful in your connector. Basically, if
your source implementation can look up an
+ * object in JNDI, simply use a single JavaBean String property that defines the full
name that should be used to locate that
+ * object in JNDI. Usually it's best to include "Jndi" in the JavaBean
property name so that administrative users understand the
+ * purpose of the property. (And some may suggest that any optional property also use the
word "optional" in the property name.)
+ * </p>
+ * <h3>Capabilities</h3>
+ * <p>
+ * Each RepositorySource implementation provides some hint as to its capabilities by
returning a
+ * {@link RepositorySourceCapabilities} object. This class currently provides methods
that say whether the connector supports
+ * updates, whether it supports same-name-siblings (SNS), and whether the connector
supports listeners and events. These may be
+ * hard-coded values, or the capabilities object {@link #getCapabilities() returned by
the connector} may determine them at
+ * runtime based upon the system its connecting to. For example, a connector may
interrogate the underlying system to decide
+ * whether it can support updates. The only criteria is that the capabilities must remain
constant throughout the lifetime of the
+ * RepositorySource instance (assuming it doesn't change).
+ * </p>
+ * <p>
+ * The {@link RepositorySourceCapabilities} can be used as is (the class is immutable),
or it can be subclassed to provide more
+ * complex behavior. Why is this a concrete class and not an interface? By using a
concrete class, connectors inherit the default
+ * behavior. If additional capabilities need to be added to the class in future releases,
connectors may not have to override the
+ * defaults. This provides some insulation against future enhancements to the connector
framework.
+ * </p>
+ * <h3>Security and authentication</h3>
+ * <p>
+ * The main method connectors have to process requests takes an {@link ExecutionContext},
which contains the JAAS security
+ * information of the subject performing the request. This means that the connector can
use this to determine authentication and
+ * authorization information for each request.
+ * </p>
+ * <p>
+ * Sometimes that is not sufficient. For example, it may be that the connector needs its
own authorization information so that it
+ * can establish a connection (even if user-level privileges still use the {@link
ExecutionContext} provided with each request).
+ * In this case, the RepositorySource implementation will probably need JavaBean
properties that represent the connector's
+ * authentication information. This may take the form of a username and password, or it
may be properties that are used to
+ * delegate authentication to JAAS. Either way, just realize that it's perfectly
acceptable for the connector to require its own
+ * security properties.
+ * </p>
*
* @author Randall Hauch
*/
Modified:
trunk/dna-graph/src/main/java/org/jboss/dna/graph/connectors/RepositorySourceCapabilities.java
===================================================================
---
trunk/dna-graph/src/main/java/org/jboss/dna/graph/connectors/RepositorySourceCapabilities.java 2008-12-19
19:49:01 UTC (rev 694)
+++
trunk/dna-graph/src/main/java/org/jboss/dna/graph/connectors/RepositorySourceCapabilities.java 2009-01-07
20:00:11 UTC (rev 695)
@@ -24,7 +24,8 @@
import net.jcip.annotations.Immutable;
/**
- * The capabilities of a {@link RepositorySource}. This class can be used as is, or
subclassed to define more complex behavior.
+ * The capabilities of a {@link RepositorySource}. This class can be used as is, or
subclassed by a connector to define more
+ * complex behavior.
*
* @see RepositorySource#getCapabilities()
* @author Randall Hauch
Modified:
trunk/dna-graph/src/main/java/org/jboss/dna/graph/connectors/RepositorySourceListener.java
===================================================================
---
trunk/dna-graph/src/main/java/org/jboss/dna/graph/connectors/RepositorySourceListener.java 2008-12-19
19:49:01 UTC (rev 694)
+++
trunk/dna-graph/src/main/java/org/jboss/dna/graph/connectors/RepositorySourceListener.java 2009-01-07
20:00:11 UTC (rev 695)
@@ -22,9 +22,12 @@
package org.jboss.dna.graph.connectors;
/**
+ * Interface for a component that wants to listen to changes occurring in a {@link
RepositorySource}.
+ *
* @author Randall Hauch
*/
public interface RepositorySourceListener {
- void notify( String sourceName, Object... events );
+ void notify( String sourceName,
+ Object... events );
}
Added: trunk/dna-graph/src/main/java/org/jboss/dna/graph/connectors/package-info.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/connectors/package-info.java
(rev 0)
+++
trunk/dna-graph/src/main/java/org/jboss/dna/graph/connectors/package-info.java 2009-01-07
20:00:11 UTC (rev 695)
@@ -0,0 +1,100 @@
+/**
+ * JBoss DNA uses <i>connectors</i> to access information from external
systems (such as databases,
+ * other repositories, services, applications, etc.) and create graph representations of
that information.
+ * This package defines the interfaces that a connector must implement.
+ *
+ * <h3>Concepts</h3>
+ * <p>
+ * A <strong>connector</strong> is the runnable code packaged in one or more
JAR files that contains implementations
+ * of several interfaces (described below). A Java developer writes a connector to a type
of source,
+ * such as a particular database management system, LDAP directory, source code
management system, etc.
+ * It is then packaged into one or more JAR files (including dependent JARs) and deployed
for use in
+ * applications that use JBoss DNA repositories.
+ * </p>
+ * <p>
+ * The description of a particular source system (e.g., the "Customer"
database, or the company LDAP system) is
+ * called a <strong>repository source</strong>. JBoss DNA defines a {@link
RepositorySource} interface that defines methods
+ * describing the behavior and supported features and a method for establishing
connections. A connector will
+ * have a class that implements this interface and that has JavaBean properties for all
of the connector-specific
+ * properties required to fully describe an instance of the system. Use of JavaBean
properties is not required,
+ * but it is highly recommended, as it enables reflective configuration and
administration. Applications that
+ * use JBoss DNA create an instance of the connector's {@link RepositorySource}
implementation and set the properties
+ * for the external source that the application wants to access with that connector.
+ * </p>
+ * <p>
+ * A repository source instance is then used to establish
<strong>connections</strong> to that source.
+ * A connector provides an implementation of the {@link RepositoryConnection} interface,
which defines methods for
+ * interacting with the external system. In particular, the execute(...) method takes an
{@link org.jboss.dna.graph.ExecutionContext}
+ * instance and a {@link org.jboss.dna.graph.requests.Request} object. The object defines
the environment in which
+ * the processing is occurring, including information about the JAAS Subject and
LoginContext. The
+ * {@link org.jboss.dna.graph.requests.Request} object describes the requested
+ * operations on the content, with different concrete subclasses representing each type
of activity.
+ * Examples of commands include (but not limited to) getting a node, moving a node,
creating a node,
+ * changing a node, and deleting a node. And, if the repository source is able to
participate in JTA/JTS
+ * distributed transactions, then the {@link RepositoryConnection} must implement the
{@link RepositoryConnection#getXAResource()}
+ * method by returning a valid {@link javax.transaction.xa.XAResource} object that can be
used by the transaction monitor.
+ * </p>
+ *
+ * <h3>Example connector</h3>
+ * <p>
+ * As an example, consider that we want JBoss DNA to give us access through JCR to the
schema information contained
+ * in a relational databases. We first have to develop a connector that allows us to
interact with relational
+ * databases using JDBC. That connector would contain a
<code>JdbcRepositorySource</code> Java class that
+ * implements {@link RepositorySource}, and that has all of the various JavaBean
properties for setting the
+ * name of the driver class, URL, username, password, and other properties. (Or we might
have a JavaBean property
+ * that defines the JNDI name where we can find a JDBC DataSource instance pointing to
our JDBC database.)
+ * </p>
+ * <p>
+ * Our new connector would also have a <code>JdbcRepositoryConnection</code>
Java class that implements the
+ * {@link RepositoryConnection} interface. This class would probably wrap a {@link
java.sql.Connection JDBC database connection},
+ * and would implement the {@link
RepositoryConnection#execute(org.jboss.dna.graph.ExecutionContext,
org.jboss.dna.graph.requests.Request)}
+ * method such that the nodes exposed by the connector describe the database schema of
the database. For example,
+ * the connector might represent each database table as a node with the table's name,
with properties that describe
+ * the table (e.g., the description, whether it's a temporary table), and with child
nodes that represent each of
+ * the columns, keys and constraints.
+ * </p>
+ * <p>
+ * To use our connector in an application that uses JBoss DNA, we need to create an
instance of the
+ * <code>JdbcRepositorySource</code> for each database instance that we want
to access. If we have 3 MySQL databases,
+ * 9 Oracle databases, and 4 PostgreSQL databases, then we'd need to create a total
of 16 <code>JdbcRepositorySource</code> instances,
+ * each with the properties describing a single database instance. Those sources are then
available for use by
+ * JBoss DNA components, including the JCR implementation.
+ * </p>
+ *
+ * <h3>Implementing a connector</h3>
+ * <p>
+ * As mentioned earlier, a connector consists of the Java code that is used to access
content from a system.
+ * Perhaps the most important class that makes up a connector is the implementation of
the {@link RepositorySource}.
+ * This class is analogous to JDBC's {@link javax.sql.DataSource} in that it is
instantiated to represent a single
+ * instance of a system that will be accessed, and it contains enough information (in the
form of JavaBean properties)
+ * so that it can create connections to the source.
+ * </p>
+ * <p>
+ * Why is the RepositorySource implementation a JavaBean? Well, this is the class that is
instantiated, usually reflectively,
+ * and so a no-arg constructor is required. Using JavaBean properties makes it possible
to reflect upon the object's
+ * class to determine the properties that can be set (using setters) and read (using
getters). This means that an
+ * administrative application can instantiate, configure, and manage the objects that
represent the actual sources,
+ * without having to know anything about the actual implementation.
+ * </p>
+ *
+ * <h3>Testing a connector implementation</h3>
+ * <p>
+ * Testing connectors is not really that much different than testing other classes. Using
mocks may help to isolate
+ * your instances so you can create more unit tests that don't require the underlying
source system.
+ * </p>
+ * <p>
+ * JBoss DNA does provide a set of {@link org.jboss.dna.graph.connectors.test unit tests}
that you can use to
+ * verify that your connector "behaves correctly". These are useful because
you only have to set up the test case
+ * classes (by extending one of the provided test case classes and overriding the
appropriate set up methods),
+ * but you don't have to write any test methods (since they're all inherited).
+ * </p>
+ * <p>
+ * However, there may be times when you have to use the underlying source system in your
tests. If this is the case,
+ * we recommend using Maven integration tests, which run at a different point in the
Maven lifecycle.
+ * The benefit of using integration tests is that by convention they're able to rely
upon external systems.
+ * Plus, your unit tests don't become polluted with slow-running tests that break if
the external system is not available.
+ * </p>
+ */
+
+package org.jboss.dna.graph.connectors;
+
Property changes on:
trunk/dna-graph/src/main/java/org/jboss/dna/graph/connectors/package-info.java
___________________________________________________________________
Name: svn:mime-type
+ text/plain
Added: trunk/dna-graph/src/main/java/org/jboss/dna/graph/mimetype/package-info.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/mimetype/package-info.java
(rev 0)
+++
trunk/dna-graph/src/main/java/org/jboss/dna/graph/mimetype/package-info.java 2009-01-07
20:00:11 UTC (rev 695)
@@ -0,0 +1,7 @@
+/**
+ * This package defines a set of utilities for detecting the MIME type for a stream of
data and/or the name of
+ * the file containing the data.
+ */
+
+package org.jboss.dna.graph.mimetype;
+
Property changes on:
trunk/dna-graph/src/main/java/org/jboss/dna/graph/mimetype/package-info.java
___________________________________________________________________
Name: svn:mime-type
+ text/plain
Added: trunk/dna-graph/src/main/java/org/jboss/dna/graph/package-info.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/package-info.java
(rev 0)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/package-info.java 2009-01-07
20:00:11 UTC (rev 695)
@@ -0,0 +1,5 @@
+/**
+ * The JBoss DNA Graph API defines the types that allow you to work with content
organized as a graph.
+ */
+
+package org.jboss.dna.graph;
\ No newline at end of file
Property changes on: trunk/dna-graph/src/main/java/org/jboss/dna/graph/package-info.java
___________________________________________________________________
Name: svn:mime-type
+ text/plain
Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/properties/PathFactory.java
===================================================================
---
trunk/dna-graph/src/main/java/org/jboss/dna/graph/properties/PathFactory.java 2008-12-19
19:49:01 UTC (rev 694)
+++
trunk/dna-graph/src/main/java/org/jboss/dna/graph/properties/PathFactory.java 2009-01-07
20:00:11 UTC (rev 695)
@@ -178,23 +178,31 @@
String subpath );
/**
- * Create a path segment given the supplied segment name. The resulting segment will
have no index.
+ * Create a path segment given the supplied segment name. The supplied string may
contain a same-name-sibling index in the
+ * form of "<code>[<i>n</i>]</code>" at the end of
the name, where <i>n</i> is a positive integer. Note that the
+ * same-name-sibling index is 1-based, not zero-based.
*
* @param segmentName the name of the segment
* @return the segment
* @throws IllegalArgumentException if the segment name reference is
<code>null</code> or the value could not be created from
* the supplied string
+ * @throws ValueFormatException if the same-name-sibling index is not an integer, or
if the supplied string is not a valid
+ * segment name
*/
Path.Segment createSegment( String segmentName );
/**
- * Create a path segment given the supplied segment name. The resulting segment will
have no index.
+ * Create a path segment given the supplied segment name. The supplied string may
contain a same-name-sibling index in the
+ * form of "<code>[<i>n</i>]</code>" at the end of
the name, where <i>n</i> is a positive integer. Note that the
+ * same-name-sibling index is 1-based, not zero-based.
*
* @param segmentName the name of the segment
* @param decoder the decoder that should be used to decode the qualified name
* @return the segment
* @throws IllegalArgumentException if the segment name reference is
<code>null</code> or the value could not be created from
* the supplied string
+ * @throws ValueFormatException if the same-name-sibling index is not an integer, or
if the supplied string is not a valid
+ * segment name
*/
Path.Segment createSegment( String segmentName,
TextDecoder decoder );
@@ -206,6 +214,7 @@
* @param index the index of the new segment
* @return the segment
* @throws IllegalArgumentException if the segment name reference is
<code>null</code> or if the index is invalid
+ * @throws ValueFormatException if the supplied string is not a valid segment name
*/
Path.Segment createSegment( String segmentName,
int index );
Added:
trunk/dna-graph/src/main/java/org/jboss/dna/graph/properties/basic/package-info.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/properties/basic/package-info.java
(rev 0)
+++
trunk/dna-graph/src/main/java/org/jboss/dna/graph/properties/basic/package-info.java 2009-01-07
20:00:11 UTC (rev 695)
@@ -0,0 +1,7 @@
+/**
+ * A set of basic implementations of the various interfaces defined in {@link
org.jboss.dna.graph.properties}.
+ * These implementations are all very good defaults, and in most cases can be used as
is.
+ */
+
+package org.jboss.dna.graph.properties.basic;
+
Property changes on:
trunk/dna-graph/src/main/java/org/jboss/dna/graph/properties/basic/package-info.java
___________________________________________________________________
Name: svn:mime-type
+ text/plain
Added: trunk/dna-graph/src/main/java/org/jboss/dna/graph/properties/package-info.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/properties/package-info.java
(rev 0)
+++
trunk/dna-graph/src/main/java/org/jboss/dna/graph/properties/package-info.java 2009-01-07
20:00:11 UTC (rev 695)
@@ -0,0 +1,158 @@
+/**
+ * Nodes in a graph contain properties, and this package defines the interfaces, classes
and exceptions for
+ * representing and working with properties and their values.
+ * <p>
+ * A <i>property</i> consists of a <i>name</i> and a set of
<i>values</i>. A property name is represented
+ * by {@link Name}, and is defined as a {@link Name#getLocalName() local name} in a
{@link Name#getNamespaceUri() namespace}.
+ * Property values can be of any type, although there are specific interfaces for the
known types:
+ * <ul>
+ * <li>{@link PropertyType#STRING String} - A value represented with instances of
the standard {@link String} class.</li>
+ * <li>{@link PropertyType#BINARY Binary} - A value represented with instances of
the {@link Binary} interface.</li>
+ * <li>{@link PropertyType#LONG Long} - A value represented with instances of the
standard {@link Long} class.</li>
+ * <li>{@link PropertyType#DOUBLE Double} - A value represented with instances of
the standard {@link Double} class.</li>
+ * <li>{@link PropertyType#DECIMAL Decimal} - A value represented with instances
of the standard {@link java.math.BigDecimal} class.</li>
+ * <li>{@link PropertyType#DATE Date} - A value represented with instances of the
{@link DateTime} interface.
+ * This interface hides the mishmash of Java date representations, and is designed to
follow the anticipated
+ * <code>ZonedDateTime</code> that is part of JSR-310.</li>
+ * <li>{@link PropertyType#BOOLEAN Boolean} - A value represented with instances
of the standard {@link Boolean} class.</li>
+ * <li>{@link PropertyType#NAME Name} - A value represented with instances of the
{@link Name} interface.</li>
+ * <li>{@link PropertyType#PATH Path} - A value represented with instances of the
{@link Path} interface.</li>
+ * <li>{@link PropertyType#UUID UUID} - A value represented with instances of the
standard {@link java.util.UUID} class.</li>
+ * <li>{@link PropertyType#REFERENCE Reference} - A value represented with
instances of the {@link Reference} interface.</li>
+ * <li>{@link PropertyType#URI URI} - A value represented with instances of the
standard {@link java.net.URI} class.</li>
+ * <li>{@link PropertyType#OBJECT Object} - A value represented with instances of
any class, although the class
+ * should in all practicality implement {@link java.io.Serializable}.</li>
+ * </ul>
+ * </p>
+ *
+ * <h3>Creating and converting property values</h3>
+ * <p>
+ * The design of properties and their values was centered around one key principle: when
using a property value,
+ * you often don't care what type the property value actually is, but instead care
about converting it to a
+ * property type that you know how to work with. For example, you may be working with a
property that represents
+ * a date, and you want to work with the value as a {@link DateTime} object, regardless
of whether the values
+ * are actually String, {@link DateTime}, {@link Binary}, or even {@link
java.util.Calendar} or {@link java.util.Date}
+ * instances. You know its should be a date, so you want to get a value that behaves as
a date.
+ * </p>
+ * <p>
+ * This notion of working with a <i>desired type</i> implies the ability to
convert from one value type to another.
+ * And in fact, creating values is really just converting from "other" types
into a known type.
+ * So, we can use the <i>factory</i> design pattern to have a single concept
of a component that creates property values
+ * from a variety of types. But by using generics, we can use a single {@link
ValueFactory factory} interface
+ * that has the same methods for creating value objects, but make the return type
specific to the type we want to create.
+ * </p>
+ * <p>
+ * The {@link ValueFactory} interface is defined as follows:
+ * <pre>
+ * public interface ValueFactory<T> {
+ * T create( String value ) throws ValueFormatException;
+ * T create( int value ) throws ValueFormatException;
+ * T create( long value ) throws ValueFormatException;
+ * T create( double value ) throws ValueFormatException;
+ * ...
+ * T create( java.util.Date value ) throws ValueFormatException;
+ * T create( java.util.Calendar value ) throws ValueFormatException;
+ * T create( DateTime value ) throws ValueFormatException;
+ * ...
+ * T create( java.util.UUID value ) throws ValueFormatException;
+ * T create( java.net.URI value ) throws ValueFormatException;
+ * T create( Reference value ) throws ValueFormatException;
+ * T create( Name value ) throws ValueFormatException;
+ * T create( Path value ) throws ValueFormatException;
+ * ...
+ * T create( InputStream value, long approximateLength ) throws
ValueFormatException;
+ * T create( Reader value, long approximateLength ) throws ValueFormatException;
+ * T create( Binary value ) throws ValueFormatException;
+ * ...
+ * T[] create( String[] value ) throws ValueFormatException;
+ * T[] create( int[] value ) throws ValueFormatException;
+ * T[] create( long[] value ) throws ValueFormatException;
+ * T[] create( double[] value ) throws ValueFormatException;
+ * ...
+ * }
+ * </pre>
+ * Notice that all the methods are called <code>create</code>, and most take
a single parameter whose type is
+ * one of the known types, a primitive, or a number of "other" types frequently
encountered. (The <code>create(...)</code>
+ * methods that take an {@link java.io.InputStream} or {@link java.io.Reader} have a
second parameter that specifies
+ * the length of the data.) Finally, note that almost all of the
<code>create</code> methods have a form that each
+ * take an array of values and return an array of <code>T</code>.
+ * </p>
+ * <p>
+ * These methods also all throw a {@link ValueFormatException}, in case the supplied
+ * parameter cannot be converted to the desired type. In many cases, there is a
conversion (e.g., from the String "123"
+ * to an integer), but there certainly are cases where no conversion is allowed (e.g.,
the String "a123" cannot be converted
+ * to an integer, and a {@link Name} cannot be converted to a
<code>boolean</code>). All types can be converted
+ * to a string, and all factories support converting that string back to its original
form.
+ * </p>
+ * <p>
+ * The factory for creating {@link DateTime} objects would then be an implementation of
<code>ValueFactory<DateTime></code>,
+ * a factory for creating {@link Binary} objects would be an implementation of
<code>ValueFactory<Binary</code>,
+ * and so on. In some cases, we'd like to add additional forms of
<code>create(...)</code> for specific values, and
+ * we can do this by extending a typed {@link ValueFactory}. For example, the {@link
DateTimeFactory} adds
+ * more methods for creating {@link DateTime} objects for the current time, current time
in UTC, from another time
+ * and an offset, and from individual field values:
+ * <pre>
+ * public interface DateTimeFactory extends ValueFactories<DateTime> {
+ * DateTime create();
+ * DateTime createUtc();
+ * DateTime create( DateTime original, long offsetInMillis );
+ * DateTime create( int year, int monthOfYear, int dayOfMonth,
+ * int hourOfDay, int minuteOfHour, int secondOfMinute, int
millisecondsOfSecond );
+ * DateTime create( int year, int monthOfYear, int dayOfMonth,
+ * int hourOfDay, int minuteOfHour, int secondOfMinute, int
millisecondsOfSecond,
+ * int timeZoneOffsetHours );
+ * DateTime create( int year, int monthOfYear, int dayOfMonth,
+ * int hourOfDay, int minuteOfHour, int secondOfMinute, int
millisecondsOfSecond,
+ * String timeZoneId );
+ * }
+ * </pre>
+ * There are specialized factory interfaces for several other types, including {@link
PathFactory}, {@link NameFactory},
+ * and {@link UuidFactory}.
+ * </p>
+ * <p>
+ * The {@link ValueFactories} interface collects all the factories into a single spot:
+ * <pre>
+ * public interface ValueFactories<T> {
+ * ValueFactory<String> getStringFactory();
+ * ValueFactory<Binary> getBinaryFactory();
+ * ValueFactory<Long> getLongFactory();
+ * ValueFactory<Double> getDoubleFactory();
+ * ValueFactory<BigDecimal> getDecimalFactory();
+ * DateTimeFactory getDateFactory();
+ * ValueFactory<Boolean> getBooleanFactory();
+ * NameFactory getNameFactory();
+ * ValueFactory<Reference> getReferenceFactory();
+ * PathFactory getPathFactory();
+ * ValueFactory<URI> getUriFactory();
+ * UuidFactory getUuidFactory();
+ * ValueFactory<Object> getObjectFactory();
+ *
+ * ValueFactory<?> getValueFactory( PropertyType type );
+ * ValueFactory<?> getValueFactory( Object prototype );
+ * }
+ * </pre>
+ * This allows us to programmatically get the correct factory for a type known at compile
time, but also
+ * to obtain the correct factory given a prototype object or the enumeration literal
representing
+ * the desired type. Thus, the following code compiles:
+ * <pre>
+ * ValueFactories factories = ...
+ * DateTime now = factories.getDateFactory.create();
+ * String stringValue = factories.getStringFactory().create(now);
+ * </pre>
+ * A {@link ValueFactories} is provided as part of the {@link
org.jboss.dna.graph.ExecutionContext}. In this way,
+ * the environment may use a different implementation of one or more factories.
+ * </p>
+ *
+ * <h3>Comparing property values</h3>
+ * <p>
+ * Because we have a mixture of standard Java types and custom interfaces for property
values, we need
+ * a set of {@link java.util.Comparator} implementations that allow us to compare
property values.
+ * The {@link ValueComparators} class defines a number of singleton comparators that can
be used.
+ * Plus, the {@link PropertyType} enumeration has the ability to {@link
PropertyType#getComparator() get the comparator}
+ * for the specific type (e.g.,
<code>PropertyType.BINARY.getComparator()</code>).
+ * </p>
+ *
+ */
+
+package org.jboss.dna.graph.properties;
+
Property changes on:
trunk/dna-graph/src/main/java/org/jboss/dna/graph/properties/package-info.java
___________________________________________________________________
Name: svn:mime-type
+ text/plain
Added: trunk/dna-graph/src/main/java/org/jboss/dna/graph/requests/package-info.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/requests/package-info.java
(rev 0)
+++
trunk/dna-graph/src/main/java/org/jboss/dna/graph/requests/package-info.java 2009-01-07
20:00:11 UTC (rev 695)
@@ -0,0 +1,139 @@
+/**
+ * Sometimes its useful to work with a graph using objects that represent individual
+ * <a
href="http://en.wikipedia.org/wiki/Command_pattern">commands... on the
graph. For example, "read node A"
+ * or "create a node named C under node /A/B" or "create a copy of the
subgraph at /E/F/G and place it under /E/H/I".
+ * The command pattern has a number of benefits. Since commands represent atomic
activities, they map well to events
+ * and can be easily serialized (making them very useful for {@link
org.jboss.dna.graph.connectors.RepositoryConnection connectors}).
+ * They provide an easy way to inherit or override functionality.
+ * New kinds of commands can be added with minimal (sometimes no) impact.
+ * And existing commands can be changed to include new fields, making it possible to
evolve a command
+ * while minimizing the changes.
+ * <p>
+ * This package defines standard commands, called "requests", that correspond
to different kinds of actions against a JBoss DNA graph.
+ * Each kind of request is represented by a single concrete class, and all request
classes extend from the
+ * {@link Request} abstract class. Because a lot of inheritance among commands can cause
interference and inheritance cross-talk,
+ * inheritance other than from {@link Request} is avoided as much possible. (One
exception to this is
+ * {@link CacheableRequest}, which extends {@link Request} and serves as the base class
for the "read" requests
+ * that return results.)
+ * </p>
+ * <h3>Processing Requests</h3>
+ * <p>
+ * Request objects are sent to a {@link
org.jboss.dna.graph.requests.processor.RequestProcessor}, which is responsible
+ * for performing the request. {@link
org.jboss.dna.graph.connectors.RepositoryConnection Repository connectors} usually
+ * implement their own <code>RequestProcessor</code>, which processes each
submitted request by performing the
+ * requested work.
+ * </p>
+ * <p>
+ * Processor generally do not throw exceptions when processing requests (other than
errors that signal problems
+ * with the processor itself, such as connectivity problems, that signals the
interruption of not only this request
+ * but subsequent requests, too). Instead, each request has an
+ * {@link Request#setError(Throwable) error} field that can be used to store the
exception that was encountered
+ * while processing the request. This makes it possible to submit {@link
CompositeRequest multiple requests}
+ * at once, and have any errors directly associated with the request.
+ * </p>
+ * <h3>What's in a Request?</h3>
+ * <p>
+ * In general, a single request contains two kinds of information: the information that
makes up the request (called the "input"),
+ * and the information that is the result of the request (called the "result").
The input information contains everything
+ * a processor needs to know to successfully perform the request. For example, if the
properties of a node are to be
+ * read, then the input information must include the identifier or location of the node.
If a node is to be moved,
+ * then the input information must include the original location of the node as well as
the new location.
+ * Oh, the request's input information is immutable, ensuring that this part of the
request doesn't change as it is passed
+ * around the system.
+ * </p>
+ * <p>
+ * A processor then fulfills the request by performing the requested work, and setting on
the request any
+ * requested "results". For example, if the properties of a node are to be
read, then the results include
+ * the set of {@link org.jboss.dna.graph.properties.Property} objects. If the children
are to be read, then the
+ * results consist of the list of {@link org.jboss.dna.graph.Location} object for each
child.
+ * </p>
+ * <h3>Locations</h3>
+ * <p>
+ * All requests operate on some portion of the graph, so it's imperative that there
be an easy but flexible
+ * way to identify the location of that area, whether it's a node, subgraph, child
reference, or node reference.
+ * Like other parts of the JBoss DNA Graph API, requests use {@link
org.jboss.dna.graph.Location} (or multiple
+ * Location objects) as request inputs, and one Location object for each
"reference" to a node that's in the output.
+ * </p>
+ * <p>
+ * A {@link org.jboss.dna.graph.Location} can be specified as a {@link
org.jboss.dna.graph.properties.Path} and/or
+ * as a set of identification properties. Usually, Locations are created using just the
path, since that's how
+ * nodes are identified most of the time. However, identification properties usually
consist of information that
+ * uniquely (and quickly) identifies a node, so including identification properties in a
Location may allow the
+ * processor to easily or more efficiently find the node given by the location.
+ * </p>
+ * <p>
+ * Fortunately, requests often return Location objects that are
<i>fully-defined</i>, meaning they have a Path
+ * <i>and</i> identification properties. For example, the children of a node
will be returned as a list of (fully-defined)
+ * Location objects. In fact, all requests have as part of their results an
"actual" Location for each Location in the input,
+ * so even when you don't have a fully-defined Location as an input, the request
(after processing) should contain
+ * a fully-defined Location for the input.
+ * </p>
+ * <p>
+ * Because of this, and because working with a graph usually consists of making one
request, using the results of that
+ * request to create additional requests, and so on, you'll find that it's very
easy to include fully-defined Location
+ * objects in your requests. Well, except for the first request.
+ * </p>
+ * <h3>Kinds of Requests</h3>
+ * <p>
+ * There are really two very broad categories of {@link Request}s: requests that
don't modify content and those that do.
+ * The requests that don't modify content are generally "read" requests
that are requests to return information about
+ * some part of the graph, and these requests should really have not side-effects on the
graph. Since these requests
+ * contain results that are essentially snapshots in time of a portion of the graph
content, these request types
+ * extend {@link CacheableRequest} and contain fields for referencing a {@link
org.jboss.dna.graph.cache.CachePolicy}
+ * and a time that the results were loaded.
+ * </p>
+ * <p>
+ * Requests that do modify graph content are used to create, update, move and delete
content in the graph. These
+ * kinds of requests often have little or no results (the changes will be made to the
graph unless an exception
+ * is set on the request during processing), and as such the requests do not have any
fields related to caching.
+ * </p>
+ * <p>
+ * The supported requests currently include:
+ * <ul>
+ * <li>{@link ReadNodeRequest} - A request to read the specified node and return
all of that node's properties and the
+ * location of every child node.</li>
+ * <li>{@link ReadAllChildrenRequest} - A request to read and return all of the
children of the specified node.
+ * Each child node is represented by a {@link org.jboss.dna.graph.Location} object.
This request is useful for
+ * determining the structure of the graph without having to load all of the properties
of each node.</li>
+ * <li>{@link ReadAllPropertiesRequest} - A request to read and return all of the
properties of the specified node.
+ * This request is useful when all of the properties for a node are to be returned, but
without any children
+ * information (usually because the children will be read later or were already
read).</li>
+ * <li>{@link ReadBranchRequest} - A request to read a branch of the graph,
returning the node (with its properties
+ * and children) at the specified location as well as nodes (with their properties and
children) below the specified node,
+ * up to an optional maximum depth. This request is useful for efficiently obtaining
the nodes in a subgraph when
+ * the structure is known ahead of time. This request can sometimes be significantly
more efficient that walking
+ * the subgraph and submitting individual {@link ReadNodeRequest}s for each node in the
subgraph.</li>
+ * <li>{@link ReadPropertyRequest} - A request to read and return a single named
property on a specified node.</li>
+ * <li>{@link ReadBlockOfChildrenRequest} - A request to read the children of the
specified node, returning only
+ * the locations for a subset of the children. The subset of children is defined by the
index of the first child
+ * to be returned and the number of children to be returned. This is often useful when
the repository may have large numbers
+ * of children and it is unlikely that client applications will need all of them.
</li>
+ * <li>{@link ReadNextBlockOfChildrenRequest} - A request to read the children of
the specified node, returning
+ * only the locations for a subset of the children. Unlike {@link
ReadBlockOfChildrenRequest}, this request
+ * specifies the Location of the child that appears immediately before the first child
to be returned. This
+ * request is useful in concurrent applications when one client is reading the children
while another client
+ * may be adding or removing children (thereby changing the indexes). Since this
request includes the Location of the
+ * last child previously read, the request is unaffected by changing
indexes.</li>
+ * <li>{@link CreateNodeRequest} - A request to create a new node under an
existing parent node. This request
+ * may optionally include properties for the new node.</li>
+ * <li>{@link CopyBranchRequest} - A request to copy an entire branch located
under a specified node, and place
+ * the copy of that branch under another (existing) node.</li>
+ * <li>{@link MoveBranchRequest} - A request to move the specified node and all
nodes below it into a new location
+ * specified by the Location of the new parent node.</li>
+ * <li>{@link RenameNodeRequest} - A request to rename an existing node. This
may adjust the indexes of sibling nodes
+ * with the same names (as same-name-sibling nodes are identified with SNS
indexes).</li>
+ * <li>{@link UpdatePropertiesRequest} - A request to update one or more
properties on a node. Any property
+ * with no values will be removed, while properties with one or more values will be set
(replace any existing property
+ * with the same name, if they exist). </li>
+ * <li>{@link RemovePropertiesRequest} - A request to remove one or more
properties on a node. No error is reported
+ * if the node does not contain a property that is to be removed.</li>
+ * <li>{@link DeleteBranchRequest} - A request to delete a node and all nodes
located below it.</li>
+ * <li>{@link CompositeRequest} - A request that acts as a container for multiple
other requests (of various kinds),
+ * allowing you to batch together multiple for processing. Use the one of the {@link
CompositeRequest#with(Request...) CompositeRequest.with(...)}
+ * methods to create a CompositeRequest from a series of other requests.</li>
+ * </ul>
+ * </p>
+ */
+
+package org.jboss.dna.graph.requests;
+
Property changes on:
trunk/dna-graph/src/main/java/org/jboss/dna/graph/requests/package-info.java
___________________________________________________________________
Name: svn:mime-type
+ text/plain
Modified:
trunk/dna-graph/src/main/java/org/jboss/dna/graph/requests/processor/RequestProcessor.java
===================================================================
---
trunk/dna-graph/src/main/java/org/jboss/dna/graph/requests/processor/RequestProcessor.java 2008-12-19
19:49:01 UTC (rev 694)
+++
trunk/dna-graph/src/main/java/org/jboss/dna/graph/requests/processor/RequestProcessor.java 2009-01-07
20:00:11 UTC (rev 695)
@@ -31,6 +31,7 @@
import org.jboss.dna.graph.ExecutionContext;
import org.jboss.dna.graph.GraphI18n;
import org.jboss.dna.graph.Location;
+import org.jboss.dna.graph.cache.CachePolicy;
import org.jboss.dna.graph.connectors.RepositorySourceException;
import org.jboss.dna.graph.properties.DateTime;
import org.jboss.dna.graph.properties.Name;
@@ -38,6 +39,7 @@
import org.jboss.dna.graph.properties.Property;
import org.jboss.dna.graph.properties.ReferentialIntegrityException;
import org.jboss.dna.graph.properties.basic.BasicEmptyProperty;
+import org.jboss.dna.graph.requests.CacheableRequest;
import org.jboss.dna.graph.requests.CompositeRequest;
import org.jboss.dna.graph.requests.CopyBranchRequest;
import org.jboss.dna.graph.requests.CreateNodeRequest;
@@ -68,20 +70,29 @@
private final ExecutionContext context;
private final String sourceName;
private final DateTime nowInUtc;
+ private final CachePolicy defaultCachePolicy;
protected RequestProcessor( String sourceName,
ExecutionContext context ) {
- this(sourceName, context, null);
+ this(sourceName, context, null, null);
}
protected RequestProcessor( String sourceName,
ExecutionContext context,
DateTime now ) {
+ this(sourceName, context, now, null);
+ }
+
+ protected RequestProcessor( String sourceName,
+ ExecutionContext context,
+ DateTime now,
+ CachePolicy defaultCachePolicy ) {
CheckArg.isNotEmpty(sourceName, "sourceName");
CheckArg.isNotNull(context, "context");
this.context = context;
this.sourceName = sourceName;
this.nowInUtc = now != null ? now :
context.getValueFactories().getDateFactory().createUtc();
+ this.defaultCachePolicy = defaultCachePolicy;
}
/**
@@ -112,6 +123,28 @@
}
/**
+ * Set the supplied request to have the default cache policy and the {@link
#getNowInUtc() current time in UTC}.
+ *
+ * @param request the cacheable request
+ */
+ protected void setCacheableInfo( CacheableRequest request ) {
+ request.setCachePolicy(defaultCachePolicy);
+ request.setTimeLoaded(nowInUtc);
+ }
+
+ /**
+ * Set the supplied request to have the supplied cache policy and the {@link
#getNowInUtc() current time in UTC}.
+ *
+ * @param request the cacheable request
+ * @param cachePolicy the cache policy for the request; may be null if there is to be
no cache policy
+ */
+ protected void setCacheableInfo( CacheableRequest request,
+ CachePolicy cachePolicy ) {
+ request.setCachePolicy(cachePolicy);
+ request.setTimeLoaded(nowInUtc);
+ }
+
+ /**
* Process a request by determining the type of request and delegating to the
appropriate <code>process</code> method for that
* type.
* <p>
@@ -272,6 +305,7 @@
}
// Set the actual location ...
request.setActualLocationOfNode(readAll.getActualLocationOfNode());
+ setCacheableInfo(request);
}
/**
@@ -326,6 +360,7 @@
// Set the actual location ...
request.setActualLocationOfStartingAfterNode(actualSiblingLocation);
+ setCacheableInfo(request);
}
/**
@@ -376,6 +411,7 @@
locationsToRead.add(new LocationWithDepth(child, read.depth + 1));
}
}
+ setCacheableInfo(request);
}
/**
@@ -424,6 +460,7 @@
for (Location child : readChildren) {
request.addChild(child);
}
+ setCacheableInfo(request);
}
/**
@@ -447,6 +484,7 @@
request.setProperty(property);
// Set the actual location ...
request.setActualLocationOfNode(readNode.getActualLocationOfNode());
+ setCacheableInfo(request);
}
/**
Added:
trunk/dna-graph/src/main/java/org/jboss/dna/graph/requests/processor/package-info.java
===================================================================
---
trunk/dna-graph/src/main/java/org/jboss/dna/graph/requests/processor/package-info.java
(rev 0)
+++
trunk/dna-graph/src/main/java/org/jboss/dna/graph/requests/processor/package-info.java 2009-01-07
20:00:11 UTC (rev 695)
@@ -0,0 +1,30 @@
+/**
+ * This portion of the JBoss DNA Graph API defines the {@link RequestProcessor processor}
for {@link org.jboss.dna.graph.requests.Request requests}.
+ * Simple enough, it defines methods that handle the processing of each kind of request
+ * (for example, {@link
RequestProcessor#process(org.jboss.dna.graph.requests.ReadNodeRequest)}).
+ * Usually, an implementation will inherit default implementations for some of these
methods, but will override
+ * others (or provide implementations for the abstract methods).
+ * <p>
+ * The design of the processor is to have a separate
<code>process(...)</code> methods that take as their only parameter
+ * a particular kind of {@link org.jboss.dna.graph.requests.Request}. Since the code to
process each kind of request
+ * is likely to be different, this helps to separate all the different processing code.
+ * </p>
+ * <p>The design also makes it possible to easily inherit or override
<code>process(...)</code> implementations.
+ * In fact, the {@link RequestProcessor} abstract class provides a number of default
implementations that are
+ * pretty good. Sure, the default implementations may not the fastest, but it allows you
to implement the minimum
+ * number of methods and have a complete processor. And should you find that the
performance is not good enough
+ * (which you'll verify by actually measuring performance, right?), then simply
override the method in question
+ * with an implementation that is more efficient. In other words, start simple and add
complexity only when needed.
+ * </p>
+ * <p>
+ * This design has a great benefit, though: backward compability. Let's imagine that
you're using a particular release
+ * of JBoss DNA, and have written a {@link
org.jboss.dna.graph.connectors.RepositoryConnection connector} that uses
+ * your own {@link RequestProcessor} subclass. The next release of JBoss DNA might
include additional request types
+ * and provide default implementations for the corresponding
<code>process(NewRequestType)</code> method, and your
+ * {@link RequestProcessor} subclass (written against an earlier release) will
automatically work with the next release.
+ * Plus, your connector will inherit the new functionality with zero effort on your
part.
+ * </p>
+ */
+
+package org.jboss.dna.graph.requests.processor;
+
Property changes on:
trunk/dna-graph/src/main/java/org/jboss/dna/graph/requests/processor/package-info.java
___________________________________________________________________
Name: svn:mime-type
+ text/plain
Added: trunk/dna-graph/src/main/java/org/jboss/dna/graph/sequencers/package-info.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/sequencers/package-info.java
(rev 0)
+++
trunk/dna-graph/src/main/java/org/jboss/dna/graph/sequencers/package-info.java 2009-01-07
20:00:11 UTC (rev 695)
@@ -0,0 +1,14 @@
+/**
+ * A sequencer in JBoss DNA is a component that is able to process information (usually
the content of a file,
+ * or a property value on a node) and recreate that information as a graph of structured
content. This package
+ * defines the interfaces for the sequencing system.
+ * <h3>StreamSequencer</h3>
+ * <p>The {@link StreamSequencer} interface is a special form of sequencer that
processes information coming
+ * through an {@link java.io.InputStream}. Implementations are responsible for processing
the content and generating
+ * structured content using the supplied {@link SequencerOutput} interface. Additional
details about the information
+ * being sequenced is available in the supplied {@link SequencerContext}.
+ * </p>
+ */
+
+package org.jboss.dna.graph.sequencers;
+
Property changes on:
trunk/dna-graph/src/main/java/org/jboss/dna/graph/sequencers/package-info.java
___________________________________________________________________
Name: svn:mime-type
+ text/plain
Added: trunk/dna-graph/src/main/java/org/jboss/dna/graph/xml/package-info.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/xml/package-info.java
(rev 0)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/xml/package-info.java 2009-01-07
20:00:11 UTC (rev 695)
@@ -0,0 +1,10 @@
+/**
+ * Graph content can often be represented in XML, so this part of the JBoss DNA Graph API
defines the classes
+ * that provide the binding between graph and XML content. The {@link XmlHandler} is an
implementation of
+ * several SAX interfaces (including {@link org.xml.sax.ext.LexicalHandler}, {@link
org.xml.sax.ext.DeclHandler},
+ * and {@link org.xml.sax.ext.EntityResolver2}) that responds to XML content events by
creating the corresponding
+ * content in a supplied graph.
+ */
+
+package org.jboss.dna.graph.xml;
+
Property changes on:
trunk/dna-graph/src/main/java/org/jboss/dna/graph/xml/package-info.java
___________________________________________________________________
Name: svn:mime-type
+ text/plain
Added:
trunk/dna-graph/src/test/java/org/jboss/dna/graph/connectors/test/AbstractConnectorTest.java
===================================================================
---
trunk/dna-graph/src/test/java/org/jboss/dna/graph/connectors/test/AbstractConnectorTest.java
(rev 0)
+++
trunk/dna-graph/src/test/java/org/jboss/dna/graph/connectors/test/AbstractConnectorTest.java 2009-01-07
20:00:11 UTC (rev 695)
@@ -0,0 +1,592 @@
+/*
+ * 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.connectors.test;
+
+import static org.hamcrest.core.Is.is;
+import static org.hamcrest.core.IsNull.notNullValue;
+import static org.junit.Assert.assertThat;
+import java.io.PrintStream;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.UUID;
+import java.util.concurrent.TimeUnit;
+import org.jboss.dna.common.stats.Stopwatch;
+import org.jboss.dna.graph.BasicExecutionContextFactory;
+import org.jboss.dna.graph.ExecutionContext;
+import org.jboss.dna.graph.ExecutionContextFactory;
+import org.jboss.dna.graph.Graph;
+import org.jboss.dna.graph.Location;
+import org.jboss.dna.graph.Node;
+import org.jboss.dna.graph.connectors.RepositoryConnection;
+import org.jboss.dna.graph.connectors.RepositoryConnectionFactory;
+import org.jboss.dna.graph.connectors.RepositoryContext;
+import org.jboss.dna.graph.connectors.RepositorySource;
+import org.jboss.dna.graph.connectors.RepositorySourceException;
+import org.jboss.dna.graph.properties.Name;
+import org.jboss.dna.graph.properties.Path;
+import org.jboss.dna.graph.properties.Property;
+import org.jboss.dna.graph.properties.ValueFormatException;
+import org.jboss.dna.graph.requests.Request;
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.Before;
+
+/**
+ * A class that provides standard reading verification tests for connectors. This class
is designed to be extended for each
+ * connector, and in each subclass the {@link #setUpSource()} method is defined to
provide a valid {@link RepositorySource} for
+ * the connector to be tested.
+ * <p>
+ * By default, this class sets up the repository before each test is run, ensuring that
all tests have a pristine environment.
+ * However, subclasses with tests that never modify the repository content may wish to
set up the repository only once. Note: this
+ * should be used with caution, but this can be achieved by overriding {@link
#afterEach()} to do nothing. The repository source
+ * will be set up during the first test, and will not be shut down until {@link
#afterAll() all tests have been run}.
+ * </p>
+ *
+ * @author Randall Hauch
+ */
+public abstract class AbstractConnectorTest {
+
+ protected static ExecutionContextFactory contextFactory;
+ protected static ExecutionContext context;
+ protected static RepositorySource source;
+ protected static Graph graph;
+ private static RepositoryConnectionFactory connectionFactory;
+ private static List<RepositoryConnection> openConnections;
+ private static boolean running;
+ private static Location rootLocation;
+
+ public void startRepository() throws Exception {
+ if (!running) {
+ // Set up the connection factory to other sources ...
+
+ // Set up the execution context ...
+ contextFactory = new BasicExecutionContextFactory();
+ context = setUpExecutionContext(contextFactory);
+
+ // Set up the source ...
+ source = setUpSource();
+
+ // Now set up the connection factory ...
+ openConnections = new ArrayList<RepositoryConnection>();
+ connectionFactory = new RepositoryConnectionFactory() {
+ @SuppressWarnings( "synthetic-access" )
+ public RepositoryConnection createConnection( String sourceName ) throws
RepositorySourceException {
+ if (!source.getName().equals(sourceName)) return null;
+ RepositoryConnection connection = source.getConnection();
+ if (connection == null) {
+ throw new RepositorySourceException("Unable to create a
repository connection to " + source.getName());
+ }
+ openConnections.add(connection);
+ return connection;
+ }
+ };
+
+ // Initialize the source with the rest of the environment ...
+ source.initialize(new RepositoryContext() {
+ public ExecutionContextFactory getExecutionContextFactory() {
+ return contextFactory;
+ }
+
+ @SuppressWarnings( "synthetic-access" )
+ public RepositoryConnectionFactory getRepositoryConnectionFactory() {
+ return connectionFactory;
+ }
+ });
+
+ // And set up the graph instance ...
+ graph = Graph.create(source.getName(), connectionFactory, context);
+
+ // Now have the source initialize the content (if any) ...
+ initializeContent(graph);
+ running = true;
+ }
+ }
+
+ public static void shutdownRepository() {
+ if (running) {
+ try {
+ // Shut down the connections to the source ...
+ for (RepositoryConnection connection : openConnections) {
+ connection.close();
+ }
+ } finally {
+ running = false;
+ rootLocation = null;
+ }
+ }
+ }
+
+ /**
+ * Method that is executed before each test. By default, this method {@link
#startRepository() sets up the repository}.
+ *
+ * @throws Exception
+ */
+ @Before
+ public void beforeEach() throws Exception {
+ startRepository();
+ }
+
+ /**
+ * Method that is executed after each test. By default, this method {@link
#shutdownRepository() shuts down the repository}.
+ *
+ * @throws Exception
+ */
+ @After
+ public void afterEach() throws Exception {
+ shutdownRepository();
+ }
+
+ /**
+ * Method executed after all tests have completed. By default, this method ensures
that the repository has been shut down (if
+ * this was not done in {@link #afterEach()}).
+ *
+ * @throws Exception
+ */
+ @AfterClass
+ public static void afterAll() throws Exception {
+ shutdownRepository();
+ }
+
+ //
----------------------------------------------------------------------------------------------------------------
+ // Methods used in the initialization and set up of test methods
+ //
----------------------------------------------------------------------------------------------------------------
+
+ /**
+ * Set up the {@link ExecutionContext} for each unit test.
+ *
+ * @param contextFactory the context factory that may be used; never null
+ * @return the execution context; may not be null
+ */
+ protected ExecutionContext setUpExecutionContext( ExecutionContextFactory
contextFactory ) {
+ return contextFactory.create();
+ }
+
+ /**
+ * Set up a {@link RepositorySource} that should be used for each of the unit tests.
+ *
+ * @return the repository source
+ */
+ protected abstract RepositorySource setUpSource();
+
+ /**
+ * Initialize the content of the {@link RepositorySource} set up for each of the unit
tests. This method is called shortly
+ * after {@link #setUpSource()} is called and the returned RepositorySource is
+ * {@link RepositorySource#initialize(RepositoryContext) initialized}.
+ *
+ * @param graph the graph for the {@link RepositorySource} returned from {@link
#setUpSource()}; never null
+ */
+ protected abstract void initializeContent( Graph graph );
+
+ //
----------------------------------------------------------------------------------------------------------------
+ // Helper methods commonly needed in unit tests
+ //
----------------------------------------------------------------------------------------------------------------
+
+ /**
+ * Utility to create a {@link Name} from a string.
+ *
+ * @param name the string form of the name
+ * @return the name object
+ * @throws ValueFormatException if a name could not be created from the supplied
string
+ */
+ protected Name name( String name ) {
+ return context.getValueFactories().getNameFactory().create(name);
+ }
+
+ /**
+ * Utility to create a {@link Path.Segment} from a string, where there will be no
index
+ *
+ * @param name the string form of the path segment, which may include a 1-based
same-name-sibling index
+ * @return the path object
+ * @throws ValueFormatException if a path could not be created from the supplied
string
+ */
+ protected Path.Segment segment( String name ) {
+ return context.getValueFactories().getPathFactory().createSegment(name);
+ }
+
+ /**
+ * Utility to create a {@link Path} from a string.
+ *
+ * @param path the string form of the path
+ * @return the path object
+ * @throws ValueFormatException if a path could not be created from the supplied
string
+ */
+ protected Path path( String path ) {
+ return context.getValueFactories().getPathFactory().create(path);
+ }
+
+ /**
+ * Utility to create a {@link Path} from a parent string and a subpath string.
+ *
+ * @param parentPath the string form of the parent path
+ * @param subPath the string form of the subpath
+ * @return the path object
+ * @throws ValueFormatException if a path could not be created from the supplied
string
+ */
+ protected Path path( String parentPath,
+ String subPath ) {
+ return path(path(parentPath), subPath);
+ }
+
+ /**
+ * Utility to create a {@link Path} from a parent string and a subpath string.
+ *
+ * @param parentPath the string form of the parent path
+ * @param subPath the string form of the subpath
+ * @return the path object
+ * @throws ValueFormatException if a path could not be created from the supplied
string
+ */
+ protected Path path( Path parentPath,
+ String subPath ) {
+ return context.getValueFactories().getPathFactory().create(parentPath, subPath);
+ }
+
+ /**
+ * Utility to create a {@link Location} from a string path.
+ *
+ * @param path the string form of the path
+ * @return the location
+ */
+ protected Location location( String path ) {
+ return new Location(path(path));
+ }
+
+ /**
+ * Utility to create a {@link Location} from a UUID.
+ *
+ * @param uuid the UUID
+ * @return the location
+ */
+ protected Location location( UUID uuid ) {
+ return new Location(uuid);
+ }
+
+ protected UUID getRootNodeUuid() {
+ if (rootLocation == null) {
+ Node root = graph.getNodeAt("/");
+ rootLocation = root.getLocation();
+ }
+ return rootLocation.getUuid();
+ }
+
+ //
----------------------------------------------------------------------------------------------------------------
+ // Utility method that may be used to execute requests against a repository ...
+ //
----------------------------------------------------------------------------------------------------------------
+
+ /**
+ * Submit the supplied request to the {@link #source source} for processing, and then
return the request.
+ *
+ * @param request the request to be processed
+ * @return the request after processing (for method chaining purposes)
+ * @param <T> the type of request
+ */
+ protected <T extends Request> T execute( T request ) {
+ // Get a connection ...
+ RepositoryConnection connection =
connectionFactory.createConnection(source.getName());
+ try {
+ connection.execute(context, request);
+ return request;
+ } finally {
+ connection.close();
+ }
+ }
+
+ //
----------------------------------------------------------------------------------------------------------------
+ // Utility method that may be used to create content in a repository ...
+ //
----------------------------------------------------------------------------------------------------------------
+
+ /**
+ * Create a structured subgraph by generating nodes with the supplied number of
properties and children, to the supplied
+ * maximum subgraph depth.
+ *
+ * @param graph the graph that should be used; may not be null
+ * @param initialPath the path to the new subgraph, or null if the root path should
be used
+ * @param depth the depth of the subgraph; must be a positive number
+ * @param numberOfChildrenPerNode the number of child nodes to create under each
node
+ * @param numberOfPropertiesPerNode the number of properties to create on each node;
must be 0 or more
+ * @param oneBatch true if all of the nodes are to be created in one batch
+ * @param stopwatch the stopwatch that should be used to measure the timings
+ * @return the number of nodes created in the subgraph
+ */
+ protected int createSubgraph( Graph graph,
+ String initialPath,
+ int depth,
+ int numberOfChildrenPerNode,
+ int numberOfPropertiesPerNode,
+ boolean oneBatch,
+ Stopwatch stopwatch ) {
+ return createSubgraph(graph,
+ initialPath,
+ depth,
+ numberOfChildrenPerNode,
+ numberOfPropertiesPerNode,
+ oneBatch,
+ stopwatch,
+ null,
+ null);
+ }
+
+ /**
+ * Create a structured subgraph by generating nodes with the supplied number of
properties and children, to the supplied
+ * maximum subgraph depth.
+ *
+ * @param graph the graph that should be used; may not be null
+ * @param initialPath the path to the new subgraph
+ * @param depth the depth of the subgraph; must be a positive number
+ * @param numberOfChildrenPerNode the number of child nodes to create under each
node
+ * @param numberOfPropertiesPerNode the number of properties to create on each node;
must be 0 or more
+ * @param oneBatch true if all of the nodes are to be created in one batch
+ * @param stopwatch the stopwatch that should be used to measure the timings
+ * @param output the writer to which metrics and messages should be written, or null
if no such information should be written
+ * @param description the description of this subgraph (used for logging and
printing), or null if the description should be
+ * generated automatically
+ * @return the number of nodes created in the subgraph
+ */
+ protected int createSubgraph( Graph graph,
+ String initialPath,
+ int depth,
+ int numberOfChildrenPerNode,
+ int numberOfPropertiesPerNode,
+ boolean oneBatch,
+ Stopwatch stopwatch,
+ PrintStream output,
+ String description ) {
+ // Calculate the number of nodes that we'll created, but subtrace 1 since it
doesn't create the root
+ long totalNumber = calculateTotalNumberOfNodesInTree(numberOfChildrenPerNode,
depth, false);
+ if (initialPath == null) initialPath = "";
+ if (description == null) {
+ description = "" + numberOfChildrenPerNode + "x" + depth
+ " tree with " + numberOfPropertiesPerNode
+ + " properties per node";
+ }
+
+ if (output != null) output.println(description + " (" + totalNumber +
" nodes):");
+ long totalNumberCreated = 0;
+ Graph.Batch batch = oneBatch ? graph.batch() : null;
+ if (batch != null) {
+ totalNumberCreated += createChildren(batch,
+ initialPath,
+ "node",
+ numberOfChildrenPerNode,
+ numberOfPropertiesPerNode,
+ depth,
+ null); // don't output anything
+ if (stopwatch != null) stopwatch.start();
+ batch.execute();
+ } else {
+ if (stopwatch != null) stopwatch.start();
+ totalNumberCreated += createChildren(null,
+ initialPath,
+ "node",
+ numberOfChildrenPerNode,
+ numberOfPropertiesPerNode,
+ depth,
+ null); // don't output anything
+ }
+ if (stopwatch != null) {
+ stopwatch.stop();
+ if (output != null) {
+ long totalDurationInMicroseconds =
TimeUnit.NANOSECONDS.toMicros(stopwatch.getTotalDuration().longValue());
+ long avgDuration = totalDurationInMicroseconds / totalNumber / 1000L;
+ String units = " millisecond(s)";
+ if (avgDuration == 0L) {
+ avgDuration = totalDurationInMicroseconds / totalNumber;
+ units = " microsecond(s)";
+ }
+ output.println(" Total = " + stopwatch.getTotalDuration() +
"; avg = " + avgDuration + units);
+ }
+
+ // Perform second batch ...
+ batch = graph.batch();
+ totalNumberCreated += createChildren(batch, initialPath,
"secondBranch", 2, numberOfPropertiesPerNode, 2, null);
+ Stopwatch sw = new Stopwatch();
+ sw.start();
+ batch.execute();
+ sw.stop();
+ long totalDurationInMicroseconds =
TimeUnit.NANOSECONDS.toMicros(sw.getTotalDuration().longValue());
+ long avgDuration = totalDurationInMicroseconds / totalNumber / 1000L;
+ String units = " millisecond(s)";
+ if (avgDuration == 0L) {
+ avgDuration = totalDurationInMicroseconds / totalNumber;
+ units = " microsecond(s)";
+ }
+ System.out.println(" Final total = " + sw.getTotalDuration() +
"; avg = " + avgDuration + units);
+ assertThat(totalNumberCreated, is(totalNumber +
calculateTotalNumberOfNodesInTree(2, 2, false)));
+ }
+ return (int)totalNumberCreated;
+
+ }
+
+ private int createChildren( Graph.Batch useBatch,
+ String parentPath,
+ String nodePrefix,
+ int number,
+ int numProps,
+ int depthRemaining,
+ PrintWriter output ) {
+ int numberCreated = 0;
+ Graph.Batch batch = useBatch;
+ if (batch == null) batch = graph.batch();
+ for (int i = 0; i != number; ++i) {
+ String path = parentPath + "/" + nodePrefix + (i + 1);
+ Graph.Create<Graph.Batch> create = batch.create(path);
+ String originalValue = "The quick brown fox jumped over the moon. What?
";
+ String value = originalValue;
+ for (int j = 0; j != numProps; ++j) {
+ // value = value + originalValue;
+ create = create.with("property" + (j + 1), value);
+ }
+ create.and();
+ }
+ numberCreated += number;
+ if (useBatch == null) {
+ batch.execute();
+ if (output != null) output.println(" total created ... " +
numberCreated);
+ }
+ if (depthRemaining > 1) {
+ for (int i = 0; i != number; ++i) {
+ String path = parentPath + "/" + nodePrefix + (i + 1);
+ numberCreated += createChildren(useBatch, path, nodePrefix, number,
numProps, depthRemaining - 1, null);
+ if (output != null) output.println(" total created ...
" + numberCreated);
+ }
+ }
+ return numberCreated;
+ }
+
+ protected int calculateTotalNumberOfNodesInTree( int numberOfChildrenPerNode,
+ int depth,
+ boolean countRoot ) {
+ assert depth > 0;
+ assert numberOfChildrenPerNode > 0;
+ int totalNumber = 0;
+ for (int i = 0; i <= depth; ++i) {
+ totalNumber += (int)Math.pow(numberOfChildrenPerNode, i);
+ }
+ return countRoot ? totalNumber : totalNumber - 1;
+ }
+
+ //
----------------------------------------------------------------------------------------------------------------
+ // Utility methods to work with nodes
+ //
----------------------------------------------------------------------------------------------------------------
+ /**
+ * Assert that the two supplied nodes represent the exact same node with the same
path, same ID properties, same properties,
+ * and same children.
+ *
+ * @param node1 the first node; may not be null
+ * @param node2 the second node; may not be null
+ */
+ public void assertSameNode( Node node1,
+ Node node2 ) {
+ assertThat(node1, is(notNullValue()));
+ assertThat(node2, is(notNullValue()));
+
+ // Check the locations ...
+ Location location1 = node1.getLocation();
+ Location location2 = node2.getLocation();
+ assertThat(location1.isSame(location2, true), is(true));
+
+ // Check the paths ...
+ assertThat(location1.getPath(), is(location2.getPath()));
+
+ // Check the ID properties ...
+ assertThat(location1.getIdProperties(), is(location2.getIdProperties()));
+
+ // Check the properties ...
+
+ // Check the children ...
+ assertThat(node1.getChildren(), is(node2.getChildren()));
+ assertThat(node1.getChildrenSegments(), is(node2.getChildrenSegments()));
+ }
+
+ /**
+ * Assert that the node has all of the supplied properties.
+ *
+ * @param node the node; may not be null
+ * @param properties the expected properties
+ */
+ public void assertSameProperties( Node node,
+ Map<Name, Property> properties ) {
+ assertThat(node, is(notNullValue()));
+ assertThat(properties, is(notNullValue()));
+ Set<Name> names = new HashSet<Name>(properties.keySet());
+ for (Property prop1 : node.getProperties()) {
+ Name name = prop1.getName();
+ assertThat(names.remove(name), is(true));
+ assertThat(prop1, is(properties.get(name)));
+ }
+ assertThat(names.isEmpty(), is(true));
+ }
+
+ /**
+ * Assert that the node has all of the supplied properties.
+ *
+ * @param node the node; may not be null
+ * @param properties the expected properties
+ */
+ public void assertSameProperties( Node node,
+ Iterable<Property> properties ) {
+ assertThat(node, is(notNullValue()));
+ assertThat(properties, is(notNullValue()));
+ Set<Name> names = new
HashSet<Name>(node.getPropertiesByName().keySet());
+ for (Property prop1 : properties) {
+ Name name = prop1.getName();
+ assertThat(names.remove(name), is(true));
+ assertThat(prop1, is(node.getProperty(name)));
+ }
+ assertThat(names.isEmpty(), is(true));
+ }
+
+ /**
+ * Find the name for a node that does not exist under the supplied parent.
+ *
+ * @param pathToExistingParent the parent to the node that must exist, under which
the non-existent node is to exist
+ * @return the name for a non-existent node; never null
+ */
+ public Path findPathToNonExistentNodeUnder( String pathToExistingParent ) {
+ return findPathToNonExistentNodeUnder(path(pathToExistingParent));
+ }
+
+ /**
+ * Find the name for a node that does not exist under the supplied parent.
+ *
+ * @param pathToExistingParent the parent to the node that must exist, under which
the non-existent node is to exist
+ * @return the name for a non-existent node; never null
+ */
+ public Path findPathToNonExistentNodeUnder( Path pathToExistingParent ) {
+ String nonExistentChildName = "ab39dbyfg739_adf7bg";
+ boolean verifiedNoChildWithName = false;
+ while (!verifiedNoChildWithName) {
+ verifiedNoChildWithName = true;
+ // Verify that no child of the root matches the name ...
+ for (Location childLocation : graph.getChildren().of(pathToExistingParent))
{
+ if
(childLocation.getPath().getLastSegment().getName().getLocalName().equals(nonExistentChildName))
{
+ nonExistentChildName = nonExistentChildName + '2' +
nonExistentChildName;
+ verifiedNoChildWithName = false;
+ break;
+ }
+ }
+ }
+ return path(pathToExistingParent, nonExistentChildName);
+ }
+}
Property changes on:
trunk/dna-graph/src/test/java/org/jboss/dna/graph/connectors/test/AbstractConnectorTest.java
___________________________________________________________________
Name: svn:mime-type
+ text/plain
Added:
trunk/dna-graph/src/test/java/org/jboss/dna/graph/connectors/test/ReadableConnectorTest.java
===================================================================
---
trunk/dna-graph/src/test/java/org/jboss/dna/graph/connectors/test/ReadableConnectorTest.java
(rev 0)
+++
trunk/dna-graph/src/test/java/org/jboss/dna/graph/connectors/test/ReadableConnectorTest.java 2009-01-07
20:00:11 UTC (rev 695)
@@ -0,0 +1,334 @@
+/*
+ * 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.connectors.test;
+
+import static org.hamcrest.core.Is.is;
+import static org.hamcrest.core.IsNull.notNullValue;
+import static org.hamcrest.core.IsNull.nullValue;
+import static org.junit.Assert.assertThat;
+import java.util.List;
+import java.util.UUID;
+import org.jboss.dna.graph.Graph;
+import org.jboss.dna.graph.Location;
+import org.jboss.dna.graph.Node;
+import org.jboss.dna.graph.Subgraph;
+import org.jboss.dna.graph.connectors.RepositorySource;
+import org.jboss.dna.graph.properties.Name;
+import org.jboss.dna.graph.properties.Path;
+import org.jboss.dna.graph.properties.PathNotFoundException;
+import org.jboss.dna.graph.properties.Property;
+import org.jboss.dna.graph.requests.CacheableRequest;
+import org.jboss.dna.graph.requests.ReadAllChildrenRequest;
+import org.jboss.dna.graph.requests.ReadAllPropertiesRequest;
+import org.jboss.dna.graph.requests.ReadBlockOfChildrenRequest;
+import org.jboss.dna.graph.requests.ReadBranchRequest;
+import org.jboss.dna.graph.requests.ReadNextBlockOfChildrenRequest;
+import org.jboss.dna.graph.requests.ReadNodeRequest;
+import org.jboss.dna.graph.requests.ReadPropertyRequest;
+import org.junit.After;
+import org.junit.Test;
+
+/**
+ * A class that provides standard reading verification tests for connectors. This class
is designed to be extended for each
+ * connector, and in each subclass the {@link #setUpSource()} method is defined to
provide a valid {@link RepositorySource} for
+ * the connector to be tested.
+ * <p>
+ * Since these tests only use methods that never modify repository content, the
repository is set up only once (before the first
+ * test) and is shut down after all tests have completed.
+ * </p>
+ *
+ * @author Randall Hauch
+ */
+public abstract class ReadableConnectorTest extends AbstractConnectorTest {
+
+ /**
+ * Method that is executed after each test. This method does nothing, since the
repository is set up once for all of the tests
+ * and then shutdown after all tests have completed.
+ *
+ * @throws Exception
+ */
+ @Override
+ @After
+ public void afterEach() throws Exception {
+ // Don't shut down the repository
+ }
+
+ /**
+ * Read the node at the supplied location, using a variety of techniques to read the
node and compare that each technique
+ * returned the same node. This method reads the entire node (via {@link
Graph#getNodeAt(Location)}, which uses
+ * {@link ReadNodeRequest}), reads all of the properties on the node (via {@link
Graph#getProperties()}, which uses
+ * {@link ReadAllPropertiesRequest}), and reads all of the children of the node (via
{@link Graph#getChildren()}, which uses
+ * {@link ReadAllChildrenRequest}).
+ *
+ * @param location the location; may not be null
+ * @return the node that was read
+ */
+ public Node readNodeThoroughly( Location location ) {
+ assertThat(location, is(notNullValue()));
+ Node result = null;
+ if (location.hasPath() && location.hasIdProperties()) {
+ // Read the node by the path ...
+ Node resultByPath = graph.getNodeAt(new Location(location.getPath()));
+
+ // Read the node by identification properties ...
+ Node resultByIdProps = graph.getNodeAt(new
Location(location.getIdProperties()));
+
+ // Read the node by using the location with both a path and ID properties
...
+ result = graph.getNodeAt(location);
+
+ // Verify that the same node was returned by each ...
+ assertSameNode(resultByPath, result);
+ assertSameNode(resultByIdProps, result);
+
+ // Check the result has the correct location ...
+ assertThat("The node that was read doesn't have the expected
location", result.getLocation(), is(location));
+ } else {
+ // Read the node by using the location (as is)
+ result = graph.getNodeAt(location);
+
+ // Check the result has the correct location ...
+ assertThat("The node that was read doesn't have the expected
location",
+ result.getLocation().isSame(location, true),
+ is(true));
+ }
+
+ // Read all the properties of the node ...
+ assertSameProperties(result, graph.getProperties().on(location));
+
+ // Read all the children of the node ...
+ assertThat(graph.getChildren().of(location), is(result.getChildren()));
+
+ return result;
+ }
+
+ @Test
+ public void shouldAlwaysBeAbleToReadRootNode() {
+ Node root = graph.getNodeAt("/");
+ assertThat("Connector must always have a root node", root,
is(notNullValue()));
+ assertThat("Root node must always have a path",
root.getLocation().hasPath(), is(true));
+ assertThat("Root node must never have a null path",
root.getLocation().getPath(), is(notNullValue()));
+ assertThat("Root node's path must be the root path",
root.getLocation().getPath().isRoot(), is(true));
+ List<Property> idProperties = root.getLocation().getIdProperties();
+ if (idProperties == null) {
+ assertThat(root.getLocation().hasIdProperties(), is(false));
+ } else {
+ assertThat(root.getLocation().hasIdProperties(), is(true));
+ }
+ }
+
+ @Test
+ public void shouldReturnEquivalentLocationForRootUponRepeatedCalls() {
+ Node root = graph.getNodeAt("/");
+ for (int i = 0; i != 10; ++i) {
+ Node anotherRoot = graph.getNodeAt("/");
+ assertThat(anotherRoot.getLocation().equals(root.getLocation()), is(true));
+ assertThat(anotherRoot.getLocation().getPath(),
is(root.getLocation().getPath()));
+ assertThat(anotherRoot.getLocation().getIdProperties(),
is(root.getLocation().getIdProperties()));
+ }
+ }
+
+ @Test
+ public void shouldFindRootByIdentificationProperties() {
+ Node root = graph.getNodeAt("/");
+ Location rootLocation = root.getLocation();
+ if (rootLocation.hasIdProperties()) {
+ List<Property> idProperties = rootLocation.getIdProperties();
+ assertThat("Root node's ID properties was null when there were
supposed to be properties",
+ idProperties,
+ is(notNullValue()));
+ Property firstProperty = idProperties.get(0);
+ Property[] additionalProperties = new Property[] {};
+ if (idProperties.size() > 1) {
+ List<Property> morePropertiesList = idProperties.subList(1,
idProperties.size());
+ assertThat(morePropertiesList.isEmpty(), is(false));
+ additionalProperties = morePropertiesList.toArray(new
Property[morePropertiesList.size()]);
+ }
+ // Find the root node using the identification properties ...
+ Node anotherRoot = graph.getNodeAt(firstProperty, additionalProperties);
+ assertThat(anotherRoot.getLocation().equals(root.getLocation()), is(true));
+ assertThat(anotherRoot.getLocation().getPath(),
is(root.getLocation().getPath()));
+ assertThat(anotherRoot.getLocation().getIdProperties(),
is(root.getLocation().getIdProperties()));
+ }
+ }
+
+ @Test
+ public void shouldFindRootByUUID() {
+ Node root = graph.getNodeAt("/");
+ Location rootLocation = root.getLocation();
+ UUID uuid = rootLocation.getUuid();
+ if (uuid != null) {
+ // Find the root node using the identification properties ...
+ Node anotherRoot = graph.getNodeAt(uuid);
+ assertThat(anotherRoot.getLocation().equals(root.getLocation()), is(true));
+ assertThat(anotherRoot.getLocation().getPath(),
is(root.getLocation().getPath()));
+ assertThat(anotherRoot.getLocation().getIdProperties(),
is(root.getLocation().getIdProperties()));
+ assertThat(anotherRoot.getLocation().getUuid(),
is(root.getLocation().getUuid()));
+ }
+ }
+
+ @Test
+ public void shouldReadTheChildrenOfTheRootNode() {
+ List<Location> children = graph.getChildren().of("/");
+ assertThat(children, is(notNullValue()));
+ for (Location child : children) {
+ // Check the location has a path that has the root as a parent ...
+ assertThat(child.hasPath(), is(true));
+ assertThat(child.getPath().getParent().isRoot(), is(true));
+
+ // Verify that each node can be read multiple ways ...
+ readNodeThoroughly(child);
+ }
+ }
+
+ @Test
+ public void shouldReadSubgraphStartingAtRootAndWithMaximumDepthOfThree() {
+ Subgraph subgraph = graph.getSubgraphOfDepth(3).at("/");
+ assertThat(subgraph, is(notNullValue()));
+
+ // Verify that the root node is the same as getting it directly ...
+ Node root = subgraph.getRoot();
+ assertSameNode(root, graph.getNodeAt("/"));
+
+ // Verify the first-level children ...
+ List<Location> children = graph.getChildren().of("/");
+ assertThat(children, is(notNullValue()));
+ for (Location childLocation : children) {
+ // Verify the child in the subgraph matches the same node obtained directly
from the graph ...
+ Node child = subgraph.getNode(childLocation);
+ assertSameNode(child, graph.getNodeAt(childLocation));
+
+ // Now get the second-level children ...
+ List<Location> grandChildren = graph.getChildren().of(childLocation);
+ assertThat(grandChildren, is(notNullValue()));
+ for (Location grandchildLocation : grandChildren) {
+ // Verify the grandchild in the subgraph matches the same node obtained
directly from the graph ...
+ Node grandchild = subgraph.getNode(grandchildLocation);
+ assertSameNode(grandchild, graph.getNodeAt(grandchildLocation));
+
+ // The subgraph should contain the children locations and properties for
the grandchildren.
+ // However, the subgraph should not a node for the children of the
grandchildren ...
+ for (Location greatGrandchild : grandchild.getChildren()) {
+ assertThat(subgraph.getNode(greatGrandchild), is(nullValue()));
+ }
+ }
+ }
+ }
+
+ @Test
+ public void shouldReadIndividualPropertiesOfNodes() {
+ // Read each node that is a child of the root...
+ for (Location childLocation : graph.getChildren().of("/")) {
+ // For each node ...
+ Node child = graph.getNodeAt(childLocation);
+ for (Property property : child.getProperties()) {
+ Name name = property.getName();
+ // Re-read the property and verify the value(s) match the value(s) in
'property'
+ Property singleProperty = graph.getProperty(name).on(childLocation);
+ assertThat(singleProperty, is(notNullValue()));
+ assertThat(singleProperty, is(property));
+ }
+ }
+ }
+
+ @Test( expected = PathNotFoundException.class )
+ public void shouldFailToReadNodeThatDoesNotExist() {
+ // Look up the child that should not exist, and this should throw an exception
...
+ Path nonExistantChildName = findPathToNonExistentNodeUnder("/");
+ graph.getNodeAt(nonExistantChildName);
+ }
+
+ @Test( expected = PathNotFoundException.class )
+ public void shouldFailToReadPropertyOnNodeThatDoesNotExist() {
+ // Look up the child that should not exist, and this should throw an exception
...
+ Path nonExistantChildName = findPathToNonExistentNodeUnder("/");
+ graph.getProperty("jcr:uuid").on(nonExistantChildName);
+ }
+
+ @Test
+ public void shouldFailToReadPropertyThatDoesNotExistOnExistingNode() {
+ // Read each node that is a child of the root...
+ for (Location childLocation : graph.getChildren().of("/")) {
+ // Find a name for a non-existing property ...
+ String nonExistentPropertyName = "ab39dbyfg739_adf7bg";
+ // For each node ...
+ Property property =
graph.getProperty(nonExistentPropertyName).on(childLocation); // will throw exception
+ assertThat(property, is(nullValue()));
+ }
+ }
+
+ @Test
+ public void shouldIncludeTimeLoadedInReadNodeRequests() {
+ CacheableRequest request = new ReadNodeRequest(location("/"));
+ execute(request);
+ assertThat(request.getTimeLoaded(), is(notNullValue()));
+ }
+
+ @Test
+ public void shouldIncludeTimeLoadedInReadAllPropertiesRequests() {
+ CacheableRequest request = new
ReadAllPropertiesRequest(location("/"));
+ execute(request);
+ assertThat(request.getTimeLoaded(), is(notNullValue()));
+ }
+
+ @Test
+ public void shouldIncludeTimeLoadedInReadAllChildrenRequests() {
+ CacheableRequest request = new ReadAllChildrenRequest(location("/"));
+ execute(request);
+ assertThat(request.getTimeLoaded(), is(notNullValue()));
+ }
+
+ @Test
+ public void shouldIncludeTimeLoadedInReadBlockOfChildrenRequests() {
+ CacheableRequest request = new
ReadBlockOfChildrenRequest(location("/"), 0, 100);
+ execute(request);
+ assertThat(request.getTimeLoaded(), is(notNullValue()));
+ }
+
+ @Test
+ public void shouldIncludeTimeLoadedInReadNextBlockOfChildrenRequests() {
+ // Get the first child ...
+ Location firstChild = graph.getChildren().of("/").get(0);
+ CacheableRequest request = new ReadNextBlockOfChildrenRequest(firstChild, 100);
+ execute(request);
+ assertThat(request.getTimeLoaded(), is(notNullValue()));
+ }
+
+ @Test
+ public void shouldIncludeTimeLoadedInReadPropertyRequests() {
+ // Get each of the properties on the first child ...
+ Location firstChildLocation = graph.getChildren().of("/").get(0);
+ Node firstChild = graph.getNodeAt(firstChildLocation);
+ for (Property property : firstChild.getProperties()) {
+ CacheableRequest request = new ReadPropertyRequest(firstChildLocation,
property.getName());
+ execute(request);
+ assertThat(request.getTimeLoaded(), is(notNullValue()));
+ }
+ }
+
+ @Test
+ public void shouldIncludeTimeLoadedInReadBranchRequests() {
+ CacheableRequest request = new ReadBranchRequest(location("/"), 2);
+ execute(request);
+ assertThat(request.getTimeLoaded(), is(notNullValue()));
+ }
+
+}
Property changes on:
trunk/dna-graph/src/test/java/org/jboss/dna/graph/connectors/test/ReadableConnectorTest.java
___________________________________________________________________
Name: svn:mime-type
+ text/plain
Added:
trunk/dna-graph/src/test/java/org/jboss/dna/graph/connectors/test/WritableConnectorTest.java
===================================================================
---
trunk/dna-graph/src/test/java/org/jboss/dna/graph/connectors/test/WritableConnectorTest.java
(rev 0)
+++
trunk/dna-graph/src/test/java/org/jboss/dna/graph/connectors/test/WritableConnectorTest.java 2009-01-07
20:00:11 UTC (rev 695)
@@ -0,0 +1,806 @@
+/*
+ * 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.connectors.test;
+
+import static org.hamcrest.core.Is.is;
+import static org.hamcrest.core.IsInstanceOf.instanceOf;
+import static org.hamcrest.core.IsNull.notNullValue;
+import static org.jboss.dna.graph.IsNodeWithChildren.hasChild;
+import static org.jboss.dna.graph.IsNodeWithChildren.hasChildren;
+import static org.jboss.dna.graph.IsNodeWithChildren.isEmpty;
+import static org.jboss.dna.graph.IsNodeWithProperty.hasProperty;
+import static org.junit.Assert.assertThat;
+import org.jboss.dna.common.stats.Stopwatch;
+import org.jboss.dna.common.util.IoUtil;
+import org.jboss.dna.graph.DnaLexicon;
+import org.jboss.dna.graph.Graph;
+import org.jboss.dna.graph.Node;
+import org.jboss.dna.graph.Subgraph;
+import org.jboss.dna.graph.connectors.RepositorySource;
+import org.jboss.dna.graph.properties.Reference;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * A class that provides standard writing verification tests for connectors that are able
to store any content (in any structure).
+ * This class is designed to be extended for each connector, and in each subclass the
{@link #setUpSource()} method is defined to
+ * provide a valid {@link RepositorySource} for the connector to be tested.
+ * <p>
+ * Since these tests do modify repository content, the repository is set up for each
test, given each test a pristine repository
+ * (as {@link #initializeContent(Graph) initialized} by the concrete test case class).
+ * </p>
+ *
+ * @author Randall Hauch
+ */
+public abstract class WritableConnectorTest extends AbstractConnectorTest {
+
+ protected String[] validLargeValues;
+
+ @Override
+ @Before
+ public void beforeEach() throws Exception {
+ super.beforeEach();
+
+ // Load in the large value ...
+ validLargeValues = new String[]
{IoUtil.read(getClass().getClassLoader().getResourceAsStream("LoremIpsum1.txt")),
+
IoUtil.read(getClass().getClassLoader().getResourceAsStream("LoremIpsum2.txt")),
+
IoUtil.read(getClass().getClassLoader().getResourceAsStream("LoremIpsum3.txt"))};
+ }
+
+ /**
+ * These tests require that the source supports updates, since all of the tests do
some form of updates.
+ */
+ @Test
+ public void shouldHaveUpdateCapabilities() {
+ assertThat(source.getCapabilities().supportsUpdates(), is(true));
+ }
+
+ @Test
+ public void shouldSetPropertyOnRootNode() {
+ graph.set("propA").to("valueA").on("/");
+ // Now look up the node ...
+ Node root = graph.getNodeAt("/");
+ assertThat(root, is(notNullValue()));
+ assertThat(root, hasProperty(DnaLexicon.UUID, getRootNodeUuid()));
+ assertThat(root, hasProperty("propA", "valueA"));
+ }
+
+ @Test
+ public void shouldAddChildUnderRootNode() {
+ graph.batch().create("/a").with("propB",
"valueB").and("propC", "valueC").execute();
+ // Now look up the root node ...
+ Node root = graph.getNodeAt("/");
+ assertThat(root, is(notNullValue()));
+ assertThat(root, hasProperty(DnaLexicon.UUID, getRootNodeUuid()));
+ assertThat(root.getChildren(), hasChild(segment("a")));
+
+ // Now look up node A ...
+ Node nodeA = graph.getNodeAt("/a");
+ assertThat(nodeA, is(notNullValue()));
+ assertThat(nodeA, hasProperty("propB", "valueB"));
+ assertThat(nodeA, hasProperty("propC", "valueC"));
+ assertThat(nodeA.getChildren(), isEmpty());
+ }
+
+ @Test
+ public void shouldAddChildrenOnRootNode() {
+
graph.batch().set("propA").to("valueA").on("/").and().create("/a").with("propB",
"valueB").and("propC",
"valueC").and().create("/b").with("propD",
+
"valueD").and("propE",
+
"valueE").execute();
+ // Now look up the root node ...
+ Node root = graph.getNodeAt("/");
+ assertThat(root, is(notNullValue()));
+ assertThat(root, hasProperty(DnaLexicon.UUID, getRootNodeUuid()));
+ assertThat(root, hasProperty("propA", "valueA"));
+ assertThat(root.getChildren(), hasChildren(segment("a"),
segment("b")));
+
+ // Now look up node A ...
+ Node nodeA = graph.getNodeAt("/a");
+ assertThat(nodeA, is(notNullValue()));
+ assertThat(nodeA, hasProperty("propB", "valueB"));
+ assertThat(nodeA, hasProperty("propC", "valueC"));
+ assertThat(nodeA.getChildren(), isEmpty());
+
+ // Now look up node B ...
+ Node nodeB = graph.getNodeAt("/b");
+ assertThat(nodeB, is(notNullValue()));
+ assertThat(nodeB, hasProperty("propD", "valueD"));
+ assertThat(nodeB, hasProperty("propE", "valueE"));
+ assertThat(nodeB.getChildren(), isEmpty());
+
+ // Get the subgraph ...
+ Subgraph subgraph = graph.getSubgraphOfDepth(3).at("/");
+ assertThat(subgraph, is(notNullValue()));
+ assertThat(subgraph.getNode("."), hasProperty(DnaLexicon.UUID,
getRootNodeUuid()));
+ assertThat(subgraph.getNode("."), hasProperty("propA",
"valueA"));
+ assertThat(subgraph.getNode(".").getChildren(),
hasChildren(segment("a"), segment("b")));
+ assertThat(subgraph.getNode("a"), is(notNullValue()));
+ assertThat(subgraph.getNode("a"), hasProperty("propB",
"valueB"));
+ assertThat(subgraph.getNode("a"), hasProperty("propC",
"valueC"));
+ assertThat(subgraph.getNode("a").getChildren(), isEmpty());
+ assertThat(subgraph.getNode("b"), is(notNullValue()));
+ assertThat(subgraph.getNode("b"), hasProperty("propD",
"valueD"));
+ assertThat(subgraph.getNode("b"), hasProperty("propE",
"valueE"));
+ assertThat(subgraph.getNode("b").getChildren(), isEmpty());
+ }
+
+ @Test
+ public void shouldStoreManyPropertiesOnANode() {
+ Graph.Create<Graph.Batch> create = graph.batch().create("/a");
+ for (int i = 0; i != 100; ++i) {
+ create = create.with("property" + i, "value" + i);
+ }
+ create.execute();
+ // Now look up all the properties ...
+ Node nodeA = graph.getNodeAt("/a");
+ assertThat(nodeA, is(notNullValue()));
+ for (int i = 0; i != 100; ++i) {
+ assertThat(nodeA, hasProperty("property" + i, "value" +
i));
+ }
+ assertThat(nodeA.getChildren(), isEmpty());
+ }
+
+ @Test
+ public void shouldUpdateSmallPropertiesOnANode() {
+ // Create the property and add some properties (including 2 large values) ...
+ Graph.Create<Graph.Batch> create = graph.batch().create("/a");
+ for (int i = 0; i != 10; ++i) {
+ create = create.with("property" + i, "value" + i);
+ }
+ create.execute();
+
+ // Now look up all the properties ...
+ Node nodeA = graph.getNodeAt("/a");
+ assertThat(nodeA, is(notNullValue()));
+ for (int i = 0; i != 10; ++i) {
+ assertThat(nodeA, hasProperty("property" + i, "value" +
i));
+ }
+ assertThat(nodeA.getChildren(), isEmpty());
+
+ // Now, remove some of the properties and add some others ...
+ Graph.Batch batch = graph.batch();
+ batch.remove("property0", "property1").on("/a");
+ batch.set("property6").to("new valid 6").on("/a");
+ batch.execute();
+
+ // Re-read the properties ...
+ nodeA = graph.getNodeAt("/a");
+ assertThat(nodeA, is(notNullValue()));
+ for (int i = 0; i != 10; ++i) {
+ if (i == 0 || i == 1) {
+ continue;
+ } else if (i == 6) {
+ assertThat(nodeA, hasProperty("property" + i, "new valid
6"));
+ } else {
+ assertThat(nodeA, hasProperty("property" + i, "value"
+ i));
+ }
+ }
+ assertThat(nodeA.getChildren(), isEmpty());
+
+ }
+
+ @Test
+ public void shouldUpdateLargePropertiesOnANode() {
+ // Create the property and add some properties (including 2 large values) ...
+ Graph.Create<Graph.Batch> create = graph.batch().create("/a");
+ for (int i = 0; i != 100; ++i) {
+ create = create.with("property" + i, "value" + i);
+ }
+ create = create.with("largeProperty1", validLargeValues[0]);
+ create = create.with("largeProperty2", validLargeValues[1]);
+ create.execute();
+
+ // Now look up all the properties ...
+ Node nodeA = graph.getNodeAt("/a");
+ assertThat(nodeA, is(notNullValue()));
+ for (int i = 0; i != 100; ++i) {
+ assertThat(nodeA, hasProperty("property" + i, "value" +
i));
+ }
+ assertThat(nodeA, hasProperty("largeProperty1", validLargeValues[0]));
+ assertThat(nodeA, hasProperty("largeProperty2", validLargeValues[1]));
+ assertThat(nodeA.getChildren(), isEmpty());
+
+ // Now, remove some of the properties and add some others ...
+ Graph.Batch batch = graph.batch();
+ batch.remove("largeProperty1", "property0",
"property1").on("/a");
+ batch.set("property50").to("new valid
50").on("/a");
+
batch.set("largeProperty3").to(validLargeValues[2]).on("/a");
+ batch.execute();
+
+ // Re-read the properties ...
+ nodeA = graph.getNodeAt("/a");
+ assertThat(nodeA, is(notNullValue()));
+ for (int i = 0; i != 100; ++i) {
+ if (i == 0 || i == 1) {
+ continue;
+ } else if (i == 50) {
+ assertThat(nodeA, hasProperty("property" + i, "new valid
50"));
+ } else {
+ assertThat(nodeA, hasProperty("property" + i, "value"
+ i));
+ }
+ }
+ assertThat(nodeA, hasProperty("largeProperty2", validLargeValues[1]));
+ assertThat(nodeA, hasProperty("largeProperty3", validLargeValues[2]));
+ assertThat(nodeA.getChildren(), isEmpty());
+
+ }
+
+ @Test
+ public void shouldCreateDeepBranchUsingIndividualRequests() {
+ String initialPath = "";
+ int depth = 50;
+ int numChildrenPerNode = 1;
+ int numPropertiesPerNode = 7;
+ Stopwatch sw = new Stopwatch();
+ boolean batch = false;
+ String description = "deep and narrow tree, 1x50";
+ createSubgraph(graph, initialPath, depth, numChildrenPerNode,
numPropertiesPerNode, batch, sw, System.out, description);
+ }
+
+ @Test
+ public void shouldCreateDeepBranchUsingOneBatch() {
+ String initialPath = "";
+ int depth = 50;
+ int numChildrenPerNode = 1;
+ int numPropertiesPerNode = 7;
+ Stopwatch sw = new Stopwatch();
+ boolean batch = true;
+ String description = "deep and narrow tree, 1x50";
+ createSubgraph(graph, initialPath, depth, numChildrenPerNode,
numPropertiesPerNode, batch, sw, System.out, description);
+ }
+
+ @Test
+ public void shouldCreateFlatAndWideTreeUsingOneBatch() {
+ String initialPath = "";
+ int depth = 1;
+ int numChildrenPerNode = 300;
+ int numPropertiesPerNode = 7;
+ Stopwatch sw = new Stopwatch();
+ boolean batch = true;
+ createSubgraph(graph, initialPath, depth, numChildrenPerNode,
numPropertiesPerNode, batch, sw, System.out, null);
+ }
+
+ @Test
+ public void shouldCreateBinaryTreeUsingOneBatch() {
+ String initialPath = "";
+ int depth = 8;
+ int numChildrenPerNode = 2;
+ int numPropertiesPerNode = 7;
+ Stopwatch sw = new Stopwatch();
+ boolean batch = true;
+ String description = "binary tree, 2x8";
+ createSubgraph(graph, initialPath, depth, numChildrenPerNode,
numPropertiesPerNode, batch, sw, System.out, description);
+ }
+
+ @Test
+ public void shouldCreateTreeWith10ChildrenAnd2LevelsDeepUsingIndividualRequests() {
+ String initialPath = "";
+ int depth = 2;
+ int numChildrenPerNode = 10;
+ int numPropertiesPerNode = 7;
+ Stopwatch sw = new Stopwatch();
+ boolean batch = false;
+ createSubgraph(graph, initialPath, depth, numChildrenPerNode,
numPropertiesPerNode, batch, sw, System.out, null);
+ }
+
+ @Test
+ public void shouldCreateTreeWith10ChildrenAnd2LevelsDeepUsingOneBatch() {
+ String initialPath = "";
+ int depth = 2;
+ int numChildrenPerNode = 10;
+ int numPropertiesPerNode = 7;
+ Stopwatch sw = new Stopwatch();
+ boolean batch = true;
+ createSubgraph(graph, initialPath, depth, numChildrenPerNode,
numPropertiesPerNode, batch, sw, System.out, null);
+ }
+
+ @Test
+ public void shouldCreateTreeWith10ChildrenAnd3LevelsDeepUsingOneBatch() {
+ String initialPath = "";
+ int depth = 3;
+ int numChildrenPerNode = 10;
+ int numPropertiesPerNode = 7;
+ Stopwatch sw = new Stopwatch();
+ boolean batch = true;
+ createSubgraph(graph, initialPath, depth, numChildrenPerNode,
numPropertiesPerNode, batch, sw, System.out, null);
+ }
+
+ @Test
+ public void shouldCreateAndReadTree() {
+ // Create the tree (at total of 40 nodes, plus the extra 6 added later)...
+ // /
+ // /node1
+ // /node1/node1
+ // /node1/node1/node1
+ // /node1/node1/node2
+ // /node1/node1/node3
+ // /node1/node2
+ // /node1/node2/node1
+ // /node1/node2/node2
+ // /node1/node2/node3
+ // /node1/node3
+ // /node1/node3/node1
+ // /node1/node3/node2
+ // /node1/node3/node3
+ // /node2
+ // /node2/node1
+ // /node2/node1/node1
+ // /node2/node1/node2
+ // /node2/node1/node3
+ // /node2/node2
+ // /node2/node2/node1
+ // /node2/node2/node2
+ // /node2/node2/node3
+ // /node2/node3
+ // /node2/node3/node1
+ // /node2/node3/node2
+ // /node2/node3/node3
+ // /node3
+ // /node3/node1
+ // /node3/node1/node1
+ // /node3/node1/node2
+ // /node3/node1/node3
+ // /node3/node2
+ // /node3/node2/node1
+ // /node3/node2/node2
+ // /node3/node2/node3
+ // /node3/node3
+ // /node3/node3/node1
+ // /node3/node3/node2
+ // /node3/node3/node3
+ // /secondBranch1
+ // /secondBranch1/secondBranch1
+ // /secondBranch1/secondBranch2
+ // /secondBranch2
+ // /secondBranch2/secondBranch1
+ // /secondBranch2/secondBranch2
+
+ String initialPath = "";
+ int depth = 3;
+ int numChildrenPerNode = 3;
+ int numPropertiesPerNode = 3;
+ Stopwatch sw = new Stopwatch();
+ boolean batch = true;
+ createSubgraph(graph, initialPath, depth, numChildrenPerNode,
numPropertiesPerNode, batch, sw, System.out, null);
+
+ assertThat(graph.getChildren().of("/node1"),
hasChildren(segment("node1"), segment("node2"),
segment("node3")));
+ assertThat(graph.getChildren().of("/node1/node1"),
hasChildren(segment("node1"), segment("node2"),
segment("node3")));
+ assertThat(graph.getChildren().of("/node1/node2"),
hasChildren(segment("node1"), segment("node2"),
segment("node3")));
+ assertThat(graph.getChildren().of("/node1/node3"),
hasChildren(segment("node1"), segment("node2"),
segment("node3")));
+ assertThat(graph.getChildren().of("/node1/node3/node1"), isEmpty());
+
+ assertThat(graph.getChildren().of("/node2"),
hasChildren(segment("node1"), segment("node2"),
segment("node3")));
+ assertThat(graph.getChildren().of("/node2/node1"),
hasChildren(segment("node1"), segment("node2"),
segment("node3")));
+ assertThat(graph.getChildren().of("/node2/node2"),
hasChildren(segment("node1"), segment("node2"),
segment("node3")));
+ assertThat(graph.getChildren().of("/node2/node3"),
hasChildren(segment("node1"), segment("node2"),
segment("node3")));
+ assertThat(graph.getChildren().of("/node2/node3/node1"), isEmpty());
+
+ assertThat(graph.getChildren().of("/node3"),
hasChildren(segment("node1"), segment("node2"),
segment("node3")));
+ assertThat(graph.getChildren().of("/node3/node1"),
hasChildren(segment("node1"), segment("node2"),
segment("node3")));
+ assertThat(graph.getChildren().of("/node3/node2"),
hasChildren(segment("node1"), segment("node2"),
segment("node3")));
+ assertThat(graph.getChildren().of("/node3/node3"),
hasChildren(segment("node1"), segment("node2"),
segment("node3")));
+ assertThat(graph.getChildren().of("/node3/node3/node1"), isEmpty());
+
+ Subgraph subgraph = graph.getSubgraphOfDepth(2).at("/node3/node2");
+ assertThat(subgraph, is(notNullValue()));
+ assertThat(subgraph.getNode(".").getChildren(),
hasChildren(segment("node1"), segment("node2"),
segment("node3")));
+ assertThat(subgraph.getNode("."), hasProperty("property1",
"The quick brown fox jumped over the moon. What? "));
+ assertThat(subgraph.getNode("."), hasProperty("property2",
"The quick brown fox jumped over the moon. What? "));
+ assertThat(subgraph.getNode("."), hasProperty("property3",
"The quick brown fox jumped over the moon. What? "));
+ assertThat(subgraph.getNode("node1").getChildren(), isEmpty());
+ assertThat(subgraph.getNode("node1"),
hasProperty("property1", "The quick brown fox jumped over the moon. What?
"));
+ assertThat(subgraph.getNode("node1"),
hasProperty("property2", "The quick brown fox jumped over the moon. What?
"));
+ assertThat(subgraph.getNode("node1"),
hasProperty("property3", "The quick brown fox jumped over the moon. What?
"));
+ assertThat(subgraph.getNode("node2").getChildren(), isEmpty());
+ assertThat(subgraph.getNode("node2"),
hasProperty("property1", "The quick brown fox jumped over the moon. What?
"));
+ assertThat(subgraph.getNode("node2"),
hasProperty("property2", "The quick brown fox jumped over the moon. What?
"));
+ assertThat(subgraph.getNode("node2"),
hasProperty("property3", "The quick brown fox jumped over the moon. What?
"));
+ assertThat(subgraph.getNode("node3").getChildren(), isEmpty());
+ assertThat(subgraph.getNode("node3"),
hasProperty("property1", "The quick brown fox jumped over the moon. What?
"));
+ assertThat(subgraph.getNode("node3"),
hasProperty("property2", "The quick brown fox jumped over the moon. What?
"));
+ assertThat(subgraph.getNode("node3"),
hasProperty("property3", "The quick brown fox jumped over the moon. What?
"));
+
+ subgraph = graph.getSubgraphOfDepth(2).at("/node3");
+ assertThat(subgraph, is(notNullValue()));
+ assertThat(subgraph.getNode(".").getChildren(),
hasChildren(segment("node1"), segment("node2"),
segment("node3")));
+ assertThat(subgraph.getNode("."), hasProperty("property1",
"The quick brown fox jumped over the moon. What? "));
+ assertThat(subgraph.getNode("."), hasProperty("property2",
"The quick brown fox jumped over the moon. What? "));
+ assertThat(subgraph.getNode("."), hasProperty("property3",
"The quick brown fox jumped over the moon. What? "));
+ assertThat(subgraph.getNode("node1").getChildren(), isEmpty());
+ assertThat(subgraph.getNode("node1"),
hasProperty("property1", "The quick brown fox jumped over the moon. What?
"));
+ assertThat(subgraph.getNode("node1"),
hasProperty("property2", "The quick brown fox jumped over the moon. What?
"));
+ assertThat(subgraph.getNode("node1"),
hasProperty("property3", "The quick brown fox jumped over the moon. What?
"));
+ assertThat(subgraph.getNode("node2").getChildren(), isEmpty());
+ assertThat(subgraph.getNode("node2"),
hasProperty("property1", "The quick brown fox jumped over the moon. What?
"));
+ assertThat(subgraph.getNode("node2"),
hasProperty("property2", "The quick brown fox jumped over the moon. What?
"));
+ assertThat(subgraph.getNode("node2"),
hasProperty("property3", "The quick brown fox jumped over the moon. What?
"));
+ assertThat(subgraph.getNode("node3").getChildren(), isEmpty());
+ assertThat(subgraph.getNode("node3"),
hasProperty("property1", "The quick brown fox jumped over the moon. What?
"));
+ assertThat(subgraph.getNode("node3"),
hasProperty("property2", "The quick brown fox jumped over the moon. What?
"));
+ assertThat(subgraph.getNode("node3"),
hasProperty("property3", "The quick brown fox jumped over the moon. What?
"));
+ }
+
+ @Test
+ public void shouldDeleteNodes() {
+ // Create the tree (at total of 40 nodes, plus the extra 6 added later)...
+ // /
+ // /node1
+ // /node1/node1
+ // /node1/node1/node1
+ // /node1/node1/node2
+ // /node1/node1/node3
+ // /node1/node2
+ // /node1/node2/node1
+ // /node1/node2/node2
+ // /node1/node2/node3
+ // /node1/node3
+ // /node1/node3/node1
+ // /node1/node3/node2
+ // /node1/node3/node3
+ // /node2
+ // /node2/node1
+ // /node2/node1/node1
+ // /node2/node1/node2
+ // /node2/node1/node3
+ // /node2/node2
+ // /node2/node2/node1
+ // /node2/node2/node2
+ // /node2/node2/node3
+ // /node2/node3
+ // /node2/node3/node1
+ // /node2/node3/node2
+ // /node2/node3/node3
+ // /node3
+ // /node3/node1
+ // /node3/node1/node1
+ // /node3/node1/node2
+ // /node3/node1/node3
+ // /node3/node2
+ // /node3/node2/node1
+ // /node3/node2/node2
+ // /node3/node2/node3
+ // /node3/node3
+ // /node3/node3/node1
+ // /node3/node3/node2
+ // /node3/node3/node3
+ // /secondBranch1
+ // /secondBranch1/secondBranch1
+ // /secondBranch1/secondBranch2
+ // /secondBranch2
+ // /secondBranch2/secondBranch1
+ // /secondBranch2/secondBranch2
+
+ String initialPath = "";
+ int depth = 3;
+ int numChildrenPerNode = 3;
+ int numPropertiesPerNode = 3;
+ Stopwatch sw = new Stopwatch();
+ boolean batch = true;
+ createSubgraph(graph, initialPath, depth, numChildrenPerNode,
numPropertiesPerNode, batch, sw, System.out, null);
+
+ // Delete two branches ...
+ graph.delete("/node2/node2").and().delete("/node3/node1");
+
+ assertThat(graph.getChildren().of("/node1"),
hasChildren(segment("node1"), segment("node2"),
segment("node3")));
+ assertThat(graph.getChildren().of("/node1/node1"),
hasChildren(segment("node1"), segment("node2"),
segment("node3")));
+ assertThat(graph.getChildren().of("/node1/node2"),
hasChildren(segment("node1"), segment("node2"),
segment("node3")));
+ assertThat(graph.getChildren().of("/node1/node3"),
hasChildren(segment("node1"), segment("node2"),
segment("node3")));
+ assertThat(graph.getChildren().of("/node1/node3/node1"),
hasChildren());
+
+ assertThat(graph.getChildren().of("/node2"),
hasChildren(segment("node1"), segment("node3")));
+ assertThat(graph.getChildren().of("/node2/node1"),
hasChildren(segment("node1"), segment("node2"),
segment("node3")));
+ assertThat(graph.getChildren().of("/node2/node3"),
hasChildren(segment("node1"), segment("node2"),
segment("node3")));
+ assertThat(graph.getChildren().of("/node2/node3/node1"),
hasChildren());
+
+ assertThat(graph.getChildren().of("/node3"),
hasChildren(segment("node2"), segment("node3")));
+ assertThat(graph.getChildren().of("/node3/node2"),
hasChildren(segment("node1"), segment("node2"),
segment("node3")));
+ assertThat(graph.getChildren().of("/node3/node3"),
hasChildren(segment("node1"), segment("node2"),
segment("node3")));
+ assertThat(graph.getChildren().of("/node3/node3/node1"),
hasChildren());
+
+ Subgraph subgraph = graph.getSubgraphOfDepth(3).at("/");
+ assertThat(subgraph, is(notNullValue()));
+ assertThat(subgraph.getNode(".").getChildren(),
hasChildren(segment("node1"), segment("node3")));
+ assertThat(subgraph.getNode("node1").getChildren(),
hasChildren(segment("node1"), segment("node2"),
segment("node3")));
+ assertThat(subgraph.getNode("node1"),
hasProperty("property1", "The quick brown fox jumped over the moon. What?
"));
+ assertThat(subgraph.getNode("node1"),
hasProperty("property2", "The quick brown fox jumped over the moon. What?
"));
+ assertThat(subgraph.getNode("node1"),
hasProperty("property3", "The quick brown fox jumped over the moon. What?
"));
+ assertThat(subgraph.getNode("node2").getChildren(),
hasChildren(segment("node1"), segment("node3")));
+ assertThat(subgraph.getNode("node2"),
hasProperty("property1", "The quick brown fox jumped over the moon. What?
"));
+ assertThat(subgraph.getNode("node2"),
hasProperty("property2", "The quick brown fox jumped over the moon. What?
"));
+ assertThat(subgraph.getNode("node2"),
hasProperty("property3", "The quick brown fox jumped over the moon. What?
"));
+ assertThat(subgraph.getNode("node3").getChildren(),
hasChildren(segment("node2"), segment("node3")));
+ assertThat(subgraph.getNode("node3"),
hasProperty("property1", "The quick brown fox jumped over the moon. What?
"));
+ assertThat(subgraph.getNode("node3"),
hasProperty("property2", "The quick brown fox jumped over the moon. What?
"));
+ assertThat(subgraph.getNode("node3"),
hasProperty("property3", "The quick brown fox jumped over the moon. What?
"));
+
+ subgraph = graph.getSubgraphOfDepth(2).at("/node3");
+ assertThat(subgraph, is(notNullValue()));
+ assertThat(subgraph.getNode(".").getChildren(),
hasChildren(segment("node2"), segment("node3")));
+ assertThat(subgraph.getNode("."), hasProperty("property1",
"The quick brown fox jumped over the moon. What? "));
+ assertThat(subgraph.getNode("."), hasProperty("property2",
"The quick brown fox jumped over the moon. What? "));
+ assertThat(subgraph.getNode("."), hasProperty("property3",
"The quick brown fox jumped over the moon. What? "));
+ assertThat(subgraph.getNode("node2").getChildren(), isEmpty());
+ assertThat(subgraph.getNode("node2"),
hasProperty("property1", "The quick brown fox jumped over the moon. What?
"));
+ assertThat(subgraph.getNode("node2"),
hasProperty("property2", "The quick brown fox jumped over the moon. What?
"));
+ assertThat(subgraph.getNode("node2"),
hasProperty("property3", "The quick brown fox jumped over the moon. What?
"));
+ assertThat(subgraph.getNode("node3").getChildren(), isEmpty());
+ assertThat(subgraph.getNode("node3"),
hasProperty("property1", "The quick brown fox jumped over the moon. What?
"));
+ assertThat(subgraph.getNode("node3"),
hasProperty("property2", "The quick brown fox jumped over the moon. What?
"));
+ assertThat(subgraph.getNode("node3"),
hasProperty("property3", "The quick brown fox jumped over the moon. What?
"));
+ }
+
+ @Test
+ public void shouldCopyNodeWithChildren() {
+ // Create the tree (at total of 40 nodes, plus the extra 6 added later)...
+ // /
+ // /node1
+ // /node1/node1
+ // /node1/node1/node1
+ // /node1/node1/node2
+ // /node1/node1/node3
+ // /node1/node2
+ // /node1/node2/node1
+ // /node1/node2/node2
+ // /node1/node2/node3
+ // /node1/node3
+ // /node1/node3/node1
+ // /node1/node3/node2
+ // /node1/node3/node3
+ // /node2
+ // /node2/node1
+ // /node2/node1/node1
+ // /node2/node1/node2
+ // /node2/node1/node3
+ // /node2/node2
+ // /node2/node2/node1
+ // /node2/node2/node2
+ // /node2/node2/node3
+ // /node2/node3
+ // /node2/node3/node1
+ // /node2/node3/node2
+ // /node2/node3/node3
+ // /node3
+ // /node3/node1
+ // /node3/node1/node1
+ // /node3/node1/node2
+ // /node3/node1/node3
+ // /node3/node2
+ // /node3/node2/node1
+ // /node3/node2/node2
+ // /node3/node2/node3
+ // /node3/node3
+ // /node3/node3/node1
+ // /node3/node3/node2
+ // /node3/node3/node3
+ // /secondBranch1
+ // /secondBranch1/secondBranch1
+ // /secondBranch1/secondBranch2
+ // /secondBranch2
+ // /secondBranch2/secondBranch1
+ // /secondBranch2/secondBranch2
+
+ String initialPath = "";
+ int depth = 3;
+ int numChildrenPerNode = 3;
+ int numPropertiesPerNode = 3;
+ Stopwatch sw = new Stopwatch();
+ boolean batch = true;
+ createSubgraph(graph, initialPath, depth, numChildrenPerNode,
numPropertiesPerNode, batch, sw, System.out, null);
+
+ // Create some references between nodes that aren't involved with the copy
...
+
graph.set("refProp").on("/node1").to(graph.getNodeAt("/node1/node3"));
+
graph.set("refProp").on("/node1/node1").to(graph.getNodeAt("/node3/node2"));
// will soon be /node3/node2[1]
+
+ // Create some "inward" references from nodes that are NOT being copied
to nodes that are being copied ...
+
graph.set("refProp").on("/node1/node2").to(graph.getNodeAt("/node2/node2"));
+
graph.set("refProp").on("/node1/node3").to(graph.getNodeAt("/node2/node2"));
+
+ // Create some "outward" references from nodes that are being copied to
nodes that are NOT being copied ...
+
graph.set("refProp").on("/node2/node1").to(graph.getNodeAt("/node1/node1"));
+
graph.set("refProp").on("/node2/node3").to(graph.getNodeAt("/node1/node2"));
+
+ // Create some "internal" references between nodes that are being
copied ...
+
graph.set("refProp").on("/node2/node2").to(graph.getNodeAt("/node2/node2/node1"));
+
graph.set("refProp").on("/node2/node3/node1").to(graph.getNodeAt("/node2/node2/node1"));
+
+ // Verify the references are there ...
+ assertReference("/node1", "refProp",
"/node1/node3");
+ assertReference("/node1/node1", "refProp",
"/node3/node2");
+ assertReference("/node1/node2", "refProp",
"/node2/node2");
+ assertReference("/node1/node3", "refProp",
"/node2/node2");
+ assertReference("/node2/node1", "refProp",
"/node1/node1");
+ assertReference("/node2/node3", "refProp",
"/node1/node2");
+ assertReference("/node2/node2", "refProp",
"/node2/node2/node1");
+ assertReference("/node2/node3/node1", "refProp",
"/node2/node2/node1");
+
+ // Copy a branches ...
+ graph.copy("/node2").into("/node3");
+
+ // Verify the references are still there ...
+ assertReference("/node1", "refProp",
"/node1/node3");
+ assertReference("/node1/node1", "refProp",
"/node3/node2[1]");
+ assertReference("/node1/node2", "refProp",
"/node2/node2");
+ assertReference("/node1/node3", "refProp",
"/node2/node2");
+ assertReference("/node2/node1", "refProp",
"/node1/node1");
+ assertReference("/node2/node3", "refProp",
"/node1/node2");
+ assertReference("/node2/node2", "refProp",
"/node2/node2/node1");
+ assertReference("/node2/node3/node1", "refProp",
"/node2/node2/node1");
+
+ // And verify that we have a few new (outward and internal) references in the
copy ...
+ assertReference("/node3/node2[2]/node1", "refProp",
"/node1/node1"); // outward
+ assertReference("/node3/node2[2]/node3", "refProp",
"/node1/node2"); // outward
+ assertReference("/node3/node2[2]/node2", "refProp",
"/node3/node2[2]/node2/node1"); // internal
+ assertReference("/node3/node2[2]/node3/node1", "refProp",
"/node3/node2[2]/node2/node1"); // internal
+
+ // Now assert the structure ...
+ assertThat(graph.getChildren().of("/node1"),
hasChildren(segment("node1"), segment("node2"),
segment("node3")));
+ assertThat(graph.getChildren().of("/node1/node1"),
hasChildren(segment("node1"), segment("node2"),
segment("node3")));
+ assertThat(graph.getChildren().of("/node1/node2"),
hasChildren(segment("node1"), segment("node2"),
segment("node3")));
+ assertThat(graph.getChildren().of("/node1/node3"),
hasChildren(segment("node1"), segment("node2"),
segment("node3")));
+ assertThat(graph.getChildren().of("/node1/node3/node1"),
hasChildren());
+
+ // The original of the copy should still exist ...
+ assertThat(graph.getChildren().of("/node2"),
hasChildren(segment("node1"), segment("node3")));
+ assertThat(graph.getChildren().of("/node2/node1"),
hasChildren(segment("node1"), segment("node2"),
segment("node3")));
+ assertThat(graph.getChildren().of("/node2/node3"),
hasChildren(segment("node1"), segment("node2"),
segment("node3")));
+ assertThat(graph.getChildren().of("/node2/node3/node1"),
hasChildren());
+
+ assertThat(graph.getChildren().of("/node3"),
hasChildren(segment("node2[1]"), segment("node3"),
segment("node2[2]")));
+ assertThat(graph.getChildren().of("/node3/node2[1]"),
hasChildren(segment("node1"), segment("node2"),
segment("node3")));
+ assertThat(graph.getChildren().of("/node3/node3"),
hasChildren(segment("node1"), segment("node2"),
segment("node3")));
+ assertThat(graph.getChildren().of("/node3/node3/node1"),
hasChildren());
+ assertThat(graph.getChildren().of("/node3/node2[2]"),
hasChildren(segment("node1"), segment("node2"),
segment("node3")));
+ assertThat(graph.getChildren().of("/node3/node2[2]/node1"),
hasChildren(segment("node1"),
+
segment("node2"),
+
segment("node3")));
+ assertThat(graph.getChildren().of("/node3/node2[2]/node2"),
hasChildren(segment("node1"),
+
segment("node2"),
+
segment("node3")));
+ assertThat(graph.getChildren().of("/node3/node2[2]/node3"),
hasChildren(segment("node1"),
+
segment("node2"),
+
segment("node3")));
+ assertThat(graph.getChildren().of("/node3/node2[2]/node1/node1"),
hasChildren());
+
+ Subgraph subgraph = graph.getSubgraphOfDepth(4).at("/node3");
+ assertThat(subgraph, is(notNullValue()));
+ assertThat(subgraph.getNode(".").getChildren(),
hasChildren(segment("node2"), segment("node3")));
+ assertThat(subgraph.getNode("."), hasProperty("property1",
"The quick brown fox jumped over the moon. What? "));
+ assertThat(subgraph.getNode("."), hasProperty("property2",
"The quick brown fox jumped over the moon. What? "));
+ assertThat(subgraph.getNode("."), hasProperty("property3",
"The quick brown fox jumped over the moon. What? "));
+ assertThat(subgraph.getNode("node2[1]").getChildren(),
hasChildren(segment("node1"), segment("node2"),
segment("node3")));
+ assertThat(subgraph.getNode("node2[1]"),
hasProperty("property1", "The quick brown fox jumped over the moon. What?
"));
+ assertThat(subgraph.getNode("node2[1]"),
hasProperty("property2", "The quick brown fox jumped over the moon. What?
"));
+ assertThat(subgraph.getNode("node2[1]"),
hasProperty("property3", "The quick brown fox jumped over the moon. What?
"));
+ assertThat(subgraph.getNode("node3").getChildren(), isEmpty());
+ assertThat(subgraph.getNode("node3"),
hasProperty("property1", "The quick brown fox jumped over the moon. What?
"));
+ assertThat(subgraph.getNode("node3"),
hasProperty("property2", "The quick brown fox jumped over the moon. What?
"));
+ assertThat(subgraph.getNode("node3"),
hasProperty("property3", "The quick brown fox jumped over the moon. What?
"));
+ assertThat(subgraph.getNode("node2[2]").getChildren(),
hasChildren(segment("node1"), segment("node2"),
segment("node3")));
+ assertThat(subgraph.getNode("node2[2]"),
hasProperty("property1", "The quick brown fox jumped over the moon. What?
"));
+ assertThat(subgraph.getNode("node2[2]"),
hasProperty("property2", "The quick brown fox jumped over the moon. What?
"));
+ assertThat(subgraph.getNode("node2[2]"),
hasProperty("property3", "The quick brown fox jumped over the moon. What?
"));
+ assertThat(subgraph.getNode("node2[2]/node1").getChildren(),
hasChildren(segment("node1"),
+
segment("node2"),
+
segment("node3")));
+ assertThat(subgraph.getNode("node2[2]/node1"),
hasProperty("property1",
+ "The quick brown
fox jumped over the moon. What? "));
+ assertThat(subgraph.getNode("node2[2]/node1"),
hasProperty("property2",
+ "The quick brown
fox jumped over the moon. What? "));
+ assertThat(subgraph.getNode("node2[2]/node1"),
hasProperty("property3",
+ "The quick brown
fox jumped over the moon. What? "));
+ assertThat(subgraph.getNode("node2[2]/node1/node1").getChildren(),
isEmpty());
+ assertThat(subgraph.getNode("node2[2]/node1/node1"),
hasProperty("property1",
+ "The quick
brown fox jumped over the moon. What? "));
+ assertThat(subgraph.getNode("node2[2]/node1/node1"),
hasProperty("property2",
+ "The quick
brown fox jumped over the moon. What? "));
+ assertThat(subgraph.getNode("node2[2]/node1/node1"),
hasProperty("property3",
+ "The quick
brown fox jumped over the moon. What? "));
+ assertThat(subgraph.getNode("node2[2]/node1/node2").getChildren(),
isEmpty());
+ assertThat(subgraph.getNode("node2[2]/node1/node2"),
hasProperty("property1",
+ "The quick
brown fox jumped over the moon. What? "));
+ assertThat(subgraph.getNode("node2[2]/node1/node2"),
hasProperty("property2",
+ "The quick
brown fox jumped over the moon. What? "));
+ assertThat(subgraph.getNode("node2[2]/node1/node2"),
hasProperty("property3",
+ "The quick
brown fox jumped over the moon. What? "));
+ assertThat(subgraph.getNode("node2[2]/node1/node3").getChildren(),
isEmpty());
+ assertThat(subgraph.getNode("node2[2]/node1/node3"),
hasProperty("property1",
+ "The quick
brown fox jumped over the moon. What? "));
+ assertThat(subgraph.getNode("node2[2]/node1/node3"),
hasProperty("property2",
+ "The quick
brown fox jumped over the moon. What? "));
+ assertThat(subgraph.getNode("node2[2]/node1/node3"),
hasProperty("property3",
+ "The quick
brown fox jumped over the moon. What? "));
+ assertThat(subgraph.getNode("node2[2]/node2").getChildren(),
hasChildren(segment("node1"),
+
segment("node2"),
+
segment("node3")));
+ assertThat(subgraph.getNode("node2[2]/node2"),
hasProperty("property1",
+ "The quick brown
fox jumped over the moon. What? "));
+ assertThat(subgraph.getNode("node2[2]/node2"),
hasProperty("property2",
+ "The quick brown
fox jumped over the moon. What? "));
+ assertThat(subgraph.getNode("node2[2]/node2"),
hasProperty("property3",
+ "The quick brown
fox jumped over the moon. What? "));
+ assertThat(subgraph.getNode("node2[2]/node2/node1").getChildren(),
isEmpty());
+ assertThat(subgraph.getNode("node2[2]/node2/node1"),
hasProperty("property1",
+ "The quick
brown fox jumped over the moon. What? "));
+ assertThat(subgraph.getNode("node2[2]/node2/node1"),
hasProperty("property2",
+ "The quick
brown fox jumped over the moon. What? "));
+ assertThat(subgraph.getNode("node2[2]/node2/node1"),
hasProperty("property3",
+ "The quick
brown fox jumped over the moon. What? "));
+ assertThat(subgraph.getNode("node2[2]/node2/node2").getChildren(),
isEmpty());
+ assertThat(subgraph.getNode("node2[2]/node2/node2"),
hasProperty("property1",
+ "The quick
brown fox jumped over the moon. What? "));
+ assertThat(subgraph.getNode("node2[2]/node2/node2"),
hasProperty("property2",
+ "The quick
brown fox jumped over the moon. What? "));
+ assertThat(subgraph.getNode("node2[2]/node2/node2"),
hasProperty("property3",
+ "The quick
brown fox jumped over the moon. What? "));
+ assertThat(subgraph.getNode("node2[2]/node2/node3").getChildren(),
isEmpty());
+ assertThat(subgraph.getNode("node2[2]/node2/node3"),
hasProperty("property1",
+ "The quick
brown fox jumped over the moon. What? "));
+ assertThat(subgraph.getNode("node2[2]/node2/node3"),
hasProperty("property2",
+ "The quick
brown fox jumped over the moon. What? "));
+ assertThat(subgraph.getNode("node2[2]/node2/node3"),
hasProperty("property3",
+ "The quick
brown fox jumped over the moon. What? "));
+ assertThat(subgraph.getNode("node2[2]/node3").getChildren(),
hasChildren(segment("node1"),
+
segment("node2"),
+
segment("node3")));
+ assertThat(subgraph.getNode("node2[2]/node3"),
hasProperty("property1",
+ "The quick brown
fox jumped over the moon. What? "));
+ assertThat(subgraph.getNode("node2[2]/node3"),
hasProperty("property2",
+ "The quick brown
fox jumped over the moon. What? "));
+ assertThat(subgraph.getNode("node2[2]/node3"),
hasProperty("property3",
+ "The quick brown
fox jumped over the moon. What? "));
+ assertThat(subgraph.getNode("node2[2]/node3/node1").getChildren(),
isEmpty());
+ assertThat(subgraph.getNode("node2[2]/node3/node1"),
hasProperty("property1",
+ "The quick
brown fox jumped over the moon. What? "));
+ assertThat(subgraph.getNode("node2[2]/node3/node1"),
hasProperty("property2",
+ "The quick
brown fox jumped over the moon. What? "));
+ assertThat(subgraph.getNode("node2[2]/node3/node1"),
hasProperty("property3",
+ "The quick
brown fox jumped over the moon. What? "));
+ assertThat(subgraph.getNode("node2[2]/node3/node2").getChildren(),
isEmpty());
+ assertThat(subgraph.getNode("node2[2]/node3/node2"),
hasProperty("property1",
+ "The quick
brown fox jumped over the moon. What? "));
+ assertThat(subgraph.getNode("node2[2]/node3/node2"),
hasProperty("property2",
+ "The quick
brown fox jumped over the moon. What? "));
+ assertThat(subgraph.getNode("node2[2]/node3/node2"),
hasProperty("property3",
+ "The quick
brown fox jumped over the moon. What? "));
+ assertThat(subgraph.getNode("node2[2]/node3/node3").getChildren(),
isEmpty());
+ assertThat(subgraph.getNode("node2[2]/node3/node3"),
hasProperty("property1",
+ "The quick
brown fox jumped over the moon. What? "));
+ assertThat(subgraph.getNode("node2[2]/node3/node3"),
hasProperty("property2",
+ "The quick
brown fox jumped over the moon. What? "));
+ assertThat(subgraph.getNode("node2[2]/node3/node3"),
hasProperty("property3",
+ "The quick
brown fox jumped over the moon. What? "));
+ }
+
+ protected void assertReference( String fromNodePath,
+ String propertyName,
+ String... toNodePath ) {
+ Object[] values =
graph.getProperty(propertyName).on(fromNodePath).getValuesAsArray();
+ assertThat(values.length, is(toNodePath.length));
+ for (int i = 0; i != values.length; ++i) {
+ Object value = values[i];
+ assertThat(value, is(instanceOf(Reference.class)));
+ Reference ref = (Reference)value;
+ assertThat(graph.resolve(ref), is(graph.getNodeAt(toNodePath[i])));
+ }
+ }
+
+}
Property changes on:
trunk/dna-graph/src/test/java/org/jboss/dna/graph/connectors/test/WritableConnectorTest.java
___________________________________________________________________
Name: svn:mime-type
+ text/plain
Added:
trunk/dna-graph/src/test/java/org/jboss/dna/graph/connectors/test/package-info.java
===================================================================
--- trunk/dna-graph/src/test/java/org/jboss/dna/graph/connectors/test/package-info.java
(rev 0)
+++
trunk/dna-graph/src/test/java/org/jboss/dna/graph/connectors/test/package-info.java 2009-01-07
20:00:11 UTC (rev 695)
@@ -0,0 +1,50 @@
+/**
+ * This package provides a set of unit tests that can be used to verify that a connector
behaves correctly
+ * and in such a way that matches DNA's expectations. These unit tests utilize JUnit
4 annotations, and are written
+ * in a <a
href="http://behaviour-driven.org/">Behavior-Driven
Development</a> style where each test method
+ * is named to describe the behavior being tested (e.g.,
"<code>shouldAlwaysBeAbleToReadRootNode()</code>").
+ * This style helps to make more obvious the requirements and behavior that the test is
verifying.
+ * <p>
+ * To use these unit tests, you simply extend the unit tests provided by this package.
But rather than write a
+ * whole slew of test methods, your tests inherit all the test methods. So, it's
pretty easy to get a lot of
+ * tests for almost no cost.
+ * </p>
+ * <p>
+ * All unit tests (directly or indirectly) subclass {@link AbstractConnectorTest}, which
does all the work of
+ * setting up and tearing down the connector, its environment, and common utility objects
needed for many of
+ * the tests. This class defines two abstract methods that each of your test classes
will have to implement:
+ * <ul>
+ * <li>{@link AbstractConnectorTest#setUpSource() setUpSource()} - This method
allows you to configure your
+ * {@link org.jboss.dna.graph.connectors.RepositorySource}, and will be called once
during the set up of each test method.</li>
+ * <li>{@link AbstractConnectorTest#initializeContent(org.jboss.dna.graph.Graph)
initializeContent()} - This method allows you to
+ * populate the repository with some predefined content, using the supplied {@link
org.jboss.dna.graph.Graph graph}.
+ * This method will be called once during the set up of each test method (after {@link
AbstractConnectorTest#setUpSource()}),
+ * ensuring that each test method gets a fresh repository with content.
+ * If your connector is accessing an empty source, you should use this method to
populate the source with some content.
+ * On the other hand, if your connector is accessing a system that already has its own
content, you may still want
+ * to use this method to prepare the content or check that the content is what
you're expecting.
+ * </li>
+ * </ul>
+ * The class also defines a number of utility methods that may be useful in different
test methods. These include (among others)
+ * {@link AbstractConnectorTest#name(String)} to create a name from a string, {@link
AbstractConnectorTest#path(String)} to
+ * create a path from a string, {@link AbstractConnectorTest#segment(String)} to create a
path segment from a string,
+ * and several forms of {@link
AbstractConnectorTest#createSubgraph(org.jboss.dna.graph.Graph, String, int, int, int,
boolean, org.jboss.dna.common.stats.Stopwatch, java.io.PrintStream, String)
createSubgraph(...)}
+ * that is useful for creating a highly-structured subgraph.
+ * </p>
+ * <p>
+ * While you may choose to extend <code>AbstractConnectorTest</code>,
you'll more likely want to extend one of the
+ * other test classes that define test methods, including:
+ * <ul>
+ * <li>{@link ReadableConnectorTest} - Reads the content of your source through a
variety of methods, including
+ * {@link org.jboss.dna.graph.requests.ReadNodeRequest reading one node at a time},
+ * {@link org.jboss.dna.graph.requests.ReadBranchRequest reading branches},
+ * {@link org.jboss.dna.graph.requests.ReadPropertyRequest reading one property at a
time},
+ * {@link org.jboss.dna.graph.requests.ReadAllPropertiesRequest reading all properties},
+ * {@link org.jboss.dna.graph.requests.ReadAllChildrenRequest reading children}, etc.
These methods really just
+ * verify that the content read is consistent, and cannot verify that all the
information is correct.
+ * </li>
+ * </ul>
+ * </p>
+ */
+
+package org.jboss.dna.graph.connectors.test;
\ No newline at end of file
Property changes on:
trunk/dna-graph/src/test/java/org/jboss/dna/graph/connectors/test/package-info.java
___________________________________________________________________
Name: svn:mime-type
+ text/plain
Copied: trunk/dna-graph/src/test/resources/LoremIpsum1.txt (from rev 694,
trunk/extensions/dna-connector-store-jpa/src/test/resources/LoremIpsum1.txt)
===================================================================
--- trunk/dna-graph/src/test/resources/LoremIpsum1.txt (rev 0)
+++ trunk/dna-graph/src/test/resources/LoremIpsum1.txt 2009-01-07 20:00:11 UTC (rev 695)
@@ -0,0 +1,9 @@
+1. Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Etiam pellentesque, erat non
congue laoreet, felis nulla eleifend nulla, ut varius tellus felis eu risus. Etiam
dapibus. Aenean eget nibh sed urna pellentesque egestas. Aliquam sit amet turpis. Ut
blandit purus sit amet pede dictum gravida. Sed vestibulum, pede vel ornare faucibus,
libero est accumsan ante, a varius diam sapien a tortor. Maecenas luctus. Nullam purus
lectus, varius non, sodales nec, adipiscing eget, nulla. Nam nunc nunc, pretium a,
tincidunt ut, tristique ac, nisi. Vivamus tempor est quis turpis. In hac habitasse platea
dictumst. Nunc porttitor lacinia nunc. Proin dolor magna, imperdiet at, placerat in,
tempor at, magna. Fusce a nulla. Pellentesque sit amet sem nec leo porttitor sagittis.
+
+Integer mauris. Nunc libero est, consectetuer id, lacinia in, tempor a, lacus. Praesent
sed augue sed felis dictum blandit. In ipsum orci, pellentesque vitae, viverra vitae,
aliquam at, ante. Sed imperdiet mauris id lectus. Morbi vel magna sed pede accumsan
vulputate. Aliquam nisi est, molestie vitae, tempus sed, sagittis at, nulla. Cum sociis
natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Curabitur
turpis risus, faucibus at, porttitor ac, venenatis a, diam. Vestibulum blandit consequat
felis. Vestibulum vitae leo. Curabitur a nunc. Phasellus ornare. Morbi tortor est,
fringilla in, volutpat non, volutpat at, risus.
+
+Proin blandit pede tristique arcu. Vivamus ac libero. Donec pharetra leo sit amet orci.
Fusce sit amet velit. Nunc dui. Ut vitae nulla. Suspendisse potenti. Cras et elit.
Maecenas ipsum. Proin at mauris. Suspendisse fringilla libero a neque.
+
+Nullam vitae justo egestas justo lobortis consectetuer. Vestibulum eros. Cras id nisl.
Sed scelerisque commodo erat. Cras dignissim massa quis tellus. Donec auctor rutrum dolor.
Donec leo est, tempor quis, eleifend tincidunt, euismod eu, purus. Duis laoreet consequat
lectus. Cras quis dolor id massa mattis lacinia. Ut sollicitudin nunc in eros. Cras
gravida, purus ac rhoncus tempus, justo eros sodales mi, sit amet tincidunt metus erat sed
neque.
+
+Aliquam vitae nunc. Nullam at lectus ut nunc iaculis commodo. Phasellus augue. Aenean eu
mi eget magna egestas vehicula. Cum sociis natoque penatibus et magnis dis parturient
montes, nascetur ridiculus mus. Aenean molestie, massa eu rhoncus euismod, nisl eros porta
augue, vel dictum orci nibh vestibulum diam. Duis interdum. In hac habitasse platea
dictumst. In hac habitasse platea dictumst. Integer mi erat, egestas ac, aliquet at,
accumsan quis, dui. Praesent consectetuer euismod lorem. Fusce sagittis eros. Vivamus
hendrerit. Pellentesque malesuada, mauris eget mollis eleifend, enim nisl imperdiet
tortor, at pulvinar pede lectus eget augue. Cras convallis, odio nec aliquet rutrum, lorem
ipsum tincidunt mauris, vel rutrum lectus felis at felis. Ut lectus. Nam varius.
\ No newline at end of file
Property changes on: trunk/dna-graph/src/test/resources/LoremIpsum1.txt
___________________________________________________________________
Name: svn:mime-type
+ text/plain
Copied: trunk/dna-graph/src/test/resources/LoremIpsum2.txt (from rev 694,
trunk/extensions/dna-connector-store-jpa/src/test/resources/LoremIpsum2.txt)
===================================================================
--- trunk/dna-graph/src/test/resources/LoremIpsum2.txt (rev 0)
+++ trunk/dna-graph/src/test/resources/LoremIpsum2.txt 2009-01-07 20:00:11 UTC (rev 695)
@@ -0,0 +1,9 @@
+2. Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Etiam pellentesque, erat non
congue laoreet, felis nulla eleifend nulla, ut varius tellus felis eu risus. Etiam
dapibus. Aenean eget nibh sed urna pellentesque egestas. Aliquam sit amet turpis. Ut
blandit purus sit amet pede dictum gravida. Sed vestibulum, pede vel ornare faucibus,
libero est accumsan ante, a varius diam sapien a tortor. Maecenas luctus. Nullam purus
lectus, varius non, sodales nec, adipiscing eget, nulla. Nam nunc nunc, pretium a,
tincidunt ut, tristique ac, nisi. Vivamus tempor est quis turpis. In hac habitasse platea
dictumst. Nunc porttitor lacinia nunc. Proin dolor magna, imperdiet at, placerat in,
tempor at, magna. Fusce a nulla. Pellentesque sit amet sem nec leo porttitor sagittis.
+
+Integer mauris. Nunc libero est, consectetuer id, lacinia in, tempor a, lacus. Praesent
sed augue sed felis dictum blandit. In ipsum orci, pellentesque vitae, viverra vitae,
aliquam at, ante. Sed imperdiet mauris id lectus. Morbi vel magna sed pede accumsan
vulputate. Aliquam nisi est, molestie vitae, tempus sed, sagittis at, nulla. Cum sociis
natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Curabitur
turpis risus, faucibus at, porttitor ac, venenatis a, diam. Vestibulum blandit consequat
felis. Vestibulum vitae leo. Curabitur a nunc. Phasellus ornare. Morbi tortor est,
fringilla in, volutpat non, volutpat at, risus.
+
+Proin blandit pede tristique arcu. Vivamus ac libero. Donec pharetra leo sit amet orci.
Fusce sit amet velit. Nunc dui. Ut vitae nulla. Suspendisse potenti. Cras et elit.
Maecenas ipsum. Proin at mauris. Suspendisse fringilla libero a neque.
+
+Nullam vitae justo egestas justo lobortis consectetuer. Vestibulum eros. Cras id nisl.
Sed scelerisque commodo erat. Cras dignissim massa quis tellus. Donec auctor rutrum dolor.
Donec leo est, tempor quis, eleifend tincidunt, euismod eu, purus. Duis laoreet consequat
lectus. Cras quis dolor id massa mattis lacinia. Ut sollicitudin nunc in eros. Cras
gravida, purus ac rhoncus tempus, justo eros sodales mi, sit amet tincidunt metus erat sed
neque.
+
+Aliquam vitae nunc. Nullam at lectus ut nunc iaculis commodo. Phasellus augue. Aenean eu
mi eget magna egestas vehicula. Cum sociis natoque penatibus et magnis dis parturient
montes, nascetur ridiculus mus. Aenean molestie, massa eu rhoncus euismod, nisl eros porta
augue, vel dictum orci nibh vestibulum diam. Duis interdum. In hac habitasse platea
dictumst. In hac habitasse platea dictumst. Integer mi erat, egestas ac, aliquet at,
accumsan quis, dui. Praesent consectetuer euismod lorem. Fusce sagittis eros. Vivamus
hendrerit. Pellentesque malesuada, mauris eget mollis eleifend, enim nisl imperdiet
tortor, at pulvinar pede lectus eget augue. Cras convallis, odio nec aliquet rutrum, lorem
ipsum tincidunt mauris, vel rutrum lectus felis at felis. Ut lectus. Nam varius.
\ No newline at end of file
Property changes on: trunk/dna-graph/src/test/resources/LoremIpsum2.txt
___________________________________________________________________
Name: svn:mime-type
+ text/plain
Copied: trunk/dna-graph/src/test/resources/LoremIpsum3.txt (from rev 694,
trunk/extensions/dna-connector-store-jpa/src/test/resources/LoremIpsum3.txt)
===================================================================
--- trunk/dna-graph/src/test/resources/LoremIpsum3.txt (rev 0)
+++ trunk/dna-graph/src/test/resources/LoremIpsum3.txt 2009-01-07 20:00:11 UTC (rev 695)
@@ -0,0 +1,9 @@
+3. Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Etiam pellentesque, erat non
congue laoreet, felis nulla eleifend nulla, ut varius tellus felis eu risus. Etiam
dapibus. Aenean eget nibh sed urna pellentesque egestas. Aliquam sit amet turpis. Ut
blandit purus sit amet pede dictum gravida. Sed vestibulum, pede vel ornare faucibus,
libero est accumsan ante, a varius diam sapien a tortor. Maecenas luctus. Nullam purus
lectus, varius non, sodales nec, adipiscing eget, nulla. Nam nunc nunc, pretium a,
tincidunt ut, tristique ac, nisi. Vivamus tempor est quis turpis. In hac habitasse platea
dictumst. Nunc porttitor lacinia nunc. Proin dolor magna, imperdiet at, placerat in,
tempor at, magna. Fusce a nulla. Pellentesque sit amet sem nec leo porttitor sagittis.
+
+Integer mauris. Nunc libero est, consectetuer id, lacinia in, tempor a, lacus. Praesent
sed augue sed felis dictum blandit. In ipsum orci, pellentesque vitae, viverra vitae,
aliquam at, ante. Sed imperdiet mauris id lectus. Morbi vel magna sed pede accumsan
vulputate. Aliquam nisi est, molestie vitae, tempus sed, sagittis at, nulla. Cum sociis
natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Curabitur
turpis risus, faucibus at, porttitor ac, venenatis a, diam. Vestibulum blandit consequat
felis. Vestibulum vitae leo. Curabitur a nunc. Phasellus ornare. Morbi tortor est,
fringilla in, volutpat non, volutpat at, risus.
+
+Proin blandit pede tristique arcu. Vivamus ac libero. Donec pharetra leo sit amet orci.
Fusce sit amet velit. Nunc dui. Ut vitae nulla. Suspendisse potenti. Cras et elit.
Maecenas ipsum. Proin at mauris. Suspendisse fringilla libero a neque.
+
+Nullam vitae justo egestas justo lobortis consectetuer. Vestibulum eros. Cras id nisl.
Sed scelerisque commodo erat. Cras dignissim massa quis tellus. Donec auctor rutrum dolor.
Donec leo est, tempor quis, eleifend tincidunt, euismod eu, purus. Duis laoreet consequat
lectus. Cras quis dolor id massa mattis lacinia. Ut sollicitudin nunc in eros. Cras
gravida, purus ac rhoncus tempus, justo eros sodales mi, sit amet tincidunt metus erat sed
neque.
+
+Aliquam vitae nunc. Nullam at lectus ut nunc iaculis commodo. Phasellus augue. Aenean eu
mi eget magna egestas vehicula. Cum sociis natoque penatibus et magnis dis parturient
montes, nascetur ridiculus mus. Aenean molestie, massa eu rhoncus euismod, nisl eros porta
augue, vel dictum orci nibh vestibulum diam. Duis interdum. In hac habitasse platea
dictumst. In hac habitasse platea dictumst. Integer mi erat, egestas ac, aliquet at,
accumsan quis, dui. Praesent consectetuer euismod lorem. Fusce sagittis eros. Vivamus
hendrerit. Pellentesque malesuada, mauris eget mollis eleifend, enim nisl imperdiet
tortor, at pulvinar pede lectus eget augue. Cras convallis, odio nec aliquet rutrum, lorem
ipsum tincidunt mauris, vel rutrum lectus felis at felis. Ut lectus. Nam varius.
\ No newline at end of file
Property changes on: trunk/dna-graph/src/test/resources/LoremIpsum3.txt
___________________________________________________________________
Name: svn:mime-type
+ text/plain
Modified:
trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/models/basic/BasicRequestProcessor.java
===================================================================
---
trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/models/basic/BasicRequestProcessor.java 2008-12-19
19:49:01 UTC (rev 694)
+++
trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/models/basic/BasicRequestProcessor.java 2009-01-07
20:00:11 UTC (rev 695)
@@ -347,6 +347,7 @@
return;
}
if (actualLocation != null) request.setActualLocationOfNode(actualLocation);
+ setCacheableInfo(request);
}
/**
@@ -374,6 +375,7 @@
return;
}
if (actualLocation != null) request.setActualLocationOfNode(actualLocation);
+ setCacheableInfo(request);
}
/**
@@ -469,6 +471,7 @@
return;
}
if (actualLocation != null) request.setActualLocationOfNode(actualLocation);
+ setCacheableInfo(request);
}
/**
@@ -564,6 +567,7 @@
return;
}
if (actualLocation != null)
request.setActualLocationOfStartingAfterNode(actualLocation);
+ setCacheableInfo(request);
}
/**
@@ -615,6 +619,7 @@
return;
}
if (actualLocation != null) request.setActualLocationOfNode(actualLocation);
+ setCacheableInfo(request);
}
/**
@@ -632,7 +637,11 @@
// Just get the UUID ...
Location location = request.on();
ActualLocation actualLocation = getActualLocation(location);
+ UUID uuid = actualLocation.location.getUuid();
+ Property uuidProperty =
getExecutionContext().getPropertyFactory().create(propertyName, uuid);
+ request.setProperty(uuidProperty);
request.setActualLocationOfNode(actualLocation.location);
+ setCacheableInfo(request);
} catch (Throwable e) { // Includes PathNotFoundException
request.setError(e);
}
@@ -678,6 +687,7 @@
return;
}
if (actualLocation != null) request.setActualLocationOfNode(actualLocation);
+ setCacheableInfo(request);
}
/**
@@ -825,6 +835,8 @@
String parentUuid = actual.uuid;
Location parentLocation = actualLocation;
List<Location> children = new LinkedList<Location>();
+ Map<Location, List<Location>> childrenByParentLocation = new
HashMap<Location, List<Location>>();
+ childrenByParentLocation.put(parentLocation, children);
boolean includeChildrenOfNodesAtMaxDepth = true;
for (ChildEntity child : query.getNodes(false,
includeChildrenOfNodesAtMaxDepth)) {
String namespaceUri = child.getChildNamespace().getUri();
@@ -834,22 +846,30 @@
// Figure out who the parent is ...
String childParentUuid = child.getId().getParentUuidString();
if (!parentUuid.equals(childParentUuid)) {
- // The parent isn't the last parent, so record the children
found so far ...
- request.setChildren(parentLocation, children);
- // And find the correct parent ...
+ // Find the correct parent ...
parentLocation = locationsByUuid.get(childParentUuid);
parent = parentLocation.getPath();
parentUuid = childParentUuid;
- children = new LinkedList<Location>();
+ // See if there is already a list of children for this parent
...
+ children = childrenByParentLocation.get(parentLocation);
+ if (children == null) {
+ children = new LinkedList<Location>();
+ childrenByParentLocation.put(parentLocation, children);
+ }
}
+ assert children != null;
Path childPath = pathFactory.create(parent, childName, sns);
String childUuidString = child.getId().getChildUuidString();
Location childLocation = new Location(childPath,
UUID.fromString(childUuidString));
locationsByUuid.put(childUuidString, childLocation);
children.add(childLocation);
}
- if (!children.isEmpty()) {
- request.setChildren(parentLocation, children);
+ // Now add the list of children to the results ...
+ for (Map.Entry<Location, List<Location>> entry :
childrenByParentLocation.entrySet()) {
+ // Don't add if there are no children ...
+ if (!entry.getValue().isEmpty()) {
+ request.setChildren(entry.getKey(), entry.getValue());
+ }
}
// Note that we've found children for nodes that are at the maximum
depth. This is so that the nodes
@@ -892,6 +912,7 @@
return;
}
request.setActualLocationOfNode(actualLocation);
+ setCacheableInfo(request);
}
/**
Modified:
trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/models/basic/LargeValueEntity.java
===================================================================
---
trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/models/basic/LargeValueEntity.java 2008-12-19
19:49:01 UTC (rev 694)
+++
trunk/extensions/dna-connector-store-jpa/src/main/java/org/jboss/dna/connector/store/jpa/models/basic/LargeValueEntity.java 2009-01-07
20:00:11 UTC (rev 695)
@@ -28,7 +28,6 @@
import javax.persistence.Enumerated;
import javax.persistence.Id;
import javax.persistence.Lob;
-import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
import javax.persistence.Query;
import javax.persistence.Table;
@@ -42,11 +41,7 @@
*/
@Entity
@Table( name = "DNA_BASIC_LARGE_VALUES" )
-@NamedQueries( {
-// @NamedQuery( name = "LargeValueEntity.selectUsed", query =
-// "select value from LargeValueEntity value where value.id in (select values.hash
from PropertiesEntity prop join prop.largeValues values)"
-// ),
-@NamedQuery( name = "LargeValueEntity.deleteUnused", query = "delete
LargeValueEntity value where value.id not in (select values.hash from PropertiesEntity
prop join prop.largeValues values)" )} )
+@NamedQuery( name = "LargeValueEntity.deleteUnused", query = "delete
LargeValueEntity value where value.id not in (select values.hash from PropertiesEntity
prop join prop.largeValues values)" )
public class LargeValueEntity {
@Id
Deleted:
trunk/extensions/dna-connector-store-jpa/src/test/java/org/jboss/dna/connector/store/jpa/JpaConnectionTest.java
===================================================================
---
trunk/extensions/dna-connector-store-jpa/src/test/java/org/jboss/dna/connector/store/jpa/JpaConnectionTest.java 2008-12-19
19:49:01 UTC (rev 694)
+++
trunk/extensions/dna-connector-store-jpa/src/test/java/org/jboss/dna/connector/store/jpa/JpaConnectionTest.java 2009-01-07
20:00:11 UTC (rev 695)
@@ -1,1126 +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.connector.store.jpa;
-
-import static org.hamcrest.core.Is.is;
-import static org.hamcrest.core.IsInstanceOf.instanceOf;
-import static org.hamcrest.core.IsNull.notNullValue;
-import static org.jboss.dna.graph.IsNodeWithChildren.hasChild;
-import static org.jboss.dna.graph.IsNodeWithChildren.hasChildren;
-import static org.jboss.dna.graph.IsNodeWithChildren.isEmpty;
-import static org.jboss.dna.graph.IsNodeWithProperty.hasProperty;
-import static org.junit.Assert.assertThat;
-import static org.mockito.Mockito.mock;
-import java.util.List;
-import java.util.UUID;
-import java.util.concurrent.TimeUnit;
-import javax.persistence.EntityManager;
-import javax.persistence.EntityManagerFactory;
-import org.hibernate.ejb.Ejb3Configuration;
-import org.jboss.dna.common.stats.Stopwatch;
-import org.jboss.dna.common.util.IoUtil;
-import org.jboss.dna.connector.store.jpa.models.basic.BasicModel;
-import org.jboss.dna.graph.BasicExecutionContext;
-import org.jboss.dna.graph.DnaLexicon;
-import org.jboss.dna.graph.ExecutionContext;
-import org.jboss.dna.graph.Graph;
-import org.jboss.dna.graph.Location;
-import org.jboss.dna.graph.Node;
-import org.jboss.dna.graph.Subgraph;
-import org.jboss.dna.graph.cache.CachePolicy;
-import org.jboss.dna.graph.properties.Name;
-import org.jboss.dna.graph.properties.Path;
-import org.jboss.dna.graph.properties.Property;
-import org.jboss.dna.graph.properties.Reference;
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-
-/**
- * Test the JpaConnection class using a {@link #getModel() model} for the entire set of
tests. To run these same methods using a
- * different {@link Model}, subclass this class and override the {@link #getModel()}
method to return the desired Model instance.
- *
- * @author Randall Hauch
- */
-public class JpaConnectionTest {
-
- private ExecutionContext context;
- private JpaConnection connection;
- private EntityManagerFactory factory;
- private EntityManager manager;
- private Model model;
- private CachePolicy cachePolicy;
- private UUID rootNodeUuid;
- private long largeValueSize;
- private boolean compressData;
- private boolean enforceReferentialIntegrity;
- private Graph graph;
- private String[] validLargeValues;
- private int numPropsOnEach;
-
- @Before
- public void beforeEach() throws Exception {
- context = new BasicExecutionContext();
- model = getModel();
- rootNodeUuid = UUID.randomUUID();
- largeValueSize = 2 ^ 10; // 1 kilobyte
- compressData = true;
- enforceReferentialIntegrity = true;
- numPropsOnEach = 0;
-
- // Load in the large value ...
- validLargeValues = new String[]
{IoUtil.read(getClass().getClassLoader().getResourceAsStream("LoremIpsum1.txt")),
-
IoUtil.read(getClass().getClassLoader().getResourceAsStream("LoremIpsum2.txt")),
-
IoUtil.read(getClass().getClassLoader().getResourceAsStream("LoremIpsum3.txt"))};
-
- // Connect to the database ...
- Ejb3Configuration configurator = new Ejb3Configuration();
- model.configure(configurator);
- configureDatabaseProperties(configurator);
- configurator.setProperty("hibernate.show_sql", "false");
- configurator.setProperty("hibernate.format_sql", "true");
- configurator.setProperty("hibernate.use_sql_comments",
"true");
- configurator.setProperty("hibernate.hbm2ddl.auto",
"create");
- factory = configurator.buildEntityManagerFactory();
- manager = factory.createEntityManager();
-
- // Create the connection ...
- cachePolicy = mock(CachePolicy.class);
- connection = new JpaConnection("source", cachePolicy, manager, model,
rootNodeUuid, largeValueSize, compressData,
- enforceReferentialIntegrity);
-
- // And create the graph ...
- graph = Graph.create(connection, context);
- }
-
- protected void configureDatabaseProperties( Ejb3Configuration configurator ) {
- configurator.setProperty("hibernate.dialect",
"org.hibernate.dialect.HSQLDialect");
- configurator.setProperty("hibernate.connection.driver_class",
"org.hsqldb.jdbcDriver");
- configurator.setProperty("hibernate.connection.username",
"sa");
- configurator.setProperty("hibernate.connection.password",
"");
- configurator.setProperty("hibernate.connection.url",
"jdbc:hsqldb:.");
- }
-
- @After
- public void afterEach() throws Exception {
- try {
- if (connection != null) connection.close();
- } finally {
- try {
- if (manager != null) manager.close();
- } finally {
- manager = null;
- if (factory != null) {
- try {
- factory.close();
- } finally {
- factory = null;
- }
- }
- }
- }
- }
-
- @Test
- public void shouldFindLargeValueContentFromFile() {
- for (int i = 0; i != validLargeValues.length; ++i) {
- assertThat(validLargeValues[i].startsWith((i + 1) + ". Lorem ipsum dolor
sit amet"), is(true));
- }
- }
-
- /**
- * Override this method in subclasses to create test cases that test other models.
- *
- * @return the model that should be used in the test
- */
- protected Model getModel() {
- return new BasicModel();
- }
-
- protected Path path( String path ) {
- return context.getValueFactories().getPathFactory().create(path);
- }
-
- protected Name name( String name ) {
- return context.getValueFactories().getNameFactory().create(name);
- }
-
- protected Property property( String name,
- Object... values ) {
- Name propName = name(name);
- return context.getPropertyFactory().create(propName, values);
- }
-
- protected Path.Segment child( String name ) {
- return context.getValueFactories().getPathFactory().createSegment(name);
- }
-
- @Test
- public void shouldAlwaysReadRootNodeByPath() {
- Node root = graph.getNodeAt("/");
- assertThat(root, is(notNullValue()));
- assertThat(root.getProperty(DnaLexicon.UUID).getFirstValue(),
is((Object)rootNodeUuid));
- assertThat(root.getLocation().getPath(), is(path("/")));
- assertThat(root.getLocation().getUuid(), is(rootNodeUuid));
- }
-
- @Test
- public void shouldAlwaysReadRootNodeByUuid() {
- Location location = new Location(rootNodeUuid);
- Node root = graph.getNodeAt(location);
- assertThat(root, is(notNullValue()));
- assertThat(root.getProperty(DnaLexicon.UUID).getFirstValue(),
is((Object)rootNodeUuid));
- assertThat(root.getLocation().getPath(), is(path("/")));
- assertThat(root.getLocation().getUuid(), is(rootNodeUuid));
- }
-
- @Test
- public void shouldSetPropertyOnRootNode() {
- graph.set("propA").to("valueA").on("/");
- // Now look up the node ...
- Node root = graph.getNodeAt("/");
- assertThat(root, is(notNullValue()));
- assertThat(root, hasProperty(DnaLexicon.UUID, rootNodeUuid));
- assertThat(root, hasProperty("propA", "valueA"));
- }
-
- @Test
- public void shouldAddChildUnderRootNode() {
- graph.batch().create("/a").with("propB",
"valueB").and("propC", "valueC").execute();
- // Now look up the root node ...
- Node root = graph.getNodeAt("/");
- assertThat(root, is(notNullValue()));
- assertThat(root, hasProperty(DnaLexicon.UUID, rootNodeUuid));
- assertThat(root.getChildren(), hasChild(child("a")));
-
- // Now look up node A ...
- Node nodeA = graph.getNodeAt("/a");
- assertThat(nodeA, is(notNullValue()));
- assertThat(nodeA, hasProperty("propB", "valueB"));
- assertThat(nodeA, hasProperty("propC", "valueC"));
- assertThat(nodeA.getChildren(), isEmpty());
- }
-
- @Test
- public void shouldAddChildrenOnRootNode() {
-
graph.batch().set("propA").to("valueA").on("/").and().create("/a").with("propB",
"valueB").and("propC",
"valueC").and().create("/b").with("propD",
-
"valueD").and("propE",
-
"valueE").execute();
- // Now look up the root node ...
- Node root = graph.getNodeAt("/");
- assertThat(root, is(notNullValue()));
- assertThat(root, hasProperty(DnaLexicon.UUID, rootNodeUuid));
- assertThat(root, hasProperty("propA", "valueA"));
- assertThat(root.getChildren(), hasChildren(child("a"),
child("b")));
-
- // Now look up node A ...
- Node nodeA = graph.getNodeAt("/a");
- assertThat(nodeA, is(notNullValue()));
- assertThat(nodeA, hasProperty("propB", "valueB"));
- assertThat(nodeA, hasProperty("propC", "valueC"));
- assertThat(nodeA.getChildren(), isEmpty());
-
- // Now look up node B ...
- Node nodeB = graph.getNodeAt("/b");
- assertThat(nodeB, is(notNullValue()));
- assertThat(nodeB, hasProperty("propD", "valueD"));
- assertThat(nodeB, hasProperty("propE", "valueE"));
- assertThat(nodeB.getChildren(), isEmpty());
-
- // Get the subgraph ...
- Subgraph subgraph = graph.getSubgraphOfDepth(3).at("/");
- assertThat(subgraph, is(notNullValue()));
- assertThat(subgraph.getNode("."), hasProperty(DnaLexicon.UUID,
rootNodeUuid));
- assertThat(subgraph.getNode("."), hasProperty("propA",
"valueA"));
- assertThat(subgraph.getNode(".").getChildren(),
hasChildren(child("a"), child("b")));
- assertThat(subgraph.getNode("a"), is(notNullValue()));
- assertThat(subgraph.getNode("a"), hasProperty("propB",
"valueB"));
- assertThat(subgraph.getNode("a"), hasProperty("propC",
"valueC"));
- assertThat(subgraph.getNode("a").getChildren(), isEmpty());
- assertThat(subgraph.getNode("b"), is(notNullValue()));
- assertThat(subgraph.getNode("b"), hasProperty("propD",
"valueD"));
- assertThat(subgraph.getNode("b"), hasProperty("propE",
"valueE"));
- assertThat(subgraph.getNode("b").getChildren(), isEmpty());
- }
-
- @Test
- public void shouldStoreManyPropertiesOnANode() {
- Graph.Create<Graph.Batch> create = graph.batch().create("/a");
- for (int i = 0; i != 100; ++i) {
- create = create.with("property" + i, "value" + i);
- }
- create.execute();
- // Now look up all the properties ...
- Node nodeA = graph.getNodeAt("/a");
- assertThat(nodeA, is(notNullValue()));
- for (int i = 0; i != 100; ++i) {
- assertThat(nodeA, hasProperty("property" + i, "value" +
i));
- }
- assertThat(nodeA.getChildren(), isEmpty());
- }
-
- @Test
- public void shouldUpdateSmallPropertiesOnANode() {
- // Create the property and add some properties (including 2 large values) ...
- Graph.Create<Graph.Batch> create = graph.batch().create("/a");
- for (int i = 0; i != 10; ++i) {
- create = create.with("property" + i, "value" + i);
- }
- create.execute();
-
- // Now look up all the properties ...
- Node nodeA = graph.getNodeAt("/a");
- assertThat(nodeA, is(notNullValue()));
- for (int i = 0; i != 10; ++i) {
- assertThat(nodeA, hasProperty("property" + i, "value" +
i));
- }
- assertThat(nodeA.getChildren(), isEmpty());
-
- // Now, remove some of the properties and add some others ...
- Graph.Batch batch = graph.batch();
- batch.remove("property0", "property1").on("/a");
- batch.set("property6").to("new valid 6").on("/a");
- batch.execute();
-
- // Re-read the properties ...
- nodeA = graph.getNodeAt("/a");
- assertThat(nodeA, is(notNullValue()));
- for (int i = 0; i != 10; ++i) {
- if (i == 0 || i == 1) {
- continue;
- } else if (i == 6) {
- assertThat(nodeA, hasProperty("property" + i, "new valid
6"));
- } else {
- assertThat(nodeA, hasProperty("property" + i, "value"
+ i));
- }
- }
- assertThat(nodeA.getChildren(), isEmpty());
-
- }
-
- @Test
- public void shouldUpdateLargePropertiesOnANode() {
- // Create the property and add some properties (including 2 large values) ...
- Graph.Create<Graph.Batch> create = graph.batch().create("/a");
- for (int i = 0; i != 100; ++i) {
- create = create.with("property" + i, "value" + i);
- }
- create = create.with("largeProperty1", validLargeValues[0]);
- create = create.with("largeProperty2", validLargeValues[1]);
- create.execute();
-
- // Now look up all the properties ...
- Node nodeA = graph.getNodeAt("/a");
- assertThat(nodeA, is(notNullValue()));
- for (int i = 0; i != 100; ++i) {
- assertThat(nodeA, hasProperty("property" + i, "value" +
i));
- }
- assertThat(nodeA, hasProperty("largeProperty1", validLargeValues[0]));
- assertThat(nodeA, hasProperty("largeProperty2", validLargeValues[1]));
- assertThat(nodeA.getChildren(), isEmpty());
-
- // Now, remove some of the properties and add some others ...
- Graph.Batch batch = graph.batch();
- batch.remove("largeProperty1", "property0",
"property1").on("/a");
- batch.set("property50").to("new valid
50").on("/a");
-
batch.set("largeProperty3").to(validLargeValues[2]).on("/a");
- batch.execute();
-
- // Re-read the properties ...
- nodeA = graph.getNodeAt("/a");
- assertThat(nodeA, is(notNullValue()));
- for (int i = 0; i != 100; ++i) {
- if (i == 0 || i == 1) {
- continue;
- } else if (i == 50) {
- assertThat(nodeA, hasProperty("property" + i, "new valid
50"));
- } else {
- assertThat(nodeA, hasProperty("property" + i, "value"
+ i));
- }
- }
- assertThat(nodeA, hasProperty("largeProperty2", validLargeValues[1]));
- assertThat(nodeA, hasProperty("largeProperty3", validLargeValues[2]));
- assertThat(nodeA.getChildren(), isEmpty());
-
- }
-
- @Test
- public void shouldGetOnePropertyOnNode() {
- Graph.Create<Graph.Batch> create = graph.batch().create("/a");
- for (int i = 0; i != 100; ++i) {
- create = create.with("property" + i, "value" + i);
- }
- create.execute();
- // Now get a single property ...
- Property p = graph.getProperty("property75").on("/a");
- assertThat(p, is(notNullValue()));
- assertThat(p.size(), is(1));
- assertThat(p.isSingle(), is(true));
- assertThat(p.getFirstValue().toString(), is("value75"));
- }
-
- @Test
- public void shouldCalculateNumberOfNodesInTreeCorrectly() {
- assertThat(numberNodesInTree(2, 2), is(7));
- assertThat(numberNodesInTree(2, 3), is(15));
- assertThat(numberNodesInTree(2, 4), is(31));
- assertThat(numberNodesInTree(3, 2), is(13));
- assertThat(numberNodesInTree(3, 3), is(40));
- assertThat(numberNodesInTree(3, 4), is(121));
- assertThat(numberNodesInTree(3, 5), is(364));
- assertThat(numberNodesInTree(3, 6), is(1093));
- assertThat(numberNodesInTree(3, 7), is(3280));
- assertThat(numberNodesInTree(3, 8), is(9841));
- assertThat(numberNodesInTree(3, 9), is(29524));
- assertThat(numberNodesInTree(4, 2), is(21));
- assertThat(numberNodesInTree(4, 3), is(85));
- assertThat(numberNodesInTree(4, 4), is(341));
- assertThat(numberNodesInTree(4, 5), is(1365));
- assertThat(numberNodesInTree(4, 6), is(5461));
- assertThat(numberNodesInTree(4, 7), is(21845));
- assertThat(numberNodesInTree(5, 3), is(156));
- assertThat(numberNodesInTree(5, 4), is(781));
- assertThat(numberNodesInTree(5, 5), is(3906));
- assertThat(numberNodesInTree(7, 3), is(400));
- assertThat(numberNodesInTree(7, 4), is(2801));
- assertThat(numberNodesInTree(7, 5), is(19608));
- assertThat(numberNodesInTree(8, 3), is(585));
- assertThat(numberNodesInTree(8, 4), is(4681));
- assertThat(numberNodesInTree(8, 5), is(37449));
- assertThat(numberNodesInTree(8, 6), is(299593));
- assertThat(numberNodesInTree(8, 7), is(2396745));
- assertThat(numberNodesInTree(10, 2), is(111));
- assertThat(numberNodesInTree(10, 3), is(1111));
- assertThat(numberNodesInTree(10, 4), is(11111));
- assertThat(numberNodesInTree(100, 1), is(101));
- assertThat(numberNodesInTree(200, 1), is(201));
- assertThat(numberNodesInTree(200, 2), is(40201));
- assertThat(numberNodesInTree(200, 3), is(8040201));
- assertThat(numberNodesInTree(1000, 1), is(1001));
- assertThat(numberNodesInTree(3000, 1), is(3001));
- }
-
- @Test
- public void shouldCreateDeepBranch() {
- createTree("", 1, 50, numPropsOnEach, "deep and narrow tree,
1x50", false, false);
- }
-
- @Test
- public void shouldCreateDeepBranchUsingOneBatch() {
- createTree("", 1, 50, numPropsOnEach, "deep and narrow tree,
1x50", true, false);
- }
-
- // @Test
- // public void shouldCreateBinaryTree() {
- // createTree("", 2, 8, "binary tree, 2x8", false,false);
- // }
-
- @Test
- public void shouldCreate10x2Tree() {
- createTree("", 10, 2, numPropsOnEach, null, false, false);
- }
-
- @Test
- public void shouldCreate10x2TreeUsingOneBatch() {
- createTree("", 10, 2, numPropsOnEach, null, true, false);
- }
-
- @Test
- public void shouldCreate10x2TreeWith10PropertiesOnEachNodeUsingOneBatch() {
- numPropsOnEach = 10;
- createTree("", 10, 2, numPropsOnEach, null, true, false);
- }
-
- // @Test
- // public void shouldCreate10x3DecimalTree() {
- // createTree("", 10, 3, numPropsOnEach, null, false, true);
- // }
- //
- // @Test
- // public void shouldCreate10x3DecimalTreeUsingOneBatch() {
- // createTree("", 10, 3, numPropsOnEach, null, true, false);
- // }
- //
- // @Test
- // public void
shouldCreate10x3DecimalTreeWith10PropertiesOnEachNodeCompressedUsingOneBatch() {
- // // compressData = true; the default
- // connection = new JpaConnection("source", cachePolicy, manager, model,
rootNodeUuid, largeValueSize, compressData);
- // graph = Graph.create(connection, context);
- //
- // numPropsOnEach = 10;
- // createTree("", 10, 3, numPropsOnEach, null, true, false);
- // }
- //
- // @Test
- // public void shouldCreate10x3DecimalTreeWith10PropertiesOnEachNodeUsingOneBatch()
{
- // compressData = false;
- // connection = new JpaConnection("source", cachePolicy, manager, model,
rootNodeUuid, largeValueSize, compressData);
- // graph = Graph.create(connection, context);
- //
- // numPropsOnEach = 10;
- // createTree("", 10, 3, numPropsOnEach, null, true, false);
- // }
-
- @Test
- public void shouldReadNodes() {
- // Create the tree (at total of 40 nodes, plus the extra 6 added later)...
- // /
- // /node1
- // /node1/node1
- // /node1/node1/node1
- // /node1/node1/node2
- // /node1/node1/node3
- // /node1/node2
- // /node1/node2/node1
- // /node1/node2/node2
- // /node1/node2/node3
- // /node1/node3
- // /node1/node3/node1
- // /node1/node3/node2
- // /node1/node3/node3
- // /node2
- // /node2/node1
- // /node2/node1/node1
- // /node2/node1/node2
- // /node2/node1/node3
- // /node2/node2
- // /node2/node2/node1
- // /node2/node2/node2
- // /node2/node2/node3
- // /node2/node3
- // /node2/node3/node1
- // /node2/node3/node2
- // /node2/node3/node3
- // /node3
- // /node3/node1
- // /node3/node1/node1
- // /node3/node1/node2
- // /node3/node1/node3
- // /node3/node2
- // /node3/node2/node1
- // /node3/node2/node2
- // /node3/node2/node3
- // /node3/node3
- // /node3/node3/node1
- // /node3/node3/node2
- // /node3/node3/node3
- // /secondBranch1
- // /secondBranch1/secondBranch1
- // /secondBranch1/secondBranch2
- // /secondBranch2
- // /secondBranch2/secondBranch1
- // /secondBranch2/secondBranch2
-
- numPropsOnEach = 3;
- createTree("", 3, 3, numPropsOnEach, null, true, false);
- assertThat(graph.getChildren().of("/node1"),
hasChildren(child("node1"), child("node2"),
child("node3")));
- assertThat(graph.getChildren().of("/node1/node1"),
hasChildren(child("node1"), child("node2"),
child("node3")));
- assertThat(graph.getChildren().of("/node1/node2"),
hasChildren(child("node1"), child("node2"),
child("node3")));
- assertThat(graph.getChildren().of("/node1/node3"),
hasChildren(child("node1"), child("node2"),
child("node3")));
- assertThat(graph.getChildren().of("/node1/node3/node1"), isEmpty());
-
- assertThat(graph.getChildren().of("/node2"),
hasChildren(child("node1"), child("node2"),
child("node3")));
- assertThat(graph.getChildren().of("/node2/node1"),
hasChildren(child("node1"), child("node2"),
child("node3")));
- assertThat(graph.getChildren().of("/node2/node2"),
hasChildren(child("node1"), child("node2"),
child("node3")));
- assertThat(graph.getChildren().of("/node2/node3"),
hasChildren(child("node1"), child("node2"),
child("node3")));
- assertThat(graph.getChildren().of("/node2/node3/node1"), isEmpty());
-
- assertThat(graph.getChildren().of("/node3"),
hasChildren(child("node1"), child("node2"),
child("node3")));
- assertThat(graph.getChildren().of("/node3/node1"),
hasChildren(child("node1"), child("node2"),
child("node3")));
- assertThat(graph.getChildren().of("/node3/node2"),
hasChildren(child("node1"), child("node2"),
child("node3")));
- assertThat(graph.getChildren().of("/node3/node3"),
hasChildren(child("node1"), child("node2"),
child("node3")));
- assertThat(graph.getChildren().of("/node3/node3/node1"), isEmpty());
-
- Subgraph subgraph = graph.getSubgraphOfDepth(2).at("/node3/node2");
- assertThat(subgraph, is(notNullValue()));
- assertThat(subgraph.getNode(".").getChildren(),
hasChildren(child("node1"), child("node2"),
child("node3")));
- assertThat(subgraph.getNode("."), hasProperty("property1",
"The quick brown fox jumped over the moon. What? "));
- assertThat(subgraph.getNode("."), hasProperty("property2",
"The quick brown fox jumped over the moon. What? "));
- assertThat(subgraph.getNode("."), hasProperty("property3",
"The quick brown fox jumped over the moon. What? "));
- assertThat(subgraph.getNode("node1").getChildren(), isEmpty());
- assertThat(subgraph.getNode("node1"),
hasProperty("property1", "The quick brown fox jumped over the moon. What?
"));
- assertThat(subgraph.getNode("node1"),
hasProperty("property2", "The quick brown fox jumped over the moon. What?
"));
- assertThat(subgraph.getNode("node1"),
hasProperty("property3", "The quick brown fox jumped over the moon. What?
"));
- assertThat(subgraph.getNode("node2").getChildren(), isEmpty());
- assertThat(subgraph.getNode("node2"),
hasProperty("property1", "The quick brown fox jumped over the moon. What?
"));
- assertThat(subgraph.getNode("node2"),
hasProperty("property2", "The quick brown fox jumped over the moon. What?
"));
- assertThat(subgraph.getNode("node2"),
hasProperty("property3", "The quick brown fox jumped over the moon. What?
"));
- assertThat(subgraph.getNode("node3").getChildren(), isEmpty());
- assertThat(subgraph.getNode("node3"),
hasProperty("property1", "The quick brown fox jumped over the moon. What?
"));
- assertThat(subgraph.getNode("node3"),
hasProperty("property2", "The quick brown fox jumped over the moon. What?
"));
- assertThat(subgraph.getNode("node3"),
hasProperty("property3", "The quick brown fox jumped over the moon. What?
"));
-
- subgraph = graph.getSubgraphOfDepth(2).at("/node3");
- assertThat(subgraph, is(notNullValue()));
- assertThat(subgraph.getNode(".").getChildren(),
hasChildren(child("node1"), child("node2"),
child("node3")));
- assertThat(subgraph.getNode("."), hasProperty("property1",
"The quick brown fox jumped over the moon. What? "));
- assertThat(subgraph.getNode("."), hasProperty("property2",
"The quick brown fox jumped over the moon. What? "));
- assertThat(subgraph.getNode("."), hasProperty("property3",
"The quick brown fox jumped over the moon. What? "));
- assertThat(subgraph.getNode("node1").getChildren(), isEmpty());
- assertThat(subgraph.getNode("node1"),
hasProperty("property1", "The quick brown fox jumped over the moon. What?
"));
- assertThat(subgraph.getNode("node1"),
hasProperty("property2", "The quick brown fox jumped over the moon. What?
"));
- assertThat(subgraph.getNode("node1"),
hasProperty("property3", "The quick brown fox jumped over the moon. What?
"));
- assertThat(subgraph.getNode("node2").getChildren(), isEmpty());
- assertThat(subgraph.getNode("node2"),
hasProperty("property1", "The quick brown fox jumped over the moon. What?
"));
- assertThat(subgraph.getNode("node2"),
hasProperty("property2", "The quick brown fox jumped over the moon. What?
"));
- assertThat(subgraph.getNode("node2"),
hasProperty("property3", "The quick brown fox jumped over the moon. What?
"));
- assertThat(subgraph.getNode("node3").getChildren(), isEmpty());
- assertThat(subgraph.getNode("node3"),
hasProperty("property1", "The quick brown fox jumped over the moon. What?
"));
- assertThat(subgraph.getNode("node3"),
hasProperty("property2", "The quick brown fox jumped over the moon. What?
"));
- assertThat(subgraph.getNode("node3"),
hasProperty("property3", "The quick brown fox jumped over the moon. What?
"));
- }
-
- @Test
- public void shouldDeleteNodes() {
- // Create the tree (at total of 40 nodes, plus the extra 6 added later)...
- // /
- // /node1
- // /node1/node1
- // /node1/node1/node1
- // /node1/node1/node2
- // /node1/node1/node3
- // /node1/node2
- // /node1/node2/node1
- // /node1/node2/node2
- // /node1/node2/node3
- // /node1/node3
- // /node1/node3/node1
- // /node1/node3/node2
- // /node1/node3/node3
- // /node2
- // /node2/node1
- // /node2/node1/node1
- // /node2/node1/node2
- // /node2/node1/node3
- // /node2/node2
- // /node2/node2/node1
- // /node2/node2/node2
- // /node2/node2/node3
- // /node2/node3
- // /node2/node3/node1
- // /node2/node3/node2
- // /node2/node3/node3
- // /node3
- // /node3/node1
- // /node3/node1/node1
- // /node3/node1/node2
- // /node3/node1/node3
- // /node3/node2
- // /node3/node2/node1
- // /node3/node2/node2
- // /node3/node2/node3
- // /node3/node3
- // /node3/node3/node1
- // /node3/node3/node2
- // /node3/node3/node3
- // /secondBranch1
- // /secondBranch1/secondBranch1
- // /secondBranch1/secondBranch2
- // /secondBranch2
- // /secondBranch2/secondBranch1
- // /secondBranch2/secondBranch2
-
- numPropsOnEach = 3;
- createTree("", 3, 3, numPropsOnEach, null, true, false);
-
- // Delete two branches ...
- graph.delete("/node2/node2").and().delete("/node3/node1");
-
- assertThat(graph.getChildren().of("/node1"),
hasChildren(child("node1"), child("node2"),
child("node3")));
- assertThat(graph.getChildren().of("/node1/node1"),
hasChildren(child("node1"), child("node2"),
child("node3")));
- assertThat(graph.getChildren().of("/node1/node2"),
hasChildren(child("node1"), child("node2"),
child("node3")));
- assertThat(graph.getChildren().of("/node1/node3"),
hasChildren(child("node1"), child("node2"),
child("node3")));
- assertThat(graph.getChildren().of("/node1/node3/node1"),
hasChildren());
-
- assertThat(graph.getChildren().of("/node2"),
hasChildren(child("node1"), child("node3")));
- assertThat(graph.getChildren().of("/node2/node1"),
hasChildren(child("node1"), child("node2"),
child("node3")));
- assertThat(graph.getChildren().of("/node2/node3"),
hasChildren(child("node1"), child("node2"),
child("node3")));
- assertThat(graph.getChildren().of("/node2/node3/node1"),
hasChildren());
-
- assertThat(graph.getChildren().of("/node3"),
hasChildren(child("node2"), child("node3")));
- assertThat(graph.getChildren().of("/node3/node2"),
hasChildren(child("node1"), child("node2"),
child("node3")));
- assertThat(graph.getChildren().of("/node3/node3"),
hasChildren(child("node1"), child("node2"),
child("node3")));
- assertThat(graph.getChildren().of("/node3/node3/node1"),
hasChildren());
-
- Subgraph subgraph = graph.getSubgraphOfDepth(3).at("/");
- assertThat(subgraph, is(notNullValue()));
- assertThat(subgraph.getNode(".").getChildren(),
hasChildren(child("node1"), child("node3")));
- assertThat(subgraph.getNode("node1").getChildren(),
hasChildren(child("node1"), child("node2"),
child("node3")));
- assertThat(subgraph.getNode("node1"),
hasProperty("property1", "The quick brown fox jumped over the moon. What?
"));
- assertThat(subgraph.getNode("node1"),
hasProperty("property2", "The quick brown fox jumped over the moon. What?
"));
- assertThat(subgraph.getNode("node1"),
hasProperty("property3", "The quick brown fox jumped over the moon. What?
"));
- assertThat(subgraph.getNode("node2").getChildren(),
hasChildren(child("node1"), child("node3")));
- assertThat(subgraph.getNode("node2"),
hasProperty("property1", "The quick brown fox jumped over the moon. What?
"));
- assertThat(subgraph.getNode("node2"),
hasProperty("property2", "The quick brown fox jumped over the moon. What?
"));
- assertThat(subgraph.getNode("node2"),
hasProperty("property3", "The quick brown fox jumped over the moon. What?
"));
- assertThat(subgraph.getNode("node3").getChildren(),
hasChildren(child("node2"), child("node3")));
- assertThat(subgraph.getNode("node3"),
hasProperty("property1", "The quick brown fox jumped over the moon. What?
"));
- assertThat(subgraph.getNode("node3"),
hasProperty("property2", "The quick brown fox jumped over the moon. What?
"));
- assertThat(subgraph.getNode("node3"),
hasProperty("property3", "The quick brown fox jumped over the moon. What?
"));
-
- subgraph = graph.getSubgraphOfDepth(2).at("/node3");
- assertThat(subgraph, is(notNullValue()));
- assertThat(subgraph.getNode(".").getChildren(),
hasChildren(child("node2"), child("node3")));
- assertThat(subgraph.getNode("."), hasProperty("property1",
"The quick brown fox jumped over the moon. What? "));
- assertThat(subgraph.getNode("."), hasProperty("property2",
"The quick brown fox jumped over the moon. What? "));
- assertThat(subgraph.getNode("."), hasProperty("property3",
"The quick brown fox jumped over the moon. What? "));
- assertThat(subgraph.getNode("node2").getChildren(), isEmpty());
- assertThat(subgraph.getNode("node2"),
hasProperty("property1", "The quick brown fox jumped over the moon. What?
"));
- assertThat(subgraph.getNode("node2"),
hasProperty("property2", "The quick brown fox jumped over the moon. What?
"));
- assertThat(subgraph.getNode("node2"),
hasProperty("property3", "The quick brown fox jumped over the moon. What?
"));
- assertThat(subgraph.getNode("node3").getChildren(), isEmpty());
- assertThat(subgraph.getNode("node3"),
hasProperty("property1", "The quick brown fox jumped over the moon. What?
"));
- assertThat(subgraph.getNode("node3"),
hasProperty("property2", "The quick brown fox jumped over the moon. What?
"));
- assertThat(subgraph.getNode("node3"),
hasProperty("property3", "The quick brown fox jumped over the moon. What?
"));
- }
-
- @Test
- public void shouldCopyNodeWithChildren() {
- // Create the tree (at total of 40 nodes, plus the extra 6 added later)...
- // /
- // /node1
- // /node1/node1
- // /node1/node1/node1
- // /node1/node1/node2
- // /node1/node1/node3
- // /node1/node2
- // /node1/node2/node1
- // /node1/node2/node2
- // /node1/node2/node3
- // /node1/node3
- // /node1/node3/node1
- // /node1/node3/node2
- // /node1/node3/node3
- // /node2
- // /node2/node1
- // /node2/node1/node1
- // /node2/node1/node2
- // /node2/node1/node3
- // /node2/node2
- // /node2/node2/node1
- // /node2/node2/node2
- // /node2/node2/node3
- // /node2/node3
- // /node2/node3/node1
- // /node2/node3/node2
- // /node2/node3/node3
- // /node3
- // /node3/node1
- // /node3/node1/node1
- // /node3/node1/node2
- // /node3/node1/node3
- // /node3/node2
- // /node3/node2/node1
- // /node3/node2/node2
- // /node3/node2/node3
- // /node3/node3
- // /node3/node3/node1
- // /node3/node3/node2
- // /node3/node3/node3
- // /secondBranch1
- // /secondBranch1/secondBranch1
- // /secondBranch1/secondBranch2
- // /secondBranch2
- // /secondBranch2/secondBranch1
- // /secondBranch2/secondBranch2
-
- numPropsOnEach = 3;
- createTree("", 3, 3, numPropsOnEach, null, true, false);
-
- // Create some references between nodes that aren't involved with the copy
...
-
graph.set("refProp").on("/node1").to(graph.getNodeAt("/node1/node3"));
-
graph.set("refProp").on("/node1/node1").to(graph.getNodeAt("/node3/node2"));
// will soon be /node3/node2[1]
-
- // Create some "inward" references from nodes that are NOT being copied
to nodes that are being copied ...
-
graph.set("refProp").on("/node1/node2").to(graph.getNodeAt("/node2/node2"));
-
graph.set("refProp").on("/node1/node3").to(graph.getNodeAt("/node2/node2"));
-
- // Create some "outward" references from nodes that are being copied to
nodes that are NOT being copied ...
-
graph.set("refProp").on("/node2/node1").to(graph.getNodeAt("/node1/node1"));
-
graph.set("refProp").on("/node2/node3").to(graph.getNodeAt("/node1/node2"));
-
- // Create some "internal" references between nodes that are being
copied ...
-
graph.set("refProp").on("/node2/node2").to(graph.getNodeAt("/node2/node2/node1"));
-
graph.set("refProp").on("/node2/node3/node1").to(graph.getNodeAt("/node2/node2/node1"));
-
- // Verify the references are there ...
- assertReference("/node1", "refProp",
"/node1/node3");
- assertReference("/node1/node1", "refProp",
"/node3/node2");
- assertReference("/node1/node2", "refProp",
"/node2/node2");
- assertReference("/node1/node3", "refProp",
"/node2/node2");
- assertReference("/node2/node1", "refProp",
"/node1/node1");
- assertReference("/node2/node3", "refProp",
"/node1/node2");
- assertReference("/node2/node2", "refProp",
"/node2/node2/node1");
- assertReference("/node2/node3/node1", "refProp",
"/node2/node2/node1");
-
- // Copy a branches ...
- graph.copy("/node2").into("/node3");
-
- // Verify the references are still there ...
- assertReference("/node1", "refProp",
"/node1/node3");
- assertReference("/node1/node1", "refProp",
"/node3/node2[1]");
- assertReference("/node1/node2", "refProp",
"/node2/node2");
- assertReference("/node1/node3", "refProp",
"/node2/node2");
- assertReference("/node2/node1", "refProp",
"/node1/node1");
- assertReference("/node2/node3", "refProp",
"/node1/node2");
- assertReference("/node2/node2", "refProp",
"/node2/node2/node1");
- assertReference("/node2/node3/node1", "refProp",
"/node2/node2/node1");
-
- // And verify that we have a few new (outward and internal) references in the
copy ...
- assertReference("/node3/node2[2]/node1", "refProp",
"/node1/node1"); // outward
- assertReference("/node3/node2[2]/node3", "refProp",
"/node1/node2"); // outward
- assertReference("/node3/node2[2]/node2", "refProp",
"/node3/node2[2]/node2/node1"); // internal
- assertReference("/node3/node2[2]/node3/node1", "refProp",
"/node3/node2[2]/node2/node1"); // internal
-
- // Now assert the structure ...
- assertThat(graph.getChildren().of("/node1"),
hasChildren(child("node1"), child("node2"),
child("node3")));
- assertThat(graph.getChildren().of("/node1/node1"),
hasChildren(child("node1"), child("node2"),
child("node3")));
- assertThat(graph.getChildren().of("/node1/node2"),
hasChildren(child("node1"), child("node2"),
child("node3")));
- assertThat(graph.getChildren().of("/node1/node3"),
hasChildren(child("node1"), child("node2"),
child("node3")));
- assertThat(graph.getChildren().of("/node1/node3/node1"),
hasChildren());
-
- // The original of the copy should still exist ...
- assertThat(graph.getChildren().of("/node2"),
hasChildren(child("node1"), child("node3")));
- assertThat(graph.getChildren().of("/node2/node1"),
hasChildren(child("node1"), child("node2"),
child("node3")));
- assertThat(graph.getChildren().of("/node2/node3"),
hasChildren(child("node1"), child("node2"),
child("node3")));
- assertThat(graph.getChildren().of("/node2/node3/node1"),
hasChildren());
-
- assertThat(graph.getChildren().of("/node3"),
hasChildren(child("node2[1]"), child("node3"),
child("node2[2]")));
- assertThat(graph.getChildren().of("/node3/node2[1]"),
hasChildren(child("node1"), child("node2"),
child("node3")));
- assertThat(graph.getChildren().of("/node3/node3"),
hasChildren(child("node1"), child("node2"),
child("node3")));
- assertThat(graph.getChildren().of("/node3/node3/node1"),
hasChildren());
- assertThat(graph.getChildren().of("/node3/node2[2]"),
hasChildren(child("node1"), child("node2"),
child("node3")));
- assertThat(graph.getChildren().of("/node3/node2[2]/node1"),
hasChildren(child("node1"), child("node2"),
child("node3")));
- assertThat(graph.getChildren().of("/node3/node2[2]/node2"),
hasChildren(child("node1"), child("node2"),
child("node3")));
- assertThat(graph.getChildren().of("/node3/node2[2]/node3"),
hasChildren(child("node1"), child("node2"),
child("node3")));
- assertThat(graph.getChildren().of("/node3/node2[2]/node1/node1"),
hasChildren());
-
- Subgraph subgraph = graph.getSubgraphOfDepth(4).at("/node3");
- assertThat(subgraph, is(notNullValue()));
- assertThat(subgraph.getNode(".").getChildren(),
hasChildren(child("node2"), child("node3")));
- assertThat(subgraph.getNode("."), hasProperty("property1",
"The quick brown fox jumped over the moon. What? "));
- assertThat(subgraph.getNode("."), hasProperty("property2",
"The quick brown fox jumped over the moon. What? "));
- assertThat(subgraph.getNode("."), hasProperty("property3",
"The quick brown fox jumped over the moon. What? "));
- assertThat(subgraph.getNode("node2[1]").getChildren(),
hasChildren(child("node1"), child("node2"),
child("node3")));
- assertThat(subgraph.getNode("node2[1]"),
hasProperty("property1", "The quick brown fox jumped over the moon. What?
"));
- assertThat(subgraph.getNode("node2[1]"),
hasProperty("property2", "The quick brown fox jumped over the moon. What?
"));
- assertThat(subgraph.getNode("node2[1]"),
hasProperty("property3", "The quick brown fox jumped over the moon. What?
"));
- assertThat(subgraph.getNode("node3").getChildren(), isEmpty());
- assertThat(subgraph.getNode("node3"),
hasProperty("property1", "The quick brown fox jumped over the moon. What?
"));
- assertThat(subgraph.getNode("node3"),
hasProperty("property2", "The quick brown fox jumped over the moon. What?
"));
- assertThat(subgraph.getNode("node3"),
hasProperty("property3", "The quick brown fox jumped over the moon. What?
"));
- assertThat(subgraph.getNode("node2[2]").getChildren(),
hasChildren(child("node1"), child("node2"),
child("node3")));
- assertThat(subgraph.getNode("node2[2]"),
hasProperty("property1", "The quick brown fox jumped over the moon. What?
"));
- assertThat(subgraph.getNode("node2[2]"),
hasProperty("property2", "The quick brown fox jumped over the moon. What?
"));
- assertThat(subgraph.getNode("node2[2]"),
hasProperty("property3", "The quick brown fox jumped over the moon. What?
"));
- assertThat(subgraph.getNode("node2[2]/node1").getChildren(),
hasChildren(child("node1"), child("node2"),
child("node3")));
- assertThat(subgraph.getNode("node2[2]/node1"),
hasProperty("property1",
- "The quick brown
fox jumped over the moon. What? "));
- assertThat(subgraph.getNode("node2[2]/node1"),
hasProperty("property2",
- "The quick brown
fox jumped over the moon. What? "));
- assertThat(subgraph.getNode("node2[2]/node1"),
hasProperty("property3",
- "The quick brown
fox jumped over the moon. What? "));
- assertThat(subgraph.getNode("node2[2]/node1/node1").getChildren(),
isEmpty());
- assertThat(subgraph.getNode("node2[2]/node1/node1"),
hasProperty("property1",
- "The quick
brown fox jumped over the moon. What? "));
- assertThat(subgraph.getNode("node2[2]/node1/node1"),
hasProperty("property2",
- "The quick
brown fox jumped over the moon. What? "));
- assertThat(subgraph.getNode("node2[2]/node1/node1"),
hasProperty("property3",
- "The quick
brown fox jumped over the moon. What? "));
- assertThat(subgraph.getNode("node2[2]/node1/node2").getChildren(),
isEmpty());
- assertThat(subgraph.getNode("node2[2]/node1/node2"),
hasProperty("property1",
- "The quick
brown fox jumped over the moon. What? "));
- assertThat(subgraph.getNode("node2[2]/node1/node2"),
hasProperty("property2",
- "The quick
brown fox jumped over the moon. What? "));
- assertThat(subgraph.getNode("node2[2]/node1/node2"),
hasProperty("property3",
- "The quick
brown fox jumped over the moon. What? "));
- assertThat(subgraph.getNode("node2[2]/node1/node3").getChildren(),
isEmpty());
- assertThat(subgraph.getNode("node2[2]/node1/node3"),
hasProperty("property1",
- "The quick
brown fox jumped over the moon. What? "));
- assertThat(subgraph.getNode("node2[2]/node1/node3"),
hasProperty("property2",
- "The quick
brown fox jumped over the moon. What? "));
- assertThat(subgraph.getNode("node2[2]/node1/node3"),
hasProperty("property3",
- "The quick
brown fox jumped over the moon. What? "));
- assertThat(subgraph.getNode("node2[2]/node2").getChildren(),
hasChildren(child("node1"), child("node2"),
child("node3")));
- assertThat(subgraph.getNode("node2[2]/node2"),
hasProperty("property1",
- "The quick brown
fox jumped over the moon. What? "));
- assertThat(subgraph.getNode("node2[2]/node2"),
hasProperty("property2",
- "The quick brown
fox jumped over the moon. What? "));
- assertThat(subgraph.getNode("node2[2]/node2"),
hasProperty("property3",
- "The quick brown
fox jumped over the moon. What? "));
- assertThat(subgraph.getNode("node2[2]/node2/node1").getChildren(),
isEmpty());
- assertThat(subgraph.getNode("node2[2]/node2/node1"),
hasProperty("property1",
- "The quick
brown fox jumped over the moon. What? "));
- assertThat(subgraph.getNode("node2[2]/node2/node1"),
hasProperty("property2",
- "The quick
brown fox jumped over the moon. What? "));
- assertThat(subgraph.getNode("node2[2]/node2/node1"),
hasProperty("property3",
- "The quick
brown fox jumped over the moon. What? "));
- assertThat(subgraph.getNode("node2[2]/node2/node2").getChildren(),
isEmpty());
- assertThat(subgraph.getNode("node2[2]/node2/node2"),
hasProperty("property1",
- "The quick
brown fox jumped over the moon. What? "));
- assertThat(subgraph.getNode("node2[2]/node2/node2"),
hasProperty("property2",
- "The quick
brown fox jumped over the moon. What? "));
- assertThat(subgraph.getNode("node2[2]/node2/node2"),
hasProperty("property3",
- "The quick
brown fox jumped over the moon. What? "));
- assertThat(subgraph.getNode("node2[2]/node2/node3").getChildren(),
isEmpty());
- assertThat(subgraph.getNode("node2[2]/node2/node3"),
hasProperty("property1",
- "The quick
brown fox jumped over the moon. What? "));
- assertThat(subgraph.getNode("node2[2]/node2/node3"),
hasProperty("property2",
- "The quick
brown fox jumped over the moon. What? "));
- assertThat(subgraph.getNode("node2[2]/node2/node3"),
hasProperty("property3",
- "The quick
brown fox jumped over the moon. What? "));
- assertThat(subgraph.getNode("node2[2]/node3").getChildren(),
hasChildren(child("node1"), child("node2"),
child("node3")));
- assertThat(subgraph.getNode("node2[2]/node3"),
hasProperty("property1",
- "The quick brown
fox jumped over the moon. What? "));
- assertThat(subgraph.getNode("node2[2]/node3"),
hasProperty("property2",
- "The quick brown
fox jumped over the moon. What? "));
- assertThat(subgraph.getNode("node2[2]/node3"),
hasProperty("property3",
- "The quick brown
fox jumped over the moon. What? "));
- assertThat(subgraph.getNode("node2[2]/node3/node1").getChildren(),
isEmpty());
- assertThat(subgraph.getNode("node2[2]/node3/node1"),
hasProperty("property1",
- "The quick
brown fox jumped over the moon. What? "));
- assertThat(subgraph.getNode("node2[2]/node3/node1"),
hasProperty("property2",
- "The quick
brown fox jumped over the moon. What? "));
- assertThat(subgraph.getNode("node2[2]/node3/node1"),
hasProperty("property3",
- "The quick
brown fox jumped over the moon. What? "));
- assertThat(subgraph.getNode("node2[2]/node3/node2").getChildren(),
isEmpty());
- assertThat(subgraph.getNode("node2[2]/node3/node2"),
hasProperty("property1",
- "The quick
brown fox jumped over the moon. What? "));
- assertThat(subgraph.getNode("node2[2]/node3/node2"),
hasProperty("property2",
- "The quick
brown fox jumped over the moon. What? "));
- assertThat(subgraph.getNode("node2[2]/node3/node2"),
hasProperty("property3",
- "The quick
brown fox jumped over the moon. What? "));
- assertThat(subgraph.getNode("node2[2]/node3/node3").getChildren(),
isEmpty());
- assertThat(subgraph.getNode("node2[2]/node3/node3"),
hasProperty("property1",
- "The quick
brown fox jumped over the moon. What? "));
- assertThat(subgraph.getNode("node2[2]/node3/node3"),
hasProperty("property2",
- "The quick
brown fox jumped over the moon. What? "));
- assertThat(subgraph.getNode("node2[2]/node3/node3"),
hasProperty("property3",
- "The quick
brown fox jumped over the moon. What? "));
- }
-
- protected void assertReference( String fromNodePath,
- String propertyName,
- String... toNodePath ) {
- Object[] values =
graph.getProperty(propertyName).on(fromNodePath).getValuesAsArray();
- assertThat(values.length, is(toNodePath.length));
- for (int i = 0; i != values.length; ++i) {
- Object value = values[i];
- assertThat(value, is(instanceOf(Reference.class)));
- Reference ref = (Reference)value;
- assertThat(graph.resolve(ref), is(graph.getNodeAt(toNodePath[i])));
- }
- }
-
- @Test
- public void shouldReadRangeOfChildren() {
- // Create a shallow tree with many children under one node ...
- // /
- // /node1
- // /node1/node1
- // /node1/node2
- // ...
- // /node1/node10
- // /node1/secondBranch1
- // ...
-
- graph.batch().create("/node1").with("prop1",
"value1").and("prop2", "value2").execute();
- numPropsOnEach = 3;
- createTree("/node1", 10, 2, numPropsOnEach, null, true, false);
-
- // Verify that the children were created ...
- List<Location> allChildren = graph.getChildren().of("/node1");
- assertThat(allChildren, hasChildren(child("node1"),
- child("node2"),
- child("node3"),
- child("node4"),
- child("node5"),
- child("node6"),
- child("node7"),
- child("node8"),
- child("node9"),
- child("node10")));
-
- // Now test reading children in various ranges ...
- List<Location> children =
graph.getChildren().inBlockOf(4).startingAt(4).under("/node1");
- assertThat(children, is(notNullValue()));
- assertThat(children, hasChildren(child("node5"),
child("node6"), child("node7"), child("node8")));
-
- children =
graph.getChildren().inBlockOf(3).startingAt(4).under("/node1");
- assertThat(children, is(notNullValue()));
- assertThat(children, hasChildren(child("node5"),
child("node6"), child("node7")));
-
- children =
graph.getChildren().inBlockOf(10).startingAt(7).under("/node1");
- assertThat(children, is(notNullValue()));
- assertThat(children, hasChildren(child("node8"),
child("node9"), child("node10")));
- }
-
- @Test
- public void shouldReadNextBlockOfChildren() {
- // Create a shallow tree with many children under one node ...
- // /
- // /node1
- // /node1/node1
- // /node1/node2
- // ...
- // /node1/node10
- // /node1/secondBranch1
- // ...
-
- graph.batch().create("/node1").with("prop1",
"value1").and("prop2", "value2").execute();
- numPropsOnEach = 3;
- createTree("/node1", 10, 2, numPropsOnEach, null, true, false);
-
- // Verify that the children were created ...
- List<Location> allChildren = graph.getChildren().of("/node1");
- assertThat(allChildren, hasChildren(child("node1"),
- child("node2"),
- child("node3"),
- child("node4"),
- child("node5"),
- child("node6"),
- child("node7"),
- child("node8"),
- child("node9"),
- child("node10")));
-
- // Now test reading children in various ranges ...
- Location node4 = allChildren.get(3);
- List<Location> children =
graph.getChildren().inBlockOf(4).startingAfter(node4);
- assertThat(children, is(notNullValue()));
- assertThat(children, hasChildren(child("node5"),
child("node6"), child("node7"), child("node8")));
-
- children = graph.getChildren().inBlockOf(3).startingAfter(node4);
- assertThat(children, is(notNullValue()));
- assertThat(children, hasChildren(child("node5"),
child("node6"), child("node7")));
-
- Location node7 = allChildren.get(6);
- children = graph.getChildren().inBlockOf(10).startingAfter(node7);
- assertThat(children, is(notNullValue()));
- assertThat(children, hasChildren(child("node8"),
child("node9"), child("node10")));
- }
-
- protected int createTree( String initialPath,
- int numberPerDepth,
- int depth,
- int numProps,
- String description,
- boolean oneBatch,
- boolean printIntermediate ) {
- long totalNumber = numberNodesInTree(numberPerDepth, depth) - 1; // this method
doesn't create the root
- if (description == null) description = "" + numberPerDepth +
"x" + depth + " tree with " + numProps
- + " properties per node" +
(compressData ? " (compressed)" : "");
- System.out.println(description + " (" + totalNumber + "
nodes):");
- long totalNumberCreated = 0;
- String batchStr = "batch ";
- Graph.Batch batch = oneBatch ? graph.batch() : null;
- Stopwatch sw = new Stopwatch();
- if (batch != null) {
- totalNumberCreated += createChildren(batch, initialPath, "node",
numberPerDepth, numProps, depth, printIntermediate);
- sw.start();
- batch.execute();
- } else {
- batchStr = "";
- sw.start();
- totalNumberCreated += createChildren(null, initialPath, "node",
numberPerDepth, numProps, depth, printIntermediate);
- }
- sw.stop();
- long totalDurationInMicroseconds =
TimeUnit.NANOSECONDS.toMicros(sw.getTotalDuration().longValue());
- long avgDuration = totalDurationInMicroseconds / totalNumber / 1000L;
- String units = " millisecond(s)";
- if (avgDuration == 0L) {
- avgDuration = totalDurationInMicroseconds / totalNumber;
- units = " microsecond(s)";
- }
- System.out.println(" Total = " + sw.getTotalDuration() + ";
avg = " + avgDuration + units);
-
- // Perform second batch ...
- batch = graph.batch();
- totalNumberCreated += createChildren(batch, initialPath,
"secondBranch", 2, numProps, 2, printIntermediate);
- sw = new Stopwatch();
- sw.start();
- batch.execute();
- sw.stop();
- totalDurationInMicroseconds =
TimeUnit.NANOSECONDS.toMicros(sw.getTotalDuration().longValue());
- avgDuration = totalDurationInMicroseconds / totalNumber / 1000L;
- units = " millisecond(s)";
- if (avgDuration == 0L) {
- avgDuration = totalDurationInMicroseconds / totalNumber;
- units = " microsecond(s)";
- }
- System.out.println(" Final " + batchStr + "total = " +
sw.getTotalDuration() + "; avg = " + avgDuration + units);
- assertThat(totalNumberCreated, is(totalNumber + numberNodesInTree(2, 2) - 1));
- return (int)totalNumberCreated;
- }
-
- protected int createChildren( Graph.Batch useBatch,
- String parentPath,
- String nodePrefix,
- int number,
- int numProps,
- int depthRemaining,
- boolean printIntermediateStatus ) {
- int numberCreated = 0;
- Graph.Batch batch = useBatch;
- if (batch == null) batch = graph.batch();
- for (int i = 0; i != number; ++i) {
- String path = parentPath + "/" + nodePrefix + (i + 1);
- Graph.Create<Graph.Batch> create = batch.create(path);
- String originalValue = "The quick brown fox jumped over the moon. What?
";
- String value = originalValue;
- for (int j = 0; j != numProps; ++j) {
- // value = value + originalValue;
- create = create.with("property" + (j + 1), value);
- }
- create.and();
- }
- numberCreated += number;
- if (useBatch == null) {
- batch.execute();
- if (printIntermediateStatus) {
- System.out.println(" total created ... " +
numberCreated);
- }
- }
- if (depthRemaining > 1) {
- for (int i = 0; i != number; ++i) {
- String path = parentPath + "/" + nodePrefix + (i + 1);
- numberCreated += createChildren(useBatch, path, nodePrefix, number,
numProps, depthRemaining - 1, false);
- if (printIntermediateStatus) {
- System.out.println(" total created ... " +
numberCreated);
- }
- }
- }
- return numberCreated;
- }
-
- protected int numberNodesInTree( int numberPerDepth,
- int depth ) {
- assert depth > 0;
- assert numberPerDepth > 0;
- int totalNumber = 0;
- for (int i = 0; i <= depth; ++i) {
- totalNumber += (int)Math.pow(numberPerDepth, i);
- }
- return totalNumber;
- }
-
-}
Deleted:
trunk/extensions/dna-connector-store-jpa/src/test/java/org/jboss/dna/connector/store/jpa/JpaConnectionUsingHsqldbTest.java
===================================================================
---
trunk/extensions/dna-connector-store-jpa/src/test/java/org/jboss/dna/connector/store/jpa/JpaConnectionUsingHsqldbTest.java 2008-12-19
19:49:01 UTC (rev 694)
+++
trunk/extensions/dna-connector-store-jpa/src/test/java/org/jboss/dna/connector/store/jpa/JpaConnectionUsingHsqldbTest.java 2009-01-07
20:00:11 UTC (rev 695)
@@ -1,45 +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.connector.store.jpa;
-
-import org.hibernate.ejb.Ejb3Configuration;
-
-/**
- * @author Randall Hauch
- */
-public class JpaConnectionUsingHsqldbTest extends JpaConnectionTest {
-
- /**
- * {@inheritDoc}
- *
- * @see
org.jboss.dna.connector.store.jpa.JpaConnectionTest#configureDatabaseProperties(org.hibernate.ejb.Ejb3Configuration)
- */
- @Override
- protected void configureDatabaseProperties( Ejb3Configuration configurator ) {
- configurator.setProperty("hibernate.dialect",
"org.hibernate.dialect.HSQLDialect");
- configurator.setProperty("hibernate.connection.driver_class",
"org.hsqldb.jdbcDriver");
- configurator.setProperty("hibernate.connection.username",
"sa");
- configurator.setProperty("hibernate.connection.password",
"");
- configurator.setProperty("hibernate.connection.url",
"jdbc:hsqldb:.");
- }
-
-}
Added:
trunk/extensions/dna-connector-store-jpa/src/test/java/org/jboss/dna/connector/store/jpa/JpaConnectorReadingTest.java
===================================================================
---
trunk/extensions/dna-connector-store-jpa/src/test/java/org/jboss/dna/connector/store/jpa/JpaConnectorReadingTest.java
(rev 0)
+++
trunk/extensions/dna-connector-store-jpa/src/test/java/org/jboss/dna/connector/store/jpa/JpaConnectorReadingTest.java 2009-01-07
20:00:11 UTC (rev 695)
@@ -0,0 +1,73 @@
+/*
+ * 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.connector.store.jpa;
+
+import org.jboss.dna.common.stats.Stopwatch;
+import org.jboss.dna.graph.Graph;
+import org.jboss.dna.graph.connectors.RepositorySource;
+import org.jboss.dna.graph.connectors.test.ReadableConnectorTest;
+
+/**
+ * @author Randall Hauch
+ */
+public class JpaConnectorReadingTest extends ReadableConnectorTest {
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.connectors.test.AbstractConnectorTest#setUpSource()
+ */
+ @Override
+ protected RepositorySource setUpSource() {
+ // Set the connection properties to be an in-memory HSQL database ...
+ JpaSource source = new JpaSource();
+ source.setName("Test Repository");
+ source.setDialect("org.hibernate.dialect.HSQLDialect");
+ source.setDriverClassName("org.hsqldb.jdbcDriver");
+ source.setUsername("sa");
+ source.setPassword("");
+ source.setUrl("jdbc:hsqldb:.");
+ source.setMaximumConnectionsInPool(3);
+ source.setMinimumConnectionsInPool(0);
+ source.setNumberOfConnectionsToAcquireAsNeeded(1);
+ source.setMaximumSizeOfStatementCache(100);
+ source.setMaximumConnectionIdleTimeInSeconds(0);
+
+ return source;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see
org.jboss.dna.graph.connectors.test.AbstractConnectorTest#initializeContent(org.jboss.dna.graph.Graph)
+ */
+ @Override
+ protected void initializeContent( Graph graph ) {
+ String initialPath = "";
+ int depth = 4;
+ int numChildrenPerNode = 4;
+ int numPropertiesPerNode = 7;
+ Stopwatch sw = new Stopwatch();
+ boolean batch = true;
+ createSubgraph(graph, initialPath, depth, numChildrenPerNode,
numPropertiesPerNode, batch, sw, System.out, null);
+ }
+}
Property changes on:
trunk/extensions/dna-connector-store-jpa/src/test/java/org/jboss/dna/connector/store/jpa/JpaConnectorReadingTest.java
___________________________________________________________________
Name: svn:mime-type
+ text/plain
Added:
trunk/extensions/dna-connector-store-jpa/src/test/java/org/jboss/dna/connector/store/jpa/JpaConnectorWritingTest.java
===================================================================
---
trunk/extensions/dna-connector-store-jpa/src/test/java/org/jboss/dna/connector/store/jpa/JpaConnectorWritingTest.java
(rev 0)
+++
trunk/extensions/dna-connector-store-jpa/src/test/java/org/jboss/dna/connector/store/jpa/JpaConnectorWritingTest.java 2009-01-07
20:00:11 UTC (rev 695)
@@ -0,0 +1,67 @@
+/*
+ * 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.connector.store.jpa;
+
+import org.jboss.dna.graph.Graph;
+import org.jboss.dna.graph.connectors.RepositorySource;
+import org.jboss.dna.graph.connectors.test.WritableConnectorTest;
+
+/**
+ * @author Randall Hauch
+ */
+public class JpaConnectorWritingTest extends WritableConnectorTest {
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.connectors.test.AbstractConnectorTest#setUpSource()
+ */
+ @Override
+ protected RepositorySource setUpSource() {
+ // Set the connection properties to be an in-memory HSQL database ...
+ JpaSource source = new JpaSource();
+ source.setName("Test Repository");
+ source.setDialect("org.hibernate.dialect.HSQLDialect");
+ source.setDriverClassName("org.hsqldb.jdbcDriver");
+ source.setUsername("sa");
+ source.setPassword("");
+ source.setUrl("jdbc:hsqldb:.");
+ source.setMaximumConnectionsInPool(3);
+ source.setMinimumConnectionsInPool(0);
+ source.setNumberOfConnectionsToAcquireAsNeeded(1);
+ source.setMaximumSizeOfStatementCache(100);
+ source.setMaximumConnectionIdleTimeInSeconds(0);
+ source.setReferentialIntegrityEnforced(true);
+
+ return source;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see
org.jboss.dna.graph.connectors.test.AbstractConnectorTest#initializeContent(org.jboss.dna.graph.Graph)
+ */
+ @Override
+ protected void initializeContent( Graph graph ) {
+ }
+
+}
Property changes on:
trunk/extensions/dna-connector-store-jpa/src/test/java/org/jboss/dna/connector/store/jpa/JpaConnectorWritingTest.java
___________________________________________________________________
Name: svn:mime-type
+ text/plain
Deleted: trunk/extensions/dna-connector-store-jpa/src/test/resources/LoremIpsum1.txt
===================================================================
--- trunk/extensions/dna-connector-store-jpa/src/test/resources/LoremIpsum1.txt 2008-12-19
19:49:01 UTC (rev 694)
+++ trunk/extensions/dna-connector-store-jpa/src/test/resources/LoremIpsum1.txt 2009-01-07
20:00:11 UTC (rev 695)
@@ -1,9 +0,0 @@
-1. Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Etiam pellentesque, erat non
congue laoreet, felis nulla eleifend nulla, ut varius tellus felis eu risus. Etiam
dapibus. Aenean eget nibh sed urna pellentesque egestas. Aliquam sit amet turpis. Ut
blandit purus sit amet pede dictum gravida. Sed vestibulum, pede vel ornare faucibus,
libero est accumsan ante, a varius diam sapien a tortor. Maecenas luctus. Nullam purus
lectus, varius non, sodales nec, adipiscing eget, nulla. Nam nunc nunc, pretium a,
tincidunt ut, tristique ac, nisi. Vivamus tempor est quis turpis. In hac habitasse platea
dictumst. Nunc porttitor lacinia nunc. Proin dolor magna, imperdiet at, placerat in,
tempor at, magna. Fusce a nulla. Pellentesque sit amet sem nec leo porttitor sagittis.
-
-Integer mauris. Nunc libero est, consectetuer id, lacinia in, tempor a, lacus. Praesent
sed augue sed felis dictum blandit. In ipsum orci, pellentesque vitae, viverra vitae,
aliquam at, ante. Sed imperdiet mauris id lectus. Morbi vel magna sed pede accumsan
vulputate. Aliquam nisi est, molestie vitae, tempus sed, sagittis at, nulla. Cum sociis
natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Curabitur
turpis risus, faucibus at, porttitor ac, venenatis a, diam. Vestibulum blandit consequat
felis. Vestibulum vitae leo. Curabitur a nunc. Phasellus ornare. Morbi tortor est,
fringilla in, volutpat non, volutpat at, risus.
-
-Proin blandit pede tristique arcu. Vivamus ac libero. Donec pharetra leo sit amet orci.
Fusce sit amet velit. Nunc dui. Ut vitae nulla. Suspendisse potenti. Cras et elit.
Maecenas ipsum. Proin at mauris. Suspendisse fringilla libero a neque.
-
-Nullam vitae justo egestas justo lobortis consectetuer. Vestibulum eros. Cras id nisl.
Sed scelerisque commodo erat. Cras dignissim massa quis tellus. Donec auctor rutrum dolor.
Donec leo est, tempor quis, eleifend tincidunt, euismod eu, purus. Duis laoreet consequat
lectus. Cras quis dolor id massa mattis lacinia. Ut sollicitudin nunc in eros. Cras
gravida, purus ac rhoncus tempus, justo eros sodales mi, sit amet tincidunt metus erat sed
neque.
-
-Aliquam vitae nunc. Nullam at lectus ut nunc iaculis commodo. Phasellus augue. Aenean eu
mi eget magna egestas vehicula. Cum sociis natoque penatibus et magnis dis parturient
montes, nascetur ridiculus mus. Aenean molestie, massa eu rhoncus euismod, nisl eros porta
augue, vel dictum orci nibh vestibulum diam. Duis interdum. In hac habitasse platea
dictumst. In hac habitasse platea dictumst. Integer mi erat, egestas ac, aliquet at,
accumsan quis, dui. Praesent consectetuer euismod lorem. Fusce sagittis eros. Vivamus
hendrerit. Pellentesque malesuada, mauris eget mollis eleifend, enim nisl imperdiet
tortor, at pulvinar pede lectus eget augue. Cras convallis, odio nec aliquet rutrum, lorem
ipsum tincidunt mauris, vel rutrum lectus felis at felis. Ut lectus. Nam varius.
\ No newline at end of file
Deleted: trunk/extensions/dna-connector-store-jpa/src/test/resources/LoremIpsum2.txt
===================================================================
--- trunk/extensions/dna-connector-store-jpa/src/test/resources/LoremIpsum2.txt 2008-12-19
19:49:01 UTC (rev 694)
+++ trunk/extensions/dna-connector-store-jpa/src/test/resources/LoremIpsum2.txt 2009-01-07
20:00:11 UTC (rev 695)
@@ -1,9 +0,0 @@
-2. Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Etiam pellentesque, erat non
congue laoreet, felis nulla eleifend nulla, ut varius tellus felis eu risus. Etiam
dapibus. Aenean eget nibh sed urna pellentesque egestas. Aliquam sit amet turpis. Ut
blandit purus sit amet pede dictum gravida. Sed vestibulum, pede vel ornare faucibus,
libero est accumsan ante, a varius diam sapien a tortor. Maecenas luctus. Nullam purus
lectus, varius non, sodales nec, adipiscing eget, nulla. Nam nunc nunc, pretium a,
tincidunt ut, tristique ac, nisi. Vivamus tempor est quis turpis. In hac habitasse platea
dictumst. Nunc porttitor lacinia nunc. Proin dolor magna, imperdiet at, placerat in,
tempor at, magna. Fusce a nulla. Pellentesque sit amet sem nec leo porttitor sagittis.
-
-Integer mauris. Nunc libero est, consectetuer id, lacinia in, tempor a, lacus. Praesent
sed augue sed felis dictum blandit. In ipsum orci, pellentesque vitae, viverra vitae,
aliquam at, ante. Sed imperdiet mauris id lectus. Morbi vel magna sed pede accumsan
vulputate. Aliquam nisi est, molestie vitae, tempus sed, sagittis at, nulla. Cum sociis
natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Curabitur
turpis risus, faucibus at, porttitor ac, venenatis a, diam. Vestibulum blandit consequat
felis. Vestibulum vitae leo. Curabitur a nunc. Phasellus ornare. Morbi tortor est,
fringilla in, volutpat non, volutpat at, risus.
-
-Proin blandit pede tristique arcu. Vivamus ac libero. Donec pharetra leo sit amet orci.
Fusce sit amet velit. Nunc dui. Ut vitae nulla. Suspendisse potenti. Cras et elit.
Maecenas ipsum. Proin at mauris. Suspendisse fringilla libero a neque.
-
-Nullam vitae justo egestas justo lobortis consectetuer. Vestibulum eros. Cras id nisl.
Sed scelerisque commodo erat. Cras dignissim massa quis tellus. Donec auctor rutrum dolor.
Donec leo est, tempor quis, eleifend tincidunt, euismod eu, purus. Duis laoreet consequat
lectus. Cras quis dolor id massa mattis lacinia. Ut sollicitudin nunc in eros. Cras
gravida, purus ac rhoncus tempus, justo eros sodales mi, sit amet tincidunt metus erat sed
neque.
-
-Aliquam vitae nunc. Nullam at lectus ut nunc iaculis commodo. Phasellus augue. Aenean eu
mi eget magna egestas vehicula. Cum sociis natoque penatibus et magnis dis parturient
montes, nascetur ridiculus mus. Aenean molestie, massa eu rhoncus euismod, nisl eros porta
augue, vel dictum orci nibh vestibulum diam. Duis interdum. In hac habitasse platea
dictumst. In hac habitasse platea dictumst. Integer mi erat, egestas ac, aliquet at,
accumsan quis, dui. Praesent consectetuer euismod lorem. Fusce sagittis eros. Vivamus
hendrerit. Pellentesque malesuada, mauris eget mollis eleifend, enim nisl imperdiet
tortor, at pulvinar pede lectus eget augue. Cras convallis, odio nec aliquet rutrum, lorem
ipsum tincidunt mauris, vel rutrum lectus felis at felis. Ut lectus. Nam varius.
\ No newline at end of file
Deleted: trunk/extensions/dna-connector-store-jpa/src/test/resources/LoremIpsum3.txt
===================================================================
--- trunk/extensions/dna-connector-store-jpa/src/test/resources/LoremIpsum3.txt 2008-12-19
19:49:01 UTC (rev 694)
+++ trunk/extensions/dna-connector-store-jpa/src/test/resources/LoremIpsum3.txt 2009-01-07
20:00:11 UTC (rev 695)
@@ -1,9 +0,0 @@
-3. Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Etiam pellentesque, erat non
congue laoreet, felis nulla eleifend nulla, ut varius tellus felis eu risus. Etiam
dapibus. Aenean eget nibh sed urna pellentesque egestas. Aliquam sit amet turpis. Ut
blandit purus sit amet pede dictum gravida. Sed vestibulum, pede vel ornare faucibus,
libero est accumsan ante, a varius diam sapien a tortor. Maecenas luctus. Nullam purus
lectus, varius non, sodales nec, adipiscing eget, nulla. Nam nunc nunc, pretium a,
tincidunt ut, tristique ac, nisi. Vivamus tempor est quis turpis. In hac habitasse platea
dictumst. Nunc porttitor lacinia nunc. Proin dolor magna, imperdiet at, placerat in,
tempor at, magna. Fusce a nulla. Pellentesque sit amet sem nec leo porttitor sagittis.
-
-Integer mauris. Nunc libero est, consectetuer id, lacinia in, tempor a, lacus. Praesent
sed augue sed felis dictum blandit. In ipsum orci, pellentesque vitae, viverra vitae,
aliquam at, ante. Sed imperdiet mauris id lectus. Morbi vel magna sed pede accumsan
vulputate. Aliquam nisi est, molestie vitae, tempus sed, sagittis at, nulla. Cum sociis
natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Curabitur
turpis risus, faucibus at, porttitor ac, venenatis a, diam. Vestibulum blandit consequat
felis. Vestibulum vitae leo. Curabitur a nunc. Phasellus ornare. Morbi tortor est,
fringilla in, volutpat non, volutpat at, risus.
-
-Proin blandit pede tristique arcu. Vivamus ac libero. Donec pharetra leo sit amet orci.
Fusce sit amet velit. Nunc dui. Ut vitae nulla. Suspendisse potenti. Cras et elit.
Maecenas ipsum. Proin at mauris. Suspendisse fringilla libero a neque.
-
-Nullam vitae justo egestas justo lobortis consectetuer. Vestibulum eros. Cras id nisl.
Sed scelerisque commodo erat. Cras dignissim massa quis tellus. Donec auctor rutrum dolor.
Donec leo est, tempor quis, eleifend tincidunt, euismod eu, purus. Duis laoreet consequat
lectus. Cras quis dolor id massa mattis lacinia. Ut sollicitudin nunc in eros. Cras
gravida, purus ac rhoncus tempus, justo eros sodales mi, sit amet tincidunt metus erat sed
neque.
-
-Aliquam vitae nunc. Nullam at lectus ut nunc iaculis commodo. Phasellus augue. Aenean eu
mi eget magna egestas vehicula. Cum sociis natoque penatibus et magnis dis parturient
montes, nascetur ridiculus mus. Aenean molestie, massa eu rhoncus euismod, nisl eros porta
augue, vel dictum orci nibh vestibulum diam. Duis interdum. In hac habitasse platea
dictumst. In hac habitasse platea dictumst. Integer mi erat, egestas ac, aliquet at,
accumsan quis, dui. Praesent consectetuer euismod lorem. Fusce sagittis eros. Vivamus
hendrerit. Pellentesque malesuada, mauris eget mollis eleifend, enim nisl imperdiet
tortor, at pulvinar pede lectus eget augue. Cras convallis, odio nec aliquet rutrum, lorem
ipsum tincidunt mauris, vel rutrum lectus felis at felis. Ut lectus. Nam varius.
\ No newline at end of file