Author: rhauch
Date: 2009-05-18 11:36:58 -0400 (Mon, 18 May 2009)
New Revision: 911
Added:
trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/
trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/BranchedMirrorProjector.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/FederatedLexicon.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/FederatedRepository.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/FederatedRepositoryConnection.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/FederatedRepositorySource.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/FederatedRequest.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/FederatedWorkspace.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/ForkRequestProcessor.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/GeneralProjector.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/JoinMirrorRequestProcessor.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/JoinRequestProcessor.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/MirrorProjector.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/NoMoreFederatedRequests.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/OffsetMirrorProjector.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/ProjectedNode.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/Projection.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/ProjectionParser.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/Projector.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/ProjectorWithPlaceholders.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/package-info.java
trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/MockRepositoryConnection.java
trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/MockRepositoryRequestProcessor.java
trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/federation/
trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/federation/AbstractFederatedRepositorySourceIntegrationTest.java
trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/federation/AbstractProjectorTest.java
trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/federation/BranchedMirrorProjectorTest.java
trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/federation/FederatedRepositoryConnectionTest.java
trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/federation/FederatedRepositorySourceTest.java
trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/federation/FederatedRepositorySourceUsingMirrorAndBranchProjectionsTest.java
trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/federation/FederatedRepositorySourceUsingMirrorProjectionTest.java
trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/federation/FederatedRepositorySourceUsingOffsetProjectionTest.java
trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/federation/FederatedRepositoryTest.java
trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/federation/FederatedRequestTest.java
trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/federation/FederatedWorkspaceTest.java
trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/federation/ForkRequestProcessorChannelTest.java
trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/federation/ForkRequestProcessorTest.java
trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/federation/GeneralProjectorTest.java
trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/federation/JoinRequestProcessorTest.java
trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/federation/MirrorProjectorTest.java
trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/federation/OffsetMirrorProjectorTest.java
trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/federation/ProjectionParserTest.java
trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/federation/ProjectionPathRuleTest.java
trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/federation/ProjectionTest.java
trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/federation/ProjectorWithPlaceholdersTest.java
Modified:
trunk/dna-graph/src/main/java/org/jboss/dna/graph/DnaLexicon.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/ExecutionContext.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/Graph.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/GraphI18n.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/Location.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/inmemory/InMemoryRequestProcessor.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/CloneWorkspaceRequest.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/CompositeRequest.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/CopyBranchRequest.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/CreateNodeRequest.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/CreateWorkspaceRequest.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/DeleteBranchRequest.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/GetWorkspacesRequest.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/MoveBranchRequest.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/ReadAllChildrenRequest.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/ReadAllPropertiesRequest.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/ReadBlockOfChildrenRequest.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/ReadBranchRequest.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/ReadNextBlockOfChildrenRequest.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/ReadNodeRequest.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/ReadPropertyRequest.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/RemovePropertyRequest.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/RenameNodeRequest.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/SetPropertyRequest.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/UpdatePropertiesRequest.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/VerifyNodeExistsRequest.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/VerifyWorkspaceRequest.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/processor/RequestProcessor.java
trunk/dna-graph/src/main/resources/org/jboss/dna/graph/GraphI18n.properties
trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/inmemory/InMemoryConnectorReadableTest.java
trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/test/AbstractConnectorTest.java
trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/test/ReadableConnectorTest.java
trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/test/WorkspaceConnectorTest.java
trunk/pom.xml
Log:
DNA-403 Federation connector does not behave properly in many situations
Implemented a new federation connector (in 'dna-graph') that addresses most if not
all of the concerns listed in the issue. Some changes were made to the requests (e.g.,
addition of DeleteChildrenRequest) and ExecutionContext and Location. However, most of
the changes were in the addition of the 'org.jboss.dna.graph.connector.federation'
package and its mostly package-level classes.
Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/DnaLexicon.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/DnaLexicon.java 2009-05-15 19:28:48
UTC (rev 910)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/DnaLexicon.java 2009-05-18 15:36:58
UTC (rev 911)
@@ -42,6 +42,15 @@
public static final Name CLASSPATH = new BasicName(Namespace.URI,
"classpath");
public static final Name NAMESPACES = new BasicName(Namespace.URI,
"namespaces");
public static final Name PROJECTION_RULES = new BasicName(Namespace.URI,
"projectionRules");
+ public static final Name READ_ONLY = new BasicName(Namespace.URI,
"readOnly");
public static final Name TIME_TO_EXPIRE = new BasicName(Namespace.URI,
"timeToExpire");
public static final Name NAMESPACE_URI = new BasicName(Namespace.URI,
"uri");
+
+ public static final Name WORKSPACES = new BasicName(Namespace.URI,
"workspaces");
+ public static final Name SOURCE_NAME = new BasicName(Namespace.URI,
"sourceName");
+ public static final Name WORKSPACE_NAME = new BasicName(Namespace.URI,
"workspaceName");
+ public static final Name DEFAULT_WORKSPACE_NAME = new BasicName(Namespace.URI,
"defaultWorkspaceName");
+ public static final Name PROJECTION = new BasicName(Namespace.URI,
"projection");
+ public static final Name PROJECTIONS = new BasicName(Namespace.URI,
"projections");
+
}
Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/ExecutionContext.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/ExecutionContext.java 2009-05-15
19:28:48 UTC (rev 910)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/ExecutionContext.java 2009-05-18
15:36:58 UTC (rev 911)
@@ -42,6 +42,7 @@
import org.jboss.dna.common.component.StandardClassLoaderFactory;
import org.jboss.dna.common.util.CheckArg;
import org.jboss.dna.common.util.Logger;
+import org.jboss.dna.graph.connector.federation.FederatedLexicon;
import org.jboss.dna.graph.mimetype.ExtensionBasedMimeTypeDetector;
import org.jboss.dna.graph.mimetype.MimeTypeDetector;
import org.jboss.dna.graph.property.NamespaceRegistry;
@@ -421,7 +422,7 @@
CallbackHandler callbackHandler ) throws LoginException
{
LoginContext loginContext = new LoginContext(name, callbackHandler);
loginContext.login();
-
+
return new ExecutionContext(this, loginContext);
}
@@ -464,7 +465,7 @@
CallbackHandler callbackHandler ) throws LoginException
{
LoginContext loginContext = new LoginContext(name, subject, callbackHandler);
loginContext.login();
-
+
return new ExecutionContext(this, loginContext);
}
@@ -499,6 +500,7 @@
namespaceRegistry.register(JcrMixLexicon.Namespace.PREFIX,
JcrMixLexicon.Namespace.URI);
namespaceRegistry.register(JcrNtLexicon.Namespace.PREFIX,
JcrNtLexicon.Namespace.URI);
namespaceRegistry.register(DnaLexicon.Namespace.PREFIX,
DnaLexicon.Namespace.URI);
+ namespaceRegistry.register(FederatedLexicon.Namespace.PREFIX,
DnaLexicon.Namespace.URI);
// namespaceRegistry.register("dnadtd",
"http://www.jboss.org/dna/dtd/1.0");
// namespaceRegistry.register("dnaxml",
"http://www.jboss.org/dna/xml/1.0");
}
@@ -521,9 +523,8 @@
}
/**
+ * {@inheritDoc}
*
- * {@inheritDoc}
- *
* @see
javax.security.auth.callback.CallbackHandler#handle(javax.security.auth.callback.Callback[])
*/
public void handle( Callback[] callbacks ) throws UnsupportedCallbackException,
IOException {
Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/Graph.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/Graph.java 2009-05-15 19:28:48 UTC
(rev 910)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/Graph.java 2009-05-18 15:36:58 UTC
(rev 911)
@@ -986,7 +986,7 @@
getCurrentWorkspaceName(),
child,
EMPTY_PROPERTIES,
-
NodeConflictBehavior.DO_NOT_REPLACE).getActualLocationOfNode();
+
NodeConflictBehavior.UPDATE).getActualLocationOfNode();
return new GetNodeOrReturnGraph(location);
}
@@ -1009,7 +1009,7 @@
getCurrentWorkspaceName(),
child,
EMPTY_PROPERTIES,
-
NodeConflictBehavior.DO_NOT_REPLACE).getActualLocationOfNode();
+
NodeConflictBehavior.UPDATE).getActualLocationOfNode();
return new GetNodeOrReturnGraph(location);
}
@@ -1716,15 +1716,13 @@
}
public List<Location> under( Location at ) {
- return requests.readBlockOfChildren(at,
getCurrentWorkspaceName(), startingIndex, blockSize)
- .getChildren();
+ return requests.readBlockOfChildren(at,
getCurrentWorkspaceName(), startingIndex, blockSize).getChildren();
}
};
}
public List<Location> startingAfter( final Location
previousSibling ) {
- return requests.readNextBlockOfChildren(previousSibling,
getCurrentWorkspaceName(), blockSize)
- .getChildren();
+ return requests.readNextBlockOfChildren(previousSibling,
getCurrentWorkspaceName(), blockSize).getChildren();
}
public List<Location> startingAfter( String
pathOfPreviousSibling ) {
@@ -5248,10 +5246,7 @@
}
public SubgraphNode getNode( Name relativePath ) {
- Path path = getGraph().getContext()
- .getValueFactories()
- .getPathFactory()
- .create(getLocation().getPath(), relativePath);
+ Path path =
getGraph().getContext().getValueFactories().getPathFactory().create(getLocation().getPath(),
relativePath);
path = path.getNormalizedPath();
return getNode(path);
}
Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/GraphI18n.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/GraphI18n.java 2009-05-15 19:28:48
UTC (rev 910)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/GraphI18n.java 2009-05-18 15:36:58
UTC (rev 911)
@@ -68,6 +68,7 @@
public static I18n executedRequest;
public static I18n closingRequestProcessor;
public static I18n closedRequestProcessor;
+ public static I18n multipleErrorsWhileExecutingManyRequests;
public static I18n multipleErrorsWhileExecutingRequests;
public static I18n unsupportedRequestType;
public static I18n unableToAddMoreRequestsToAlreadyExecutedBatch;
@@ -88,13 +89,33 @@
public static I18n unableToFindRepositorySourceWithName;
/* In-Memory Connector */
- public static I18n inMemoryConnectorName;
public static I18n inMemoryNodeDoesNotExist;
public static I18n errorSerializingInMemoryCachePolicyInSource;
public static I18n inMemoryConnectorRequestsMustHavePathOrUuid;
public static I18n workspaceDoesNotExistInRepository;
public static I18n workspaceAlreadyExistsInRepository;
+ /* Federation Connection */
+ public static I18n namePropertyIsRequiredForFederatedRepositorySource;
+ public static I18n propertyIsRequiredForFederatedRepositorySource;
+ public static I18n federatedRepositorySourceMustBeInitialized;
+ public static I18n errorReadingConfigurationForFederatedRepositorySource;
+ public static I18n errorAddingProjectionRuleParseMethod;
+ public static I18n requiredNodeDoesNotExistRelativeToNode;
+ public static I18n unableToObtainConnectionToFederatedSource;
+ public static I18n workspaceDoesNotExistInFederatedRepository;
+ public static I18n locationCannotBeProjectedIntoWorkspaceAndSource;
+ public static I18n unableToAddRequestToChannelThatIsDone;
+ public static I18n federatedSourceDoesNotSupportCreatingWorkspaces;
+ public static I18n federatedSourceDoesNotSupportCloningWorkspaces;
+ public static I18n federatedSourceDoesNotSupportDestroyingWorkspaces;
+ public static I18n unableToProjectSourceInformationIntoWorkspace;
+ public static I18n unableToCreateNodeUnderPlaceholder;
+ public static I18n unableToUpdatePlaceholder;
+ public static I18n unableToDeletePlaceholder;
+ public static I18n copyLimitedToBeWithinSingleSource;
+ public static I18n moveLimitedToBeWithinSingleSource;
+
static {
try {
I18n.initialize(GraphI18n.class);
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 2009-05-15 19:28:48
UTC (rev 910)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/Location.java 2009-05-18 15:36:58
UTC (rev 911)
@@ -45,7 +45,7 @@
* @author Randall Hauch
*/
@Immutable
-public abstract class Location implements Iterable<Property> {
+public abstract class Location implements Iterable<Property>,
Comparable<Location> {
/**
* Simple shared iterator instance that is used when there are no properties.
@@ -432,6 +432,24 @@
}
/**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Comparable#compareTo(java.lang.Object)
+ */
+ public int compareTo( Location that ) {
+ if (this == that) return 0;
+ if (this.hasPath() && that.hasPath()) {
+ return this.getPath().compareTo(that.getPath());
+ }
+ UUID thisUuid = this.getUuid();
+ UUID thatUuid = that.getUuid();
+ if (thisUuid != null && thatUuid != null) {
+ return thisUuid.compareTo(thatUuid);
+ }
+ return this.hashCode() - that.hashCode();
+ }
+
+ /**
* Get the string form of the location.
*
* @return the string
Added:
trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/BranchedMirrorProjector.java
===================================================================
---
trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/BranchedMirrorProjector.java
(rev 0)
+++
trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/BranchedMirrorProjector.java 2009-05-18
15:36:58 UTC (rev 911)
@@ -0,0 +1,193 @@
+/*
+ * JBoss DNA (
http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership. Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of
+ * individual contributors.
+ *
+ * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
+ * is licensed to you under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * JBoss DNA is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site:
http://www.fsf.org.
+ */
+package org.jboss.dna.graph.connector.federation;
+
+import java.util.Iterator;
+import java.util.List;
+import net.jcip.annotations.Immutable;
+import org.jboss.dna.graph.ExecutionContext;
+import org.jboss.dna.graph.Location;
+import org.jboss.dna.graph.connector.federation.Projection.Rule;
+import org.jboss.dna.graph.property.Path;
+import org.jboss.dna.graph.property.PathFactory;
+import org.jboss.dna.graph.property.Path.Segment;
+
+/**
+ * A Projector for federated repository configurations that have a single mirrored
projection (a projection that is a one-for-one
+ * mapping to a single source) and a single branch projection that maps a single path in
the source to a single path in the
+ * federated repository.
+ * <p>
+ * For example, consider a federated repository that maps the "/" path in
source1 into "/" in the federated repository, and the
+ * "/path/in/source" path into the "/federated/path" branch in the
federated repository. Thus, anything at or below
+ * "/federated/path" goes into the (If this were the only projection, then a
{@link MirrorProjector} could be used.)
+ */
+@Immutable
+final class BranchedMirrorProjector extends ProjectorWithPlaceholders {
+
+ /**
+ * Attempt to create an instance of the {@link BranchedMirrorProjector} with the
supplied projections using the supplied
+ * context.
+ *
+ * @param context the context; may not be null
+ * @param projections the projections in the federated repository; may not be null
+ * @return the branched mirror projector, or null if the projections didn't match
the criteria for such a projector
+ */
+ static BranchedMirrorProjector with( ExecutionContext context,
+ List<Projection> projections ) {
+ assert projections != null;
+ assert context != null;
+ if (projections.size() != 2) return null;
+ Projection first = projections.get(0);
+ Projection second = projections.get(1);
+ if (first.getRules().size() != 1) return null;
+ if (second.getRules().size() != 1) return null;
+ Rule firstRule = first.getRules().get(0);
+ Rule secondRule = second.getRules().get(0);
+ assert firstRule != null;
+ assert secondRule != null;
+ PathFactory pathFactory = context.getValueFactories().getPathFactory();
+ List<Path> firstTopLevelPaths =
first.getRules().get(0).getTopLevelPathsInRepository(pathFactory);
+ if (firstTopLevelPaths.size() != 1) return null;
+ List<Path> secondTopLevelPaths =
second.getRules().get(0).getTopLevelPathsInRepository(pathFactory);
+ if (secondTopLevelPaths.size() != 1) return null;
+ // We're good, so create the instance ...
+ Path firstTopLevelPath = firstTopLevelPaths.get(0);
+ Path secondTopLevelPath = secondTopLevelPaths.get(0);
+ if (firstTopLevelPath.isRoot()) {
+ return new BranchedMirrorProjector(context, projections, first, second,
secondTopLevelPath,
+
secondRule.getPathInSource(secondTopLevelPath, pathFactory));
+ }
+ return new BranchedMirrorProjector(context, projections, second, first,
firstTopLevelPath,
+ firstRule.getPathInSource(firstTopLevelPath,
pathFactory));
+ }
+
+ private final Projection mirrorProjection;
+ private final Projection branchProjection;
+ private final Path branchFederatedPath;
+ private final Path branchSourcePath;
+ private final boolean branchSourceUsesSamePath;
+
+ BranchedMirrorProjector( ExecutionContext context,
+ List<Projection> projections,
+ Projection mirrorProjection,
+ Projection branchProjection,
+ Path branchFederatedPath,
+ Path branchSourcePath ) {
+ super(context, projections);
+ assert mirrorProjection != null;
+ assert branchProjection != null;
+ assert branchFederatedPath != null;
+ assert branchSourcePath != null;
+ this.mirrorProjection = mirrorProjection;
+ this.branchProjection = branchProjection;
+ this.branchFederatedPath = branchFederatedPath;
+ this.branchSourcePath = branchSourcePath;
+ this.branchSourceUsesSamePath = branchSourcePath.equals(branchFederatedPath);
+ }
+
+ /**
+ * {@inheritDoc}
+ * <p>
+ * This implementation <i>always<i> returns a single {@link ProxyNode}
for the location in the single projection.
+ * </p>
+ *
+ * @see org.jboss.dna.graph.connector.federation.Projector#project(ExecutionContext,
Location, boolean)
+ */
+ public ProjectedNode project( ExecutionContext context,
+ Location location,
+ boolean requiresUpdate ) {
+ if (location.hasPath()) {
+ Path path = location.getPath();
+ if (path.isRoot()) {
+ // It is a projection of the mirror's root and the placeholder for
the branch root ...
+ if (requiresUpdate && mirrorProjection.isReadOnly()) return
null;
+ ProxyNode result = new ProxyNode(mirrorProjection, location,
location.with(branchSourcePath),
+ branchSourceUsesSamePath);
+ result.add(isPlaceholder(location));
+ return result;
+ }
+ ProjectedNode onBranch = isOnBranch(path, location, context);
+ if (onBranch != null) {
+ if (requiresUpdate && branchProjection.isReadOnly()) return
null;
+ return onBranch;
+ }
+ // Otherwise it is a proxy to the mirror only ...
+ if (requiresUpdate && mirrorProjection.isReadOnly()) return null;
+ return new ProxyNode(mirrorProjection, location, location, true);
+ }
+
+ // The location has no path and only identifier properties ...
+ if (requiresUpdate) {
+ if (branchProjection.isReadOnly()) {
+ // Can't update branch ...
+ if (mirrorProjection.isReadOnly()) return null;
+ return new ProxyNode(mirrorProjection, location, location, true); // no
paths
+ }
+ if (mirrorProjection.isReadOnly()) {
+ // Can't update mirror ...
+ return new ProxyNode(branchProjection, location, location,
branchSourceUsesSamePath); // no paths
+ }
+ }
+
+ // This is just a read, so create one projection for the mirror, and a second one
for the branch.
+ ProjectedNode result = new ProxyNode(mirrorProjection, location, location,
true);
+ result.add(new ProxyNode(branchProjection, location, location,
branchSourceUsesSamePath)); // no paths
+ return result;
+ }
+
+ protected final ProjectedNode isOnBranch( Path federatedPath,
+ Location location,
+ ExecutionContext context ) {
+ Iterator<Segment> branchIter = branchFederatedPath.iterator();
+ Iterator<Segment> federIter = federatedPath.iterator();
+ // Look at the first path ...
+ if (branchIter.hasNext() && federIter.hasNext()) {
+ if (!branchIter.next().equals(federIter.next())) return null; // not on
branch
+ }
+ // Otherwise, the federated path is on the branch, but how far ...
+ while (branchIter.hasNext() && federIter.hasNext()) {
+ if (!branchIter.next().equals(federIter.next())) {
+ // Didn't make it all the way along the branch ...
+ return null;
+ }
+ }
+ if (branchIter.hasNext()) {
+ // Didn't make it all the way down to the top of the branch, but it is a
placeholder ...
+ return isPlaceholder(location);
+ }
+ // Otherwise it is within the brach ...
+ Location locationInSource = location;
+ if (!branchSourceUsesSamePath) {
+ // The source uses a different path ...
+ if (federIter.hasNext()) {
+ Path subpath = federatedPath.subpath(branchFederatedPath.size());
+ Path sourcePath =
context.getValueFactories().getPathFactory().create(branchSourcePath, subpath);
+ locationInSource = location.with(sourcePath);
+ } else {
+ locationInSource = location.with(branchSourcePath);
+ }
+ }
+ return new ProxyNode(branchProjection, locationInSource, location,
branchSourceUsesSamePath);
+ }
+}
Property changes on:
trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/BranchedMirrorProjector.java
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Copied:
trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/FederatedLexicon.java
(from rev 910, trunk/dna-graph/src/main/java/org/jboss/dna/graph/DnaLexicon.java)
===================================================================
---
trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/FederatedLexicon.java
(rev 0)
+++
trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/FederatedLexicon.java 2009-05-18
15:36:58 UTC (rev 911)
@@ -0,0 +1,42 @@
+/*
+ * JBoss DNA (
http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership. Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of
+ * individual contributors.
+ *
+ * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
+ * is licensed to you under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * JBoss DNA is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site:
http://www.fsf.org.
+ */
+package org.jboss.dna.graph.connector.federation;
+
+import org.jboss.dna.graph.DnaLexicon;
+import org.jboss.dna.graph.property.Name;
+import org.jboss.dna.graph.property.basic.BasicName;
+
+/**
+ * @author Randall Hauch
+ */
+public class FederatedLexicon extends DnaLexicon {
+
+ public static final Name WORKSPACES = new BasicName(Namespace.URI,
"workspaces");
+ public static final Name CACHE = new BasicName(Namespace.URI, "cache");
+ public static final Name PROJECTION = new BasicName(Namespace.URI,
"projection");
+ public static final Name PROJECTIONS = new BasicName(Namespace.URI,
"projections");
+ public static final Name SOURCE_NAME = new BasicName(Namespace.URI,
"sourceName");
+ public static final Name WORKSPACE_NAME = new BasicName(Namespace.URI,
"workspaceName");
+ public static final Name DEFAULT_WORKSPACE_NAME = new BasicName(Namespace.URI,
"defaultWorkspaceName");
+}
Added:
trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/FederatedRepository.java
===================================================================
---
trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/FederatedRepository.java
(rev 0)
+++
trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/FederatedRepository.java 2009-05-18
15:36:58 UTC (rev 911)
@@ -0,0 +1,166 @@
+/*
+ * JBoss DNA (
http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership. Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of
+ * individual contributors.
+ *
+ * Unless otherwise indicated, all code in JBoss DNA is licensed
+ * to you under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * JBoss DNA is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site:
http://www.fsf.org.
+ */
+package org.jboss.dna.graph.connector.federation;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ExecutorService;
+import net.jcip.annotations.Immutable;
+import org.jboss.dna.common.util.CheckArg;
+import org.jboss.dna.graph.GraphI18n;
+import org.jboss.dna.graph.cache.CachePolicy;
+import org.jboss.dna.graph.connector.RepositoryConnectionFactory;
+import org.jboss.dna.graph.connector.RepositorySource;
+import org.jboss.dna.graph.request.InvalidWorkspaceException;
+
+/**
+ * The configuration of a {@link FederatedRepositorySource}.
+ */
+@Immutable
+class FederatedRepository {
+
+ private final String sourceName;
+ private final CachePolicy defaultCachePolicy;
+ private final RepositoryConnectionFactory connectionFactory;
+ private final Map<String, FederatedWorkspace> workspacesByName;
+ private final FederatedWorkspace defaultWorkspace;
+ private final ExecutorService executor;
+
+ /**
+ * Construct a new instance of a configuration defining how the workspaces in a
federated repository should project the
+ * content from one or more sources.
+ *
+ * @param sourceName the name of the federated repository source; may not be null
+ * @param connectionFactory the factory for connections to the sources being
federated; may not be null
+ * @param workspaces the workspaces that make up this federated repository; may not
be null or empty
+ * @param defaultCachePolicy the default cache policy for the source, or null if
there is no default caching policy for the
+ * whole federated repository (each workspace may have its own)
+ * @param executor the {@link ExecutorService} that can be used to parallelize
actions within this repository;may not be null
+ */
+ public FederatedRepository( String sourceName,
+ RepositoryConnectionFactory connectionFactory,
+ Iterable<FederatedWorkspace> workspaces,
+ CachePolicy defaultCachePolicy,
+ ExecutorService executor ) {
+ CheckArg.isNotNull(sourceName, "sourceName");
+ CheckArg.isNotNull(connectionFactory, "connectionFactory");
+ CheckArg.isNotNull(workspaces, "workspaces");
+ CheckArg.isNotNull(executor, "executor");
+ this.sourceName = sourceName;
+ this.connectionFactory = connectionFactory;
+ this.executor = executor;
+ this.defaultCachePolicy = defaultCachePolicy;
+ this.workspacesByName = new HashMap<String, FederatedWorkspace>();
+ FederatedWorkspace defaultWorkspace = null;
+ for (FederatedWorkspace workspace : workspaces) {
+ if (defaultWorkspace == null) defaultWorkspace = workspace;
+ this.workspacesByName.put(workspace.getName(), workspace);
+ }
+ this.defaultWorkspace = defaultWorkspace;
+ assert this.defaultWorkspace != null;
+ assert this.workspacesByName.size() > 0;
+ }
+
+ /**
+ * Get the name of the {@link RepositorySource} that owns this configuration.
+ *
+ * @return the source's name; never null
+ */
+ public String getSourceName() {
+ return sourceName;
+ }
+
+ /**
+ * Get the {@link RepositoryConnectionFactory factory for connections} to the sources
that are being federated by this
+ * repository.
+ *
+ * @return the connection factory for the sources; never null
+ */
+ public RepositoryConnectionFactory getConnectionFactory() {
+ return connectionFactory;
+ }
+
+ /**
+ * Get the {@link FederatedWorkspace workspace} information, given its name.
+ *
+ * @param name the name of the workspace, or null if the default workspace should be
returned
+ * @return the workspace
+ * @throws InvalidWorkspaceException if the specified workspace does not exist
+ */
+ public FederatedWorkspace getWorkspace( String name ) {
+ if (name == null) {
+ assert defaultWorkspace != null;
+ return defaultWorkspace;
+ }
+ FederatedWorkspace workspace = workspacesByName.get(name);
+ if (workspace == null) {
+ String msg = GraphI18n.workspaceDoesNotExistInFederatedRepository.text(name,
getSourceName());
+ throw new InvalidWorkspaceException(msg);
+ }
+ return workspace;
+ }
+
+ /**
+ * Get the names of the available workspaces.
+ *
+ * @return the unmodifiable copy of the workspace names; never null
+ */
+ Set<String> getWorkspaceNames() {
+ return Collections.unmodifiableSet(new
HashSet<String>(workspacesByName.keySet()));
+ }
+
+ /**
+ * Get the default cache policy that used for the whole repository. Note that the
repository may or may not have a default
+ * caching policy, and each {@link FederatedWorkspace workspace} may have its own
{@link FederatedWorkspace#getCachePolicy()
+ * caching policy}.
+ *
+ * @return the default cache policy for the repository, used if/when the workspace(s)
don't have their own caching policy or
+ * when the source content does not specify the caching policy; may be null
+ */
+ public CachePolicy getDefaultCachePolicy() {
+ return defaultCachePolicy;
+ }
+
+ /**
+ * Get the executor for this repository. This executor can be used to process tasks.
+ *
+ * @return the executor; may not be null
+ */
+ public ExecutorService getExecutor() {
+ return executor;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Object#toString()
+ */
+ @Override
+ public String toString() {
+ return "Federated repository \"" + getSourceName() + "\"
with workspaces " + workspacesByName.keySet();
+ }
+}
Property changes on:
trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/FederatedRepository.java
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Added:
trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/FederatedRepositoryConnection.java
===================================================================
---
trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/FederatedRepositoryConnection.java
(rev 0)
+++
trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/FederatedRepositoryConnection.java 2009-05-18
15:36:58 UTC (rev 911)
@@ -0,0 +1,265 @@
+/*
+ * JBoss DNA (
http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership. Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of
+ * individual contributors.
+ *
+ * Unless otherwise indicated, all code in JBoss DNA is licensed
+ * to you under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * JBoss DNA is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site:
http://www.fsf.org.
+ */
+package org.jboss.dna.graph.connector.federation;
+
+import java.util.LinkedList;
+import java.util.Queue;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.CancellationException;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.TimeUnit;
+import javax.transaction.xa.XAResource;
+import org.jboss.dna.common.statistic.Stopwatch;
+import org.jboss.dna.common.util.Logger;
+import org.jboss.dna.graph.ExecutionContext;
+import org.jboss.dna.graph.cache.CachePolicy;
+import org.jboss.dna.graph.connector.RepositoryConnection;
+import org.jboss.dna.graph.connector.RepositorySourceException;
+import org.jboss.dna.graph.connector.RepositorySourceListener;
+import org.jboss.dna.graph.property.DateTime;
+import org.jboss.dna.graph.request.CompositeRequest;
+import org.jboss.dna.graph.request.Request;
+import org.jboss.dna.graph.request.processor.RequestProcessor;
+
+/**
+ * This {@link RepositoryConnection} implementation executes {@link Request requests}
against the federated repository by
+ * projecting them into requests against the underlying sources that are being
federated.
+ * <p>
+ * One important design of the connector framework is that requests can be submitted in
{@link CompositeRequest batch} to a
+ * {@link RepositoryConnection connection}, which may perform them more efficiently than
if each request was submitted one at a
+ * time. This connector design maintains this feature by projecting the incoming requests
into requests against each source, then
+ * submitting the batch of projected requests to each source, and then transforming the
results of the projected requests back
+ * into original requests.
+ * </p>
+ * <p>
+ * This is accomplished using a three-step process:
+ * <ol>
+ * <li><strong>Step 1:</strong> Process the incoming requests and for
each generate the appropriate request(s) against the sources
+ * (dictated by the {@link FederatedWorkspace workspace's} {@link
FederatedWorkspace#getProjections() projections}). These
+ * "projected requests" are then enqueued for each source.</li>
+ * <li><strong>Step 2:</strong> Submit each batch of projected requests
to the appropriate source, in parallel where possible.
+ * Note that the requests are still ordered correctly for each source.</li>
+ * <li><strong>Step 3:</strong> Accumulate the results for the incoming
requests by post-processing the projected requests and
+ * transforming the source-specific results back into the federated workspace (again,
using the workspace's projections).</li>
+ * </ol>
+ * </p>
+ * <p>
+ * This process is a form of the <i>fork-join</i> divide-and-conquer
algorithm, which involves splitting a problem into smaller
+ * parts, forking new subtasks to execute each smaller part, joining on the subtasks
(waiting until all have finished), and then
+ * composing the results. Technically, Step 2 performs the fork and join operations, but
this class uses {@link RequestProcessor}
+ * implementations to do Step 1 and 3 (called {@link ForkRequestProcessor} and {@link
JoinRequestProcessor}, respectively).
+ * </p>
+ */
+class FederatedRepositoryConnection implements RepositoryConnection {
+
+ private final FederatedRepository repository;
+ private final Stopwatch stopwatch;
+ private final Logger logger;
+
+ FederatedRepositoryConnection( FederatedRepository repository ) {
+ this.repository = repository;
+ this.logger = Logger.getLogger(getClass());
+ this.stopwatch = logger.isTraceEnabled() ? new Stopwatch() : null;
+ }
+
+ /**
+ * The federated repository that created this connection.
+ *
+ * @return repository
+ */
+ FederatedRepository getRepository() {
+ return repository;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.connector.RepositoryConnection#getDefaultCachePolicy()
+ */
+ public CachePolicy getDefaultCachePolicy() {
+ return repository.getDefaultCachePolicy(); // may be null
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.connector.RepositoryConnection#getSourceName()
+ */
+ public String getSourceName() {
+ return repository.getSourceName();
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.connector.RepositoryConnection#getXAResource()
+ */
+ public XAResource getXAResource() {
+ return null;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.connector.RepositoryConnection#ping(long,
java.util.concurrent.TimeUnit)
+ */
+ public boolean ping( long time,
+ TimeUnit unit ) {
+ return true;
+ }
+
+ protected boolean shouldProcessSynchronously( Request request ) {
+ if (request instanceof CompositeRequest) {
+ CompositeRequest composite = (CompositeRequest)request;
+ if (composite.size() == 1) return true;
+ return false;
+ }
+ // Otherwise, its just a single request ...
+ return true;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see
org.jboss.dna.graph.connector.RepositoryConnection#execute(org.jboss.dna.graph.ExecutionContext,
+ * org.jboss.dna.graph.request.Request)
+ */
+ public void execute( ExecutionContext context,
+ final Request request ) throws RepositorySourceException {
+ // Compute the current time ...
+ DateTime nowInUtc = context.getValueFactories().getDateFactory().createUtc();
+
+ // Figure out whether we should asynchronously do the forking ...
+ boolean synchronousStep1 = shouldProcessSynchronously(request);
+ final boolean awaitAllSubtasks = false;
+
+ // Prepare for trace-level logging ...
+ if (stopwatch != null) stopwatch.start();
+
+ boolean abort = false;
+ try {
+ //
----------------------------------------------------------------------------------------------------
+ // Step 1: Fork the submitted requests into source-specific requests...
+ //
----------------------------------------------------------------------------------------------------
+ // This forks a subtask for each source, as soon as the first source-specific
request for a source
+ // is generated. Each source's
"execute(ExecutionContext,Request)" is called only once (a queue is
+ // used so that the source can begin processing the requests before all the
requests have been
+ // computed and submitted to the subtask). Thus, it's possible (and
likely) that this thread
+ // and subtask threads are executed in parallel.
+ final Queue<FederatedRequest> requests = awaitAllSubtasks ? new
LinkedList<FederatedRequest>() : new LinkedBlockingQueue<FederatedRequest>();
+ final ForkRequestProcessor fork = new ForkRequestProcessor(repository,
context, nowInUtc, requests);
+ if (synchronousStep1) {
+ // Execute the forking process in this thread ...
+ try {
+ fork.process(request);
+ } finally {
+ fork.close();
+ }
+ if (!awaitAllSubtasks) requests.add(new NoMoreFederatedRequests());
+ // At this point, all submitted requests have been processed/forked, so
we can continue with
+ // the join process, starting with the first submitted request. Note that
the subtasks may
+ // still be executing, but as the join process operates on a forked
request, it will wait
+ // until all forked requests have completed. Hopefully, in most
situations, the subtasks
+ // have enough of a lead that the join process never has to wait.
+ } else {
+ // Submit the forking process for execution in a separate thread ...
+ repository.getExecutor().submit(new Runnable() {
+ public void run() {
+ try {
+ fork.process(request);
+ } finally {
+ fork.close();
+ }
+ if (!awaitAllSubtasks) requests.add(new
NoMoreFederatedRequests());
+ }
+ });
+
+ // At this point, the forking process is being run by a thread owned by
the Executor. We'll still
+ // continue with the join process, starting with the first submitted
request. Note that it is
+ // likely that the subtasks are still running in threads owned by the
Executor.
+ }
+
+ if (awaitAllSubtasks) {
+ // Await until all subtasks have completed ...
+ fork.await();
+ }
+
+ //
----------------------------------------------------------------------------------------------------
+ // Step 2: Join the results of the source-specific (forked) requests back
into the submitted requests
+ //
----------------------------------------------------------------------------------------------------
+ JoinRequestProcessor join = new JoinRequestProcessor(repository, context,
nowInUtc);
+ try {
+ if (awaitAllSubtasks) {
+ join.process(requests);
+ } else {
+ join.process((BlockingQueue<FederatedRequest>)requests);
+ }
+ } catch (RuntimeException e) {
+ abort = true;
+ throw e;
+ } finally {
+ join.close();
+ }
+ } catch (InterruptedException e) {
+ abort = true;
+ request.setError(e);
+ } catch (ExecutionException e) {
+ abort = true;
+ request.setError(e);
+ } catch (CancellationException e) {
+ abort = true;
+ request.cancel();
+ // do nothing else
+ } catch (RuntimeException e) {
+ abort = true;
+ throw e;
+ } finally {
+ if (stopwatch != null) stopwatch.stop();
+ if (abort) {
+ // Rollback the transaction (if there is one) ...
+ }
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.connector.RepositoryConnection#close()
+ */
+ public void close() {
+ if (stopwatch != null) {
+ logger.trace("Processing federated requests:\n" +
stopwatch.getDetailedStatistics());
+ }
+ // do nothing else, since we don't currently hold any state
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see
org.jboss.dna.graph.connector.RepositoryConnection#setListener(org.jboss.dna.graph.connector.RepositorySourceListener)
+ */
+ public void setListener( RepositorySourceListener listener ) {
+ }
+}
Property changes on:
trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/FederatedRepositoryConnection.java
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Added:
trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/FederatedRepositorySource.java
===================================================================
---
trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/FederatedRepositorySource.java
(rev 0)
+++
trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/FederatedRepositorySource.java 2009-05-18
15:36:58 UTC (rev 911)
@@ -0,0 +1,588 @@
+/*
+ * JBoss DNA (
http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership. Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of
+ * individual contributors.
+ *
+ * Unless otherwise indicated, all code in JBoss DNA is licensed
+ * to you under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * JBoss DNA is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site:
http://www.fsf.org.
+ */
+package org.jboss.dna.graph.connector.federation;
+
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Hashtable;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
+import javax.naming.Context;
+import javax.naming.Name;
+import javax.naming.RefAddr;
+import javax.naming.Reference;
+import javax.naming.StringRefAddr;
+import javax.naming.spi.ObjectFactory;
+import net.jcip.annotations.GuardedBy;
+import org.jboss.dna.common.i18n.I18n;
+import org.jboss.dna.common.util.HashCode;
+import org.jboss.dna.graph.DnaLexicon;
+import org.jboss.dna.graph.ExecutionContext;
+import org.jboss.dna.graph.Graph;
+import org.jboss.dna.graph.GraphI18n;
+import org.jboss.dna.graph.Location;
+import org.jboss.dna.graph.Node;
+import org.jboss.dna.graph.Subgraph;
+import org.jboss.dna.graph.SubgraphNode;
+import org.jboss.dna.graph.cache.BasicCachePolicy;
+import org.jboss.dna.graph.cache.CachePolicy;
+import org.jboss.dna.graph.connector.RepositoryConnection;
+import org.jboss.dna.graph.connector.RepositoryConnectionFactory;
+import org.jboss.dna.graph.connector.RepositoryContext;
+import org.jboss.dna.graph.connector.RepositorySource;
+import org.jboss.dna.graph.connector.RepositorySourceCapabilities;
+import org.jboss.dna.graph.connector.RepositorySourceException;
+import org.jboss.dna.graph.property.NamespaceRegistry;
+import org.jboss.dna.graph.property.Path;
+import org.jboss.dna.graph.property.Property;
+import org.jboss.dna.graph.property.ValueFactories;
+import org.jboss.dna.graph.property.ValueFactory;
+
+/**
+ *
+ */
+public class FederatedRepositorySource implements RepositorySource, ObjectFactory {
+
+ /**
+ * The default limit is {@value} for retrying {@link RepositoryConnection connection}
calls to the underlying source.
+ */
+ public static final int DEFAULT_RETRY_LIMIT = 0;
+
+ /**
+ * The default path to the node defining the configuration for a source.
+ */
+ public static final String DEFAULT_CONFIGURATION_PATH = "/";
+
+ protected static final String SOURCE_NAME = "sourceName";
+ protected static final String RETRY_LIMIT = "retryLimit";
+ protected static final String CONFIGURATION_SOURCE_NAME =
"configurationSourceName";
+ protected static final String CONFIGURATION_PATH = "configurationPath";
+ protected static final String CONFIGURATION_WORKSPACE_NAME =
"configurationWorkspaceName";
+
+ private static final long serialVersionUID = 1L;
+
+ private volatile String name;
+ private volatile int retryLimit;
+ private volatile String configurationSourceName;
+ private volatile String configurationWorkspaceName;
+ private volatile String configurationPath = DEFAULT_CONFIGURATION_PATH;
+ private volatile RepositorySourceCapabilities capabilities = new
RepositorySourceCapabilities(true, true, false, false, true);
+ private volatile transient FederatedRepository configuration;
+ private volatile transient RepositoryContext context;
+
+ /**
+ * Construct a new instance of a {@link RepositorySource} for a federated
repository.
+ */
+ public FederatedRepositorySource() {
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.connector.RepositorySource#getName()
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * @param name Sets name to the specified value.
+ */
+ public synchronized void setName( String name ) {
+ if (this.name == name || this.name != null && this.name.equals(name))
return; // unchanged
+ this.name = name;
+ changeConfiguration();
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.connector.RepositorySource#getRetryLimit()
+ */
+ public int getRetryLimit() {
+ return retryLimit;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.connector.RepositorySource#setRetryLimit(int)
+ */
+ public synchronized void setRetryLimit( int limit ) {
+ retryLimit = limit < 0 ? 0 : limit;
+ changeConfiguration();
+ }
+
+ /**
+ * Get the name of the {@link RepositorySource} that should be used to create the
{@link FederatedRepository federated
+ * repository configuration} as the configuration repository.
+ * <p>
+ * This is a required property.
+ * </p>
+ *
+ * @return the name of the {@link RepositorySource} instance that should be used for
the configuration
+ * @see #setConfigurationSourceName(String)
+ * @see #getConfigurationWorkspaceName()
+ * @see #getConfigurationPath()
+ */
+ public String getConfigurationSourceName() {
+ return configurationSourceName;
+ }
+
+ /**
+ * Set the name of the {@link RepositorySource} that should be used to create the
{@link FederatedRepository federated
+ * repository configuration} as the configuration repository. The instance will be
retrieved from the
+ * {@link RepositoryConnectionFactory} instance inside the {@link
RepositoryContext#getRepositoryConnectionFactory()
+ * repository context} supplied during {@link
RepositorySource#initialize(RepositoryContext) initialization}.
+ * <p>
+ * This is a required property.
+ * </p>
+ *
+ * @param sourceName the name of the {@link RepositorySource} instance that should be
used for the configuration, or null if
+ * the federated repository instance is to be found in JNDI
+ * @see #getConfigurationSourceName()
+ * @see #setConfigurationPath(String)
+ * @see #setConfigurationWorkspaceName(String)
+ */
+ public synchronized void setConfigurationSourceName( String sourceName ) {
+ if (this.configurationSourceName == sourceName || this.configurationSourceName !=
null
+ && this.configurationSourceName.equals(sourceName)) return; //
unchanged
+ this.configurationSourceName = sourceName;
+ changeConfiguration();
+ }
+
+ /**
+ * Get the name of the workspace in the {@link #getConfigurationSourceName() source}
containing the configuration content for
+ * this source. If this workspace name is null, the default workspace as defined by
that source will be used.
+ *
+ * @return the name of the configuration workspace, or null if the default workspace
for the
+ * {@link #getConfigurationSourceName() configuration source} should be used
+ * @see #getConfigurationSourceName()
+ * @see #setConfigurationWorkspaceName(String)
+ * @see #getConfigurationPath()
+ */
+ public String getConfigurationWorkspaceName() {
+ return configurationWorkspaceName;
+ }
+
+ /**
+ * Set the name of the workspace in the {@link #getConfigurationSourceName() source}
containing the configuration content for
+ * this source. If this workspace name is null, the default workspace as defined by
that source will be used.
+ *
+ * @param workspaceName the name of the configuration workspace, or null if the
default workspace for the
+ * {@link #getConfigurationSourceName() configuration source} should be used
+ * @see #setConfigurationSourceName(String)
+ * @see #setConfigurationPath(String)
+ * @see #getConfigurationWorkspaceName()
+ */
+ public synchronized void setConfigurationWorkspaceName( String workspaceName ) {
+ if (this.configurationWorkspaceName == workspaceName ||
this.configurationWorkspaceName != null
+ && this.configurationWorkspaceName.equals(workspaceName)) return; //
unchanged
+ this.configurationWorkspaceName = workspaceName;
+ changeConfiguration();
+ }
+
+ /**
+ * Get the path in the {@link #getConfigurationWorkspaceName() workspace} of the
{@link #getConfigurationSourceName()
+ * configuration source} where this source can find the content defining its
configuration.
+ * <p>
+ * This is a required property.
+ * </p>
+ *
+ * @return the string array of projection rules, or null if the projection rules
haven't yet been set or if the federated
+ * repository instance is to be found in JNDI
+ * @see #getConfigurationSourceName()
+ * @see #getConfigurationWorkspaceName()
+ * @see #setConfigurationPath(String)
+ */
+ public String getConfigurationPath() {
+ return configurationPath;
+ }
+
+ /**
+ * Set the path in the {@link #getConfigurationWorkspaceName() workspace} of the
{@link #getConfigurationSourceName()
+ * configuration source} where this source can find the content defining its
configuration.
+ * <p>
+ * This is a required property.
+ * </p>
+ *
+ * @param pathInSourceToConfigurationRoot the path within the configuration source to
the node that should be the root of the
+ * configuration information, or null if the {@link
#DEFAULT_CONFIGURATION_PATH default path} should be used
+ * @see #getConfigurationPath()
+ * @see #setConfigurationSourceName(String)
+ * @see #setConfigurationWorkspaceName(String)
+ */
+ public void setConfigurationPath( String pathInSourceToConfigurationRoot ) {
+ if (this.configurationPath == pathInSourceToConfigurationRoot ||
this.configurationPath != null
+ && this.configurationPath.equals(pathInSourceToConfigurationRoot))
return;
+ String path = pathInSourceToConfigurationRoot != null ?
pathInSourceToConfigurationRoot : DEFAULT_CONFIGURATION_PATH;
+ // Ensure one leading slash and one trailing slashes ...
+ this.configurationPath = path = ("/" +
path).replaceAll("^/+", "/").replaceAll("/+$", "")
+ "/";
+ changeConfiguration();
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.connector.RepositorySource#getCapabilities()
+ */
+ public RepositorySourceCapabilities getCapabilities() {
+ return capabilities;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see
org.jboss.dna.graph.connector.RepositorySource#initialize(org.jboss.dna.graph.connector.RepositoryContext)
+ */
+ public synchronized void initialize( RepositoryContext context ) throws
RepositorySourceException {
+ this.context = context;
+ changeConfiguration();
+ }
+
+ /**
+ * Get the repository context that was used to {@link #initialize(RepositoryContext)
initialize} this source.
+ *
+ * @return the context, or null if the source was not yet {@link
#initialize(RepositoryContext) initialized}
+ */
+ /*package*/RepositoryContext getRepositoryContext() {
+ return context;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.connector.RepositorySource#getConnection()
+ */
+ public RepositoryConnection getConnection() throws RepositorySourceException {
+ FederatedRepository config = this.configuration;
+ if (config == null) {
+ synchronized (this) {
+ if (this.configuration == null) {
+ // Check all the properties of this source ...
+ String name = getName();
+ if (name == null) {
+ I18n msg =
GraphI18n.namePropertyIsRequiredForFederatedRepositorySource;
+ throw new RepositorySourceException(getName(),
msg.text("name"));
+ }
+ RepositoryContext repositoryContext = getRepositoryContext();
+ if (repositoryContext == null) {
+ I18n msg = GraphI18n.federatedRepositorySourceMustBeInitialized;
+ throw new RepositorySourceException(getName(),
msg.text("name", name));
+ }
+ String configSource = getConfigurationSourceName();
+ String configWorkspace = getConfigurationWorkspaceName();
+ String configPath = getConfigurationPath();
+ if (configSource == null) {
+ I18n msg =
GraphI18n.propertyIsRequiredForFederatedRepositorySource;
+ throw new RepositorySourceException(getName(),
msg.text("configuration source name", name));
+ }
+
+ // Load the configuration ...
+ this.configuration = loadRepository(name, repositoryContext,
configSource, configWorkspace, configPath);
+ }
+ config = this.configuration;
+ }
+ }
+ return new FederatedRepositoryConnection(config);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see javax.naming.Referenceable#getReference()
+ */
+ public Reference getReference() {
+ String className = getClass().getName();
+ String factoryClassName = this.getClass().getName();
+ Reference ref = new Reference(className, factoryClassName, null);
+
+ ref.add(new StringRefAddr(SOURCE_NAME, getName()));
+ ref.add(new StringRefAddr(RETRY_LIMIT, Integer.toString(getRetryLimit())));
+ ref.add(new StringRefAddr(CONFIGURATION_SOURCE_NAME,
getConfigurationSourceName()));
+ ref.add(new StringRefAddr(CONFIGURATION_WORKSPACE_NAME,
getConfigurationWorkspaceName()));
+ ref.add(new StringRefAddr(CONFIGURATION_PATH, getConfigurationPath()));
+ return ref;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see javax.naming.spi.ObjectFactory#getObjectInstance(java.lang.Object,
javax.naming.Name, javax.naming.Context,
+ * java.util.Hashtable)
+ */
+ public Object getObjectInstance( Object obj,
+ Name name,
+ Context nameCtx,
+ Hashtable<?, ?> environment ) throws Exception
{
+ if (obj instanceof Reference) {
+ Map<String, String> values = new HashMap<String, String>();
+ Reference ref = (Reference)obj;
+ Enumeration<?> en = ref.getAll();
+ while (en.hasMoreElements()) {
+ RefAddr subref = (RefAddr)en.nextElement();
+ if (subref instanceof StringRefAddr) {
+ String key = subref.getType();
+ Object value = subref.getContent();
+ if (value != null) values.put(key, value.toString());
+ }
+ }
+ String sourceName = values.get(SOURCE_NAME);
+ String retryLimit = values.get(RETRY_LIMIT);
+ String configSourceName = values.get(CONFIGURATION_SOURCE_NAME);
+ String configSourceWorkspace = values.get(CONFIGURATION_WORKSPACE_NAME);
+ String configSourcePath = values.get(CONFIGURATION_PATH);
+
+ // Create the source instance ...
+ FederatedRepositorySource source = new FederatedRepositorySource();
+ if (sourceName != null) source.setName(sourceName);
+ if (configSourceName != null)
source.setConfigurationSourceName(configSourceName);
+ if (configSourceWorkspace != null)
source.setConfigurationWorkspaceName(configSourceWorkspace);
+ if (configSourcePath != null) source.setConfigurationPath(configSourcePath);
+ if (retryLimit != null) source.setRetryLimit(Integer.parseInt(retryLimit));
+ return source;
+ }
+ return null;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public int hashCode() {
+ return HashCode.compute(getName());
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean equals( Object obj ) {
+ if (obj == this) return true;
+ if (obj instanceof FederatedRepositorySource) {
+ FederatedRepositorySource that = (FederatedRepositorySource)obj;
+ // The source name must match
+ if (this.getName() == null) {
+ if (that.getName() != null) return false;
+ } else {
+ if (!this.getName().equals(that.getName())) return false;
+ }
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Mark the current configuration (if there is one) as being invalid.
+ */
+ @GuardedBy( "this" )
+ protected void changeConfiguration() {
+ this.configuration = null;
+ }
+
+ /**
+ * Utility to load the current configuration for this source from the {@link
#getConfigurationSourceName() configuration
+ * repository}. This method may only be called after the source is {@link
#initialize(RepositoryContext) initialized}.
+ *
+ * @param name the name of the source; may not be null
+ * @param repositoryContext the repository context; may not be null
+ * @param configSource the name of the configuration source; may not be null
+ * @param configWorkspace the name of the workspace in the configuration source, or
null if the configuration source's default
+ * workspace should be used
+ * @param configPath the path to the node in the configuration workspace that defines
the source; may not be null
+ * @return the configuration; never null
+ * @throws RepositorySourceException if there is a problem with the configuration
+ */
+ protected FederatedRepository loadRepository( String name,
+ RepositoryContext repositoryContext,
+ String configSource,
+ String configWorkspace,
+ String configPath ) throws
RepositorySourceException {
+ // All the required properties have been set ...
+ ExecutionContext executionContext = repositoryContext.getExecutionContext();
+ RepositoryConnectionFactory connectionFactory =
repositoryContext.getRepositoryConnectionFactory();
+ ValueFactories valueFactories = executionContext.getValueFactories();
+ ValueFactory<String> strings = valueFactories.getStringFactory();
+ ValueFactory<Long> longs = valueFactories.getLongFactory();
+ ProjectionParser projectionParser = ProjectionParser.getInstance();
+ NamespaceRegistry registry = executionContext.getNamespaceRegistry();
+
+ try {
+ Graph config = Graph.create(configSource, connectionFactory,
executionContext);
+ if (configWorkspace != null) {
+ configWorkspace = config.useWorkspace(configWorkspace).getName();
+ } else {
+ configWorkspace = config.getCurrentWorkspaceName();
+ }
+
+ // Read the configuration for the federated repository:
+ // Level 1: the node representing the federated repository
+ // Level 2: the "dna:workspaces" node
+ // Level 3: a node for each workspace in the federated repository
+ // Level 4: the "dna:projections" nodes
+ // Level 5: a node below "dna:projections" for each projection,
with properties for the source name,
+ // workspace name, cache expiration time, and projection rules
+ Subgraph repositories = config.getSubgraphOfDepth(5).at(configPath);
+
+ // Get the name of the default workspace ...
+ String defaultWorkspaceName = null;
+ Property defaultWorkspaceNameProperty =
repositories.getRoot().getProperty(DnaLexicon.DEFAULT_WORKSPACE_NAME);
+ if (defaultWorkspaceNameProperty != null) {
+ // Set the name using the property if there is one ...
+ defaultWorkspaceName =
strings.create(defaultWorkspaceNameProperty.getFirstValue());
+ }
+
+ // Get the default expiration time for the repository ...
+ CachePolicy defaultCachePolicy = null;
+ Property timeToExpire =
repositories.getRoot().getProperty(DnaLexicon.TIME_TO_EXPIRE);
+ if (timeToExpire != null && !timeToExpire.isEmpty()) {
+ long timeToCacheInMillis = longs.create(timeToExpire.getFirstValue());
+ defaultCachePolicy = new BasicCachePolicy(timeToCacheInMillis,
TimeUnit.MILLISECONDS).getUnmodifiable();
+ }
+
+ // Level 2: The "dna:workspaces" node ...
+ Node workspacesNode = repositories.getNode(DnaLexicon.WORKSPACES);
+ if (workspacesNode == null) {
+ I18n msg = GraphI18n.requiredNodeDoesNotExistRelativeToNode;
+ throw new
RepositorySourceException(msg.text(DnaLexicon.WORKSPACES.getString(registry),
+
repositories.getLocation().getPath().getString(registry),
+ configWorkspace,
+ configSource));
+ }
+
+ // Level 3: The workspace nodes ...
+ LinkedList<FederatedWorkspace> workspaces = new
LinkedList<FederatedWorkspace>();
+ for (Location workspace : workspacesNode) {
+
+ // Get the name of the workspace ...
+ String workspaceName = null;
+ SubgraphNode workspaceNode = repositories.getNode(workspace);
+ Property workspaceNameProperty =
workspaceNode.getProperty(DnaLexicon.WORKSPACE_NAME);
+ if (workspaceNameProperty != null) {
+ // Set the name using the property if there is one ...
+ workspaceName =
strings.create(workspaceNameProperty.getFirstValue());
+ }
+ if (workspaceName == null) {
+ // Otherwise, set the name using the local name of the workspace node
...
+ workspaceName =
workspace.getPath().getLastSegment().getName().getLocalName();
+ }
+
+ // Level 4: the "dna:projections" node ...
+ Node projectionsNode = workspaceNode.getNode(DnaLexicon.PROJECTIONS);
+ if (projectionsNode == null) {
+ I18n msg = GraphI18n.requiredNodeDoesNotExistRelativeToNode;
+ throw new RepositorySourceException(getName(),
msg.text(DnaLexicon.PROJECTIONS.getString(registry),
+
workspaceNode.getLocation()
+
.getPath()
+
.getString(registry),
+
configWorkspace,
+
configSource));
+ }
+
+ // Level 5: the projection nodes ...
+ List<Projection> sourceProjections = new
LinkedList<Projection>();
+ for (Location projection : projectionsNode) {
+ Node projectionNode = repositories.getNode(projection);
+ sourceProjections.add(createProjection(executionContext,
projectionParser, projectionNode));
+ }
+
+ // Create the federated workspace configuration ...
+ FederatedWorkspace space = new FederatedWorkspace(repositoryContext,
name, workspaceName, sourceProjections,
+ defaultCachePolicy);
+ if (workspaceName.equals(defaultWorkspaceName)) {
+ workspaces.addFirst(space);
+ } else {
+ workspaces.add(space);
+ }
+ }
+
+ // Create the ExecutorService ...
+ ExecutorService executor = Executors.newCachedThreadPool();
+
+ return new FederatedRepository(name, connectionFactory, workspaces,
defaultCachePolicy, executor);
+ } catch (RepositorySourceException t) {
+ throw t; // rethrow
+ } catch (Throwable t) {
+ I18n msg = GraphI18n.errorReadingConfigurationForFederatedRepositorySource;
+ throw new RepositorySourceException(getName(), msg.text(name, configSource,
configWorkspace, configPath), t);
+ }
+ }
+
+ /**
+ * Instantiate the {@link Projection} described by the supplied properties.
+ *
+ * @param context the execution context that should be used to read the
configuration; may not be null
+ * @param projectionParser the projection rule parser that should be used; may not be
null
+ * @param node the node where these properties were found; never null
+ * @return the region instance, or null if it could not be created
+ */
+ protected Projection createProjection( ExecutionContext context,
+ ProjectionParser projectionParser,
+ Node node ) {
+ ValueFactory<String> strings =
context.getValueFactories().getStringFactory();
+
+ Path path = node.getLocation().getPath();
+
+ // Get the source name from the local name of the node ...
+ String sourceName = path.getLastSegment().getName().getLocalName();
+ Property sourceNameProperty = node.getProperty(DnaLexicon.SOURCE_NAME);
+ if (sourceNameProperty != null && !sourceNameProperty.isEmpty()) {
+ // There is a "dna:sourceName" property, so use this instead ...
+ sourceName = strings.create(sourceNameProperty.getFirstValue());
+ }
+ assert sourceName != null;
+
+ // Get the workspace name ...
+ String workspaceName = null;
+ Property workspaceNameProperty = node.getProperty(DnaLexicon.WORKSPACE_NAME);
+ if (workspaceNameProperty != null && !workspaceNameProperty.isEmpty()) {
+ // There is a "dna:workspaceName" property, so use this instead
...
+ workspaceName = strings.create(workspaceNameProperty.getFirstValue());
+ }
+
+ // Get the projection rules ...
+ Projection.Rule[] projectionRules = null;
+ Property projectionRulesProperty =
node.getProperty(DnaLexicon.PROJECTION_RULES);
+ if (projectionRulesProperty != null &&
!projectionRulesProperty.isEmpty()) {
+ String[] projectionRuleStrs =
strings.create(projectionRulesProperty.getValuesAsArray());
+ if (projectionRuleStrs != null && projectionRuleStrs.length != 0) {
+ projectionRules = projectionParser.rulesFromStrings(context,
projectionRuleStrs);
+ }
+ }
+
+ // Is this projection read-only?
+ boolean readOnly = false;
+ Property readOnlyProperty = node.getProperty(DnaLexicon.READ_ONLY);
+ if (readOnlyProperty != null && !readOnlyProperty.isEmpty()) {
+ readOnly =
context.getValueFactories().getBooleanFactory().create(readOnlyProperty.getFirstValue());
+ }
+
+ return new Projection(sourceName, workspaceName, readOnly, projectionRules);
+ }
+
+}
Property changes on:
trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/FederatedRepositorySource.java
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Added:
trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/FederatedRequest.java
===================================================================
---
trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/FederatedRequest.java
(rev 0)
+++
trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/FederatedRequest.java 2009-05-18
15:36:58 UTC (rev 911)
@@ -0,0 +1,174 @@
+/*
+ * JBoss DNA (
http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership. Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of
+ * individual contributors.
+ *
+ * Unless otherwise indicated, all code in JBoss DNA is licensed
+ * to you under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * JBoss DNA is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site:
http://www.fsf.org.
+ */
+package org.jboss.dna.graph.connector.federation;
+
+import java.util.concurrent.CountDownLatch;
+import net.jcip.annotations.NotThreadSafe;
+import org.jboss.dna.graph.request.Request;
+
+/**
+ * A wrapper for a request submitted to the federated repository, and the corresponding
source-specific {@link ProjectedRequest
+ * projected requests}.
+ */
+@NotThreadSafe
+class FederatedRequest {
+ private final Request original;
+ private CountDownLatch forkLatch;
+ private int incompleteCount;
+ private ProjectedRequest first;
+
+ FederatedRequest( Request original ) {
+ this.original = original;
+ }
+
+ public Request original() {
+ return original;
+ }
+
+ public final FederatedRequest add( Request request,
+ boolean isSameLocationAsOriginal,
+ boolean isComplete,
+ Projection projection,
+ Projection secondProjection ) {
+ if (!isComplete) ++incompleteCount;
+ if (first == null) {
+ if (isSameLocationAsOriginal) {
+ first = new MirrorRequest(request, isComplete, projection,
secondProjection);
+ } else {
+ first = new ProjectedRequest(request, isComplete, projection,
secondProjection);
+ }
+ } else {
+ first.addNext(request, isComplete, projection);
+ }
+ return this;
+ }
+
+ public final FederatedRequest add( Request request,
+ boolean isSameLocationAsOriginal,
+ boolean isComplete,
+ Projection projection ) {
+ return add(request, isSameLocationAsOriginal, isComplete, projection, null);
+ }
+
+ public void freeze() {
+ if (incompleteCount > 0 && forkLatch == null) {
+ forkLatch = new CountDownLatch(incompleteCount);
+ }
+ }
+
+ public ProjectedRequest getFirstProjectedRequest() {
+ return first;
+ }
+
+ public boolean hasIncompleteRequests() {
+ return incompleteCount != 0;
+ }
+
+ public CountDownLatch getLatch() {
+ return forkLatch;
+ }
+
+ public void await() throws InterruptedException {
+ if (forkLatch != null) forkLatch.await();
+ }
+
+ class ProjectedRequest {
+ private final Projection projection;
+ private final Projection projection2;
+ private final Request request;
+ private final boolean isComplete;
+ private ProjectedRequest next;
+
+ protected ProjectedRequest( Request request,
+ boolean isComplete,
+ Projection projection,
+ Projection secondProjection ) {
+ this.projection = projection;
+ this.request = request;
+ this.isComplete = isComplete;
+ this.projection2 = secondProjection;
+ }
+
+ public Projection getProjection() {
+ return projection;
+ }
+
+ public Projection getSecondProjection() {
+ return projection2;
+ }
+
+ public Request getRequest() {
+ return request;
+ }
+
+ public boolean isComplete() {
+ return isComplete;
+ }
+
+ public boolean isSameLocation() {
+ return false;
+ }
+
+ public ProjectedRequest next() {
+ return next;
+ }
+
+ public boolean hasNext() {
+ return next != null;
+ }
+
+ protected final ProjectedRequest addNext( Request request,
+ boolean isComplete,
+ Projection projection,
+ Projection secondProjection ) {
+ ProjectedRequest last = this;
+ while (last.next != null) {
+ last = last.next;
+ }
+ last.next = new ProjectedRequest(request, isComplete, projection,
secondProjection);
+ return last.next;
+ }
+
+ protected final ProjectedRequest addNext( Request request,
+ boolean isComplete,
+ Projection projection ) {
+ return addNext(request, isComplete, projection, null);
+ }
+ }
+
+ class MirrorRequest extends ProjectedRequest {
+ protected MirrorRequest( Request request,
+ boolean isComplete,
+ Projection projection,
+ Projection secondProjection ) {
+ super(request, isComplete, projection, secondProjection);
+ }
+
+ @Override
+ public boolean isSameLocation() {
+ return true;
+ }
+ }
+
+}
Property changes on:
trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/FederatedRequest.java
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Added:
trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/FederatedWorkspace.java
===================================================================
---
trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/FederatedWorkspace.java
(rev 0)
+++
trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/FederatedWorkspace.java 2009-05-18
15:36:58 UTC (rev 911)
@@ -0,0 +1,235 @@
+/*
+ * JBoss DNA (
http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership. Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of
+ * individual contributors.
+ *
+ * Unless otherwise indicated, all code in JBoss DNA is licensed
+ * to you under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * JBoss DNA is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site:
http://www.fsf.org.
+ */
+package org.jboss.dna.graph.connector.federation;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import net.jcip.annotations.Immutable;
+import org.jboss.dna.common.collection.Problems;
+import org.jboss.dna.common.collection.ThreadSafeProblems;
+import org.jboss.dna.common.util.CheckArg;
+import org.jboss.dna.graph.ExecutionContext;
+import org.jboss.dna.graph.Location;
+import org.jboss.dna.graph.cache.CachePolicy;
+import org.jboss.dna.graph.connector.RepositoryContext;
+
+/**
+ * The configuration of a single workspace within a {@link FederatedRepositorySource
federated repository}.
+ */
+@Immutable
+class FederatedWorkspace {
+
+ private final RepositoryContext repositoryContext;
+ private final String sourceName;
+ private final String workspaceName;
+ private final List<Projection> projections;
+ private final Map<String, List<Projection>> projectionsBySourceName;
+ private final CachePolicy cachePolicy;
+ private final Problems problems;
+ private final Projector projector;
+
+ /**
+ * Create a configuration for a federated workspace.
+ *
+ * @param repositoryContext the repository context; may not be null
+ * @param sourceName the name of the federated repository; may not be null
+ * @param workspaceName the name of the federated workspace; may not be null
+ * @param projections the source projections; may not be null
+ * @param cachePolicy the cache policy for this workspace; may be null if there is no
policy
+ * @throws IllegalArgumentException if the name is null or is blank
+ */
+ public FederatedWorkspace( RepositoryContext repositoryContext,
+ String sourceName,
+ String workspaceName,
+ Iterable<Projection> projections,
+ CachePolicy cachePolicy ) {
+ CheckArg.isNotNull(repositoryContext, "repositoryContext");
+ CheckArg.isNotNull(sourceName, "sourceName");
+ CheckArg.isNotNull(workspaceName, "workspaceName");
+ this.repositoryContext = repositoryContext;
+ this.workspaceName = workspaceName;
+ this.sourceName = sourceName;
+ this.cachePolicy = cachePolicy;
+ this.problems = new ThreadSafeProblems();
+ List<Projection> projectionList = new ArrayList<Projection>();
+ for (Projection projection : projections) {
+ if (projection == null) continue;
+ if (!projectionList.contains(projection)) {
+ projectionList.add(projection);
+ }
+ }
+ this.projections = Collections.unmodifiableList(projectionList);
+ CheckArg.isNotEmpty(this.projections, "sourceProjections");
+ this.projectionsBySourceName = new HashMap<String,
List<Projection>>();
+ for (Projection projection : this.projections) {
+ String nameOfSource = projection.getSourceName();
+ List<Projection> projectionsForSource =
projectionsBySourceName.get(nameOfSource);
+ if (projectionsForSource == null) {
+ projectionsForSource = new LinkedList<Projection>();
+ projectionsBySourceName.put(nameOfSource, projectionsForSource);
+ }
+ projectionsForSource.add(projection);
+ }
+
+ // Create the (most) appropriate projector ...
+ ExecutionContext context = this.repositoryContext.getExecutionContext();
+ Projector projector = MirrorProjector.with(context, projectionList);
+ if (projector == null) projector = BranchedMirrorProjector.with(context,
projectionList);
+ if (projector == null) projector = OffsetMirrorProjector.with(context,
projectionList);
+ if (projector == null) projector = GeneralProjector.with(context,
projectionList);
+ assert projector != null;
+ this.projector = projector;
+ }
+
+ /**
+ * Get the repository context in which this workspace exists and has been
initialized.
+ *
+ * @return the repository context
+ */
+ public RepositoryContext getRepositoryContext() {
+ return repositoryContext;
+ }
+
+ /**
+ * Get the name of the federated repository.
+ *
+ * @return sourceName
+ */
+ public String getSourceName() {
+ return sourceName;
+ }
+
+ /**
+ * Get the name of this repository
+ *
+ * @return name
+ */
+ public String getName() {
+ return this.workspaceName;
+ }
+
+ /**
+ * Get the cache policy for this workspace
+ *
+ * @return the workspace's cache policy; may be null
+ */
+ public CachePolicy getCachePolicy() {
+ return cachePolicy;
+ }
+
+ /**
+ * Return the problem associated with this configuration. These problems may change
at any time, although the returned
+ * {@link Problems} object is thread-safe.
+ *
+ * @return the thread-safe problems for this configuration
+ */
+ public Problems getProblems() {
+ return problems;
+ }
+
+ /**
+ * Return the unmodifiable list of source projections.
+ *
+ * @return the source projections; never null and never empty
+ */
+ public List<Projection> getProjections() {
+ return projections;
+ }
+
+ /**
+ * Determine whether this workspace has a projection supplied contribution
+ *
+ * @param sourceName the name of the source
+ * @param workspaceName the name of the workspace
+ * @return true if this workspace contains a projection that uses the supplied source
and workspace
+ */
+ public boolean contains( String sourceName,
+ String workspaceName ) {
+ List<Projection> projections =
this.projectionsBySourceName.get(sourceName);
+ if (projections != null) {
+ for (Projection projection : projections) {
+ if (projection.getWorkspaceName().equals(workspaceName)) return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Get the map of projections by their source name. This method provides direct
access to the map used by this instance, and
+ * is mutable. This is meant to be called only by subclasses and tests.
+ *
+ * @return the list of projections for each source
+ */
+ Map<String, List<Projection>> getProjectionsBySourceName() {
+ return projectionsBySourceName;
+ }
+
+ /**
+ * Project the supplied location in the federated repository into the equivalent
projected node(s).
+ *
+ * @param context the execution context in which the content is being accessed; may
not be null
+ * @param location the location in the federated repository; may not be null
+ * @param requiresUpdate true if the operation for which this projection is needed
will update the content in some way, or
+ * false if read-only operations will be performed
+ * @return the projected node, or null if the node does not exist in any projection
+ */
+ public ProjectedNode project( ExecutionContext context,
+ Location location,
+ boolean requiresUpdate ) {
+ return projector.project(context, location, requiresUpdate);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Object#hashCode()
+ */
+ @Override
+ public int hashCode() {
+ return this.workspaceName.hashCode();
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Object#equals(java.lang.Object)
+ */
+ @Override
+ public boolean equals( Object obj ) {
+ if (obj == this) return true;
+ if (obj instanceof FederatedWorkspace) {
+ FederatedWorkspace that = (FederatedWorkspace)obj;
+ if (!this.getSourceName().equals(that.getSourceName())) return false;
+ if (!this.getName().equals(that.getName())) return false;
+ if (!this.getProjections().equals(that.getProjections())) return false;
+ return true;
+ }
+ return false;
+ }
+
+}
Property changes on:
trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/FederatedWorkspace.java
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Added:
trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/ForkRequestProcessor.java
===================================================================
---
trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/ForkRequestProcessor.java
(rev 0)
+++
trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/ForkRequestProcessor.java 2009-05-18
15:36:58 UTC (rev 911)
@@ -0,0 +1,1267 @@
+/*
+ * JBoss DNA (
http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership. Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of
+ * individual contributors.
+ *
+ * Unless otherwise indicated, all code in JBoss DNA is licensed
+ * to you under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * JBoss DNA is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site:
http://www.fsf.org.
+ */
+package org.jboss.dna.graph.connector.federation;
+
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.NoSuchElementException;
+import java.util.Queue;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.Callable;
+import java.util.concurrent.CancellationException;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Future;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.atomic.AtomicBoolean;
+import net.jcip.annotations.NotThreadSafe;
+import org.jboss.dna.common.i18n.I18n;
+import org.jboss.dna.graph.ExecutionContext;
+import org.jboss.dna.graph.GraphI18n;
+import org.jboss.dna.graph.Location;
+import org.jboss.dna.graph.connector.RepositoryConnection;
+import org.jboss.dna.graph.connector.RepositoryConnectionFactory;
+import org.jboss.dna.graph.connector.federation.FederatedRequest.ProjectedRequest;
+import org.jboss.dna.graph.property.DateTime;
+import org.jboss.dna.graph.property.Name;
+import org.jboss.dna.graph.property.Path;
+import org.jboss.dna.graph.property.PathNotFoundException;
+import org.jboss.dna.graph.property.Property;
+import org.jboss.dna.graph.request.CloneWorkspaceRequest;
+import org.jboss.dna.graph.request.CompositeRequest;
+import org.jboss.dna.graph.request.CopyBranchRequest;
+import org.jboss.dna.graph.request.CreateNodeRequest;
+import org.jboss.dna.graph.request.CreateWorkspaceRequest;
+import org.jboss.dna.graph.request.DeleteBranchRequest;
+import org.jboss.dna.graph.request.DeleteChildrenRequest;
+import org.jboss.dna.graph.request.DestroyWorkspaceRequest;
+import org.jboss.dna.graph.request.GetWorkspacesRequest;
+import org.jboss.dna.graph.request.InvalidRequestException;
+import org.jboss.dna.graph.request.InvalidWorkspaceException;
+import org.jboss.dna.graph.request.MoveBranchRequest;
+import org.jboss.dna.graph.request.ReadAllChildrenRequest;
+import org.jboss.dna.graph.request.ReadAllPropertiesRequest;
+import org.jboss.dna.graph.request.ReadBranchRequest;
+import org.jboss.dna.graph.request.ReadNodeRequest;
+import org.jboss.dna.graph.request.ReadPropertyRequest;
+import org.jboss.dna.graph.request.Request;
+import org.jboss.dna.graph.request.UnsupportedRequestException;
+import org.jboss.dna.graph.request.UpdatePropertiesRequest;
+import org.jboss.dna.graph.request.VerifyNodeExistsRequest;
+import org.jboss.dna.graph.request.VerifyWorkspaceRequest;
+import org.jboss.dna.graph.request.processor.RequestProcessor;
+
+/**
+ * This is a {@link RequestProcessor} implementation that is responsible for forking each
incoming request into the
+ * source-specific requests. This processor uses an {@link ExecutorService} to begin
processing the forked requests immediately.
+ * As a result, while this processor processes the incoming requests on the federated
content, the sources may already be
+ * processing previously forked (and source-specific) requests. Thus, it's quite
possible that the sources finish processing their
+ * requests very shortly after this processor finishes its work.
+ * <p>
+ * This processor creates a separate channels for each source to which a request needs to
be submitted. This channel submits all
+ * requests to that source via a single {@link
RepositoryConnection#execute(ExecutionContext, Request) execute} call, meaning that
+ * all requests will be processed by the source within a single atomic operation. The
channels also generally remain open until
+ * this processor completes processing of all incoming requests (that is, until all
requests have been forked), so that any
+ * cancellation of this processor results in cancellation of each channel (and
consequently the cancellation of the
+ * {@link CompositeRequest} that the channel submitted to its source).
+ * </p>
+ *
+ * @see FederatedRepositoryConnection#execute(ExecutionContext, Request)
+ * @see JoinRequestProcessor
+ */
+@NotThreadSafe
+class ForkRequestProcessor extends RequestProcessor {
+
+ /**
+ * A psuedo Request that is used by {@link Channel} to insert into a request queue so
that the queue's iterator knows when
+ * there are no more requests to process.
+ */
+ protected static class LastRequest extends Request {
+ private static final long serialVersionUID = 1L;
+
+ @Override
+ public boolean isReadOnly() {
+ return false;
+ }
+ }
+
+ /**
+ * A pseudo Request that allows addition of a request that, when processed, will
decrement a latch. Since the latch is
+ * supplied by the submitter, this is useful when the submitter wishes to block until
a particular request is processed. The
+ * submitter merely creates a {@link CountDownLatch}, submits their real request
wrapped by a BlockedRequest, and calls
+ * {@link CountDownLatch#await()} on the latch. When <code>await()</code>
returns, the first request has been completed.
+ *
+ * @see ForkRequestProcessor#submit(Request, String, CountDownLatch)
+ * @see ForkRequestProcessor#submitAndAwait(Request, String)
+ */
+ protected static class BlockedRequest extends Request {
+ private static final long serialVersionUID = 1L;
+ protected final Request original;
+ protected final transient CountDownLatch latch;
+
+ protected BlockedRequest( Request original,
+ CountDownLatch latch ) {
+ this.original = original;
+ this.latch = latch;
+ }
+
+ @Override
+ public boolean isReadOnly() {
+ return original.isReadOnly();
+ }
+
+ @Override
+ public boolean isCancelled() {
+ return original.isCancelled();
+ }
+ }
+
+ /**
+ * Represents the channel for a specific source into which this processor submits the
requests for that source. To use, create
+ * a Channel, {@link Channel#start(ExecutorService, ExecutionContext,
RepositoryConnectionFactory) start it}, and then
+ * {@link Channel#add(Request) add} requests (optionally with a {@link
Channel#add(Request, CountDownLatch) latch} or via a
+ * {@link Channel#addAndAwait(Request) add and await}). Finally, call {@link
Channel#done()} when there are no more requests.
+ * <p>
+ * When the channel is {@link Channel#start(ExecutorService, ExecutionContext,
RepositoryConnectionFactory) started}, it
+ * creates a {@link Callable} and submits it to the supplied {@link ExecutorService}.
(The resulting {@link Future} is then
+ * captured so that the channel can be {@link Channel#cancel(boolean) cancelled}.)
The Callable obtains a
+ * {@link RepositoryConnection connection} to the channel's source, and then has
the connection process a single
+ * {@link CompositeRequest} that fronts the queue of Request instances added to this
channel. Because a blocking queue is
+ * used, the CompositeRequest's {@link CompositeRequest#iterator() iterator}
blocks (on {@link Iterator#hasNext()}) until the
+ * next request is available. When {@link Channel#done()} is called, the iterator
stops blocking and completes.
+ * </p>
+ */
+ protected static class Channel {
+ protected final String sourceName;
+ /** The list of all requests that are or have been processed as part of this
channel */
+ protected final LinkedList<Request> allRequests = new
LinkedList<Request>();
+ /** The queue of requests that remain unprocessed */
+ private final BlockingQueue<Request> queue = new
LinkedBlockingQueue<Request>();
+ /** The CompositeRequest that is submitted to the underlying processor */
+ protected final CompositeRequest composite;
+ /** The Future that is submitted to the ExecutorService to do the processing */
+ protected Future<String> future;
+ /** Flag that defines whether the channel has processed all requests */
+ protected final AtomicBoolean done = new AtomicBoolean(false);
+
+ /**
+ * Create a new channel that operates against the supplied source.
+ *
+ * @param sourceName the name of the repository source used to execute this
channel's {@link #allRequests() requests}; may
+ * not be null or empty
+ */
+ protected Channel( final String sourceName ) {
+ assert sourceName != null;
+ this.sourceName = sourceName;
+ this.composite = new CompositeRequest(false) {
+ private static final long serialVersionUID = 1L;
+ private final LinkedList<Request> allRequests =
Channel.this.allRequests;
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.request.CompositeRequest#iterator()
+ */
+ @Override
+ public Iterator<Request> iterator() {
+ return createIterator();
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.request.CompositeRequest#getRequests()
+ */
+ @Override
+ public List<Request> getRequests() {
+ return allRequests;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.request.CompositeRequest#size()
+ */
+ @Override
+ public int size() {
+ return done.get() ? allRequests.size() :
CompositeRequest.UNKNOWN_NUMBER_OF_REQUESTS;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.request.Request#cancel()
+ */
+ @Override
+ public void cancel() {
+ done.set(true);
+ }
+ };
+ }
+
+ /**
+ * Utility method to create an iterator over the requests in this channel. This
really should be called once
+ *
+ * @return the iterator over the channels
+ */
+ protected Iterator<Request> createIterator() {
+ final BlockingQueue<Request> queue = this.queue;
+ return new Iterator<Request>() {
+ private BlockedRequest previousBlocked;
+ private Request next;
+
+ public boolean hasNext() {
+ // If the previous request was a blocked request, then we need to
decrement its latch ...
+ if (previousBlocked != null && previousBlocked.latch != null)
{
+ previousBlocked.latch.countDown();
+ }
+
+ // If next still has a request, then 'hasNext()' has been
called multiple times in a row
+ if (next != null) return true;
+
+ // Now, block for a next item (this blocks) ...
+ try {
+ next = queue.take();
+ } catch (InterruptedException e) {
+ // This happens when the federated connector has been told to
shutdown now, and it shuts down
+ // its executor (the worker pool) immediately by interrupting
each in-use thread.
+ // In this case, we should consider there to be more more
requests ...
+ try {
+ return false;
+ } finally {
+ // reset the interrupted status ...
+ Thread.interrupted();
+ }
+ }
+ if (next instanceof LastRequest) {
+ return false;
+ }
+ return next != null;
+ }
+
+ public Request next() {
+ if (next == null) {
+ // Must have been called without first calling
'hasNext()' ...
+ try {
+ next = queue.take();
+ } catch (InterruptedException e) {
+ // This happens when the federated connector has been told to
shutdown now, and it shuts down
+ // its executor (the worker pool) immediately by interrupting
each in-use thread.
+ // In this case, we should consider there to be more more
requests (again, this case
+ // is when 'next()' has been called without calling
'hasNext()') ...
+ try {
+ throw new NoSuchElementException();
+ } finally {
+ // reset the interrupted status ...
+ Thread.interrupted();
+ }
+ }
+ }
+ // If this request is a blocked request, we need to return the
wrapped request and record the blocked request
+ if (next instanceof BlockedRequest) {
+ previousBlocked = (BlockedRequest)next;
+ next = null;
+ return previousBlocked.original;
+ }
+ previousBlocked = null;
+ if (next != null) {
+ Request result = next;
+ next = null;
+ return result;
+ }
+ throw new NoSuchElementException();
+ }
+
+ public void remove() {
+ throw new UnsupportedOperationException();
+ }
+ };
+ }
+
+ /**
+ * Begins processing any requests that have been {@link #add(Request) added} to
this channel. Processing is done by
+ * submitting the channel to the supplied executor.
+ *
+ * @param executor the executor that is to do the work; may not be null
+ * @param context the execution context in which the work is to be performed; may
not be null
+ * @param connectionFactory the connection factory that should be used to create
connections; may not be null
+ * @throws IllegalStateException if this channel has already been started
+ */
+ protected void start( final ExecutorService executor,
+ final ExecutionContext context,
+ final RepositoryConnectionFactory connectionFactory ) {
+ assert executor != null;
+ assert context != null;
+ assert connectionFactory != null;
+ if (this.future != null) {
+ throw new IllegalStateException();
+ }
+ this.future = executor.submit(new Callable<String>() {
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.util.concurrent.Callable#call()
+ */
+ public String call() throws Exception {
+ final RepositoryConnection connection =
connectionFactory.createConnection(sourceName);
+ try {
+ connection.execute(context, composite);
+ } finally {
+ connection.close();
+ }
+ return sourceName;
+ }
+ });
+ }
+
+ /**
+ * Add the request to this channel for asynchronous processing. This method is
called by the
+ * {@link ForkRequestProcessor#submit(Request, String)} method.
+ *
+ * @param request the request to be submitted; may not be null
+ * @throws IllegalStateException if this channel has already been marked as
{@link #done()}
+ */
+ protected void add( Request request ) {
+ if (done.get()) {
+ throw new
IllegalStateException(GraphI18n.unableToAddRequestToChannelThatIsDone.text(sourceName,
request));
+ }
+ assert request != null;
+ this.allRequests.add(request);
+ this.queue.add(request);
+ }
+
+ /**
+ * Add the request to this channel for asynchronous processing, and supply a
{@link CountDownLatch count-down latch} that
+ * should be {@link CountDownLatch#countDown() decremented} when this request is
completed.
+ *
+ * @param request the request to be submitted; may not be null
+ * @param latch the count-down latch that should be decremented when
<code>request</code> has been completed; may not be
+ * null
+ * @return the same latch that was supplied, for method chaining purposes; never
null
+ * @throws IllegalStateException if this channel has already been marked as
{@link #done()}
+ */
+ protected CountDownLatch add( Request request,
+ CountDownLatch latch ) {
+ if (done.get()) {
+ throw new
IllegalStateException(GraphI18n.unableToAddRequestToChannelThatIsDone.text(sourceName,
request));
+ }
+ assert request != null;
+ assert latch != null;
+ // Submit the request for processing ...
+ this.allRequests.add(request);
+ this.queue.add(new BlockedRequest(request, latch));
+ return latch;
+ }
+
+ /**
+ * Add the request to this channel for asynchronous processing, and supply a
{@link CountDownLatch count-down latch} that
+ * should be {@link CountDownLatch#countDown() decremented} when this request is
completed. This method is called by the
+ * {@link ForkRequestProcessor#submitAndAwait(Request, String)} method.
+ *
+ * @param request the request to be submitted; may not be null
+ * @throws InterruptedException if the current thread is interrupted while
waiting
+ */
+ protected void addAndAwait( Request request ) throws InterruptedException {
+ // Add the request with a latch, then block until the request has completed
...
+ add(request, new CountDownLatch(1)).await();
+ }
+
+ /**
+ * Mark this source as having no more requests to process.
+ */
+ protected void done() {
+ this.done.set(true);
+ this.queue.add(new LastRequest());
+ }
+
+ /**
+ * Return whether this channel has been {@link #done() marked as done}.
+ *
+ * @return true if the channel was marked as done, or false otherwise
+ */
+ protected boolean isDone() {
+ return done.get();
+ }
+
+ /**
+ * Cancel this forked channel, stopping work as soon as possible. If the channel
has not yet been started, this method
+ *
+ * @param mayInterruptIfRunning true if the channel is still being worked on, and
the thread on which its being worked on
+ * may be interrupted, or false if the channel should be allowed to finish
if it is already in work.
+ */
+ public void cancel( boolean mayInterruptIfRunning ) {
+ if (this.future == null || this.future.isDone() || this.future.isCancelled())
return;
+
+ // Mark the composite as cancelled first, so that the next composed request
will be marked as
+ // cancelled.
+ this.composite.cancel();
+
+ // Now mark the channel as being done ...
+ done();
+
+ // Now, mark the channel as being cancelled (do allow interrupting the worker
thread) ...
+ this.future.cancel(mayInterruptIfRunning);
+ }
+
+ /**
+ * Return whether this channel has been {@link #start(ExecutorService,
ExecutionContext, RepositoryConnectionFactory)
+ * started}.
+ *
+ * @return true if this channel was started, or false otherwise
+ */
+ public boolean isStarted() {
+ return this.future != null;
+ }
+
+ /**
+ * Return whether this channel has completed all of its work.
+ *
+ * @return true if the channel was started and is complete, or false otherwise
+ */
+ public boolean isComplete() {
+ return this.future != null && this.future.isDone();
+ }
+
+ /**
+ * Await until this channel has completed.
+ *
+ * @throws CancellationException if the channel was cancelled
+ * @throws ExecutionException if the channel execution threw an exception
+ * @throws InterruptedException if the current thread is interrupted while
waiting
+ */
+ protected void await() throws ExecutionException, InterruptedException,
CancellationException {
+ this.future.get();
+ }
+
+ /**
+ * Get all the requests that were submitted to this queue. The resulting list is
the actual list that is appended when
+ * requests are added, and may change until the channel is marked as {@link
#done() done}.
+ *
+ * @return all of the requests that were submitted to this channel; never null
+ */
+ protected List<Request> allRequests() {
+ return allRequests;
+ }
+
+ /**
+ * Get the name of the source that this channel uses.
+ *
+ * @return the source name; never null
+ */
+ protected String sourceName() {
+ return sourceName;
+ }
+ }
+
+ private final FederatedRepository repository;
+ private final ExecutorService executor;
+ private final RepositoryConnectionFactory connectionFactory;
+ private final Map<String, Channel> channelBySourceName = new HashMap<String,
Channel>();
+ private final Queue<FederatedRequest> federatedRequestQueue;
+
+ /**
+ * Create a new fork processor
+ *
+ * @param repository the federated repository configuration; never null
+ * @param context the execution context in which this processor is executing; may not
be null
+ * @param now the timestamp representing the current time in UTC; may not be null
+ * @param federatedRequestQueue the queue into which should be placed the {@link
FederatedRequest} objects created by this
+ * processor that still must be post-processed
+ */
+ public ForkRequestProcessor( FederatedRepository repository,
+ ExecutionContext context,
+ DateTime now,
+ Queue<FederatedRequest> federatedRequestQueue ) {
+ super(repository.getSourceName(), context, now);
+ this.repository = repository;
+ this.executor = this.repository.getExecutor();
+ this.connectionFactory = this.repository.getConnectionFactory();
+ this.federatedRequestQueue = federatedRequestQueue;
+ assert this.executor != null;
+ assert this.connectionFactory != null;
+ assert this.federatedRequestQueue != null;
+ }
+
+ /**
+ * Asynchronously submit a request to the supplied source. This is typically called
when the forked requests are not needed
+ * before continuing.
+ *
+ * @param request the request to be submitted; may not be null
+ * @param sourceName the name of the source; may not be null
+ * @see #submitAndAwait(Request, String)
+ * @see #submit(Request, String, CountDownLatch)
+ */
+ protected void submit( Request request,
+ String sourceName ) {
+ assert request != null;
+ Channel channel = channelBySourceName.get(sourceName);
+ if (channel == null) {
+ channel = new Channel(sourceName);
+ channelBySourceName.put(sourceName, channel);
+ channel.start(executor, getExecutionContext(), connectionFactory);
+ }
+ channel.add(request);
+ }
+
+ /**
+ * Submit a request to the supplied source, and block until the request has been
processed. This method is typically used when
+ * a federated request is forked into multiple source-specific requests, but the
output of a source-specific request is
+ * required before forking other source-specific requests. This pattern is common in
requests that update one source and any
+ * information not stored by that source needs to be stored in another source.
+ *
+ * @param request the request to be submitted; may not be null
+ * @param sourceName the name of the source; may not be null
+ * @throws InterruptedException if the current thread is interrupted while waiting
+ * @see #submit(Request, String)
+ * @see #submit(Request, String, CountDownLatch)
+ */
+ protected void submitAndAwait( Request request,
+ String sourceName ) throws InterruptedException {
+ assert request != null;
+ Channel channel = channelBySourceName.get(sourceName);
+ if (channel == null) {
+ channel = new Channel(sourceName);
+ channelBySourceName.put(sourceName, channel);
+ channel.start(executor, getExecutionContext(), connectionFactory);
+ }
+ channel.addAndAwait(request);
+ }
+
+ /**
+ * Submit a request to the supplied source, and have the supplied {@link
CountDownLatch latch} be
+ * {@link CountDownLatch#countDown() decremented} when the request has been
completed. Note that the same latch can be used
+ * multiple times.
+ * <p>
+ * This method is typically used when a federated request is forked into multiple
source-specific requests, but the output of
+ * a source-specific request is required before forking other source-specific
requests. This pattern is common in requests
+ * that update one source and any information not stored by that source needs to be
stored in another source.
+ * </p>
+ *
+ * @param request the request to be submitted; may not be null
+ * @param sourceName the name of the source; may not be null
+ * @param latch the count-down latch; may not be null
+ * @see #submit(Request, String)
+ * @see #submitAndAwait(Request, String)
+ */
+ protected void submit( Request request,
+ String sourceName,
+ CountDownLatch latch ) {
+ assert request != null;
+ Channel channel = channelBySourceName.get(sourceName);
+ if (channel == null) {
+ channel = new Channel(sourceName);
+ channelBySourceName.put(sourceName, channel);
+ channel.start(executor, getExecutionContext(), connectionFactory);
+ }
+ channel.add(request, latch);
+ }
+
+ /**
+ * Await until all of this processor's channels have completed.
+ *
+ * @throws CancellationException if the channel was cancelled
+ * @throws ExecutionException if the channel execution threw an exception
+ * @throws InterruptedException if the current thread is interrupted while waiting
+ */
+ public void await() throws ExecutionException, InterruptedException,
CancellationException {
+ for (Channel channel : channelBySourceName.values()) {
+ channel.await();
+ }
+ }
+
+ /**
+ * Utility to obtain the federated workspace referenced by the request. This method
supports using the default workspace if
+ * the workspace name is null. If no such workspace, the request is marked with an
appropriate error.
+ *
+ * @param request the request; may not be null
+ * @param workspaceName the name of the workspace; may be null if the default
workspace should be used
+ * @return the federated workspace, or null if none was found
+ */
+ protected final FederatedWorkspace getWorkspace( Request request,
+ String workspaceName ) {
+ try {
+ return repository.getWorkspace(workspaceName);
+ } catch (InvalidWorkspaceException e) {
+ request.setError(e);
+ } catch (Throwable e) {
+ request.setError(e);
+ }
+ return null;
+ }
+
+ protected final String readable( Location location ) {
+ return location.getString(getExecutionContext().getNamespaceRegistry());
+ }
+
+ protected final String readable( Name name ) {
+ return name.getString(getExecutionContext().getNamespaceRegistry());
+ }
+
+ /**
+ * Utility method to project the specified location into the federated
repository's sources.
+ *
+ * @param location the location that should be projected; may not be null
+ * @param workspaceName the name of the workspace, or null if the default workspace
should be used
+ * @param request the request; may not be null
+ * @param requiresUpdate true if the operation for which this projection is needed
will update the content in some way, or
+ * false if read-only operations will be performed
+ * @return the projected node, or null if there was no projected node (and an error
was set on the request)
+ */
+ protected final ProjectedNode project( Location location,
+ String workspaceName,
+ Request request,
+ boolean requiresUpdate ) {
+ FederatedWorkspace workspace = getWorkspace(request, workspaceName);
+ if (workspace == null) return null;
+
+ ProjectedNode projectedNode = workspace.project(getExecutionContext(), location,
requiresUpdate);
+ if (projectedNode == null) {
+ I18n msg = GraphI18n.locationCannotBeProjectedIntoWorkspaceAndSource;
+ Path root =
getExecutionContext().getValueFactories().getPathFactory().createRootPath();
+ request.setError(new PathNotFoundException(location, root,
msg.text(readable(location),
+
workspace.getName(),
+
repository.getSourceName())));
+ }
+ return projectedNode;
+ }
+
+ protected void submit( FederatedRequest request ) {
+ request.freeze();
+ if (request.hasIncompleteRequests()) {
+ // Submit the projected requests ...
+ ProjectedRequest projected = request.getFirstProjectedRequest();
+ while (projected != null) {
+ if (!projected.isComplete()) {
+ submit(projected.getRequest(),
projected.getProjection().getSourceName(), request.getLatch());
+ }
+ projected = projected.next();
+ }
+ }
+ // Record this federated request ...
+ this.federatedRequestQueue.add(request);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see
org.jboss.dna.graph.request.processor.RequestProcessor#process(org.jboss.dna.graph.request.VerifyNodeExistsRequest)
+ */
+ @Override
+ public void process( VerifyNodeExistsRequest request ) {
+ // Figure out where this request is projected ...
+ ProjectedNode projectedNode = project(request.at(), request.inWorkspace(),
request, false);
+ if (projectedNode == null) return;
+
+ // Create the federated request ...
+ FederatedRequest federatedRequest = new FederatedRequest(request);
+ while (projectedNode != null) {
+ if (projectedNode.isPlaceholder()) {
+ PlaceholderNode placeholder = projectedNode.asPlaceholder();
+ // Create a request and set the results ...
+ ReadNodeRequest placeholderRequest = new ReadNodeRequest(request.at(),
request.inWorkspace());
+ placeholderRequest.addChildren(placeholder.children());
+ placeholderRequest.addProperties(placeholder.properties().values());
+ placeholderRequest.setActualLocationOfNode(placeholder.location());
+ federatedRequest.add(placeholderRequest, true, true, null);
+ } else if (projectedNode.isProxy()) {
+ ProxyNode proxy = projectedNode.asProxy();
+ // Create and submit a request for the projection ...
+ VerifyNodeExistsRequest pushDownRequest = new
VerifyNodeExistsRequest(proxy.location(), proxy.workspaceName());
+ federatedRequest.add(pushDownRequest, proxy.isSameLocationAsOriginal(),
false, proxy.projection());
+ }
+ projectedNode = projectedNode.next();
+ }
+ // Submit for processing ...
+ submit(federatedRequest);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see
org.jboss.dna.graph.request.processor.RequestProcessor#process(org.jboss.dna.graph.request.ReadNodeRequest)
+ */
+ @Override
+ public void process( ReadNodeRequest request ) {
+ // Figure out where this request is projected ...
+ ProjectedNode projectedNode = project(request.at(), request.inWorkspace(),
request, false);
+ if (projectedNode == null) return;
+
+ // Create the federated request ...
+ FederatedRequest federatedRequest = new FederatedRequest(request);
+ while (projectedNode != null) {
+ if (projectedNode.isPlaceholder()) {
+ PlaceholderNode placeholder = projectedNode.asPlaceholder();
+ // Create a request and set the results ...
+ ReadNodeRequest placeholderRequest = new
ReadNodeRequest(placeholder.location(), request.inWorkspace());
+ placeholderRequest.addChildren(placeholder.children());
+ placeholderRequest.addProperties(placeholder.properties().values());
+ placeholderRequest.setActualLocationOfNode(placeholder.location());
+ federatedRequest.add(placeholderRequest, true, true, null);
+ } else if (projectedNode.isProxy()) {
+ ProxyNode proxy = projectedNode.asProxy();
+ // Create and submit a request for the projection ...
+ ReadNodeRequest pushDownRequest = new ReadNodeRequest(proxy.location(),
proxy.workspaceName());
+ federatedRequest.add(pushDownRequest, proxy.isSameLocationAsOriginal(),
false, proxy.projection());
+ }
+ projectedNode = projectedNode.next();
+ }
+ // Submit for processing ...
+ submit(federatedRequest);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see
org.jboss.dna.graph.request.processor.RequestProcessor#process(org.jboss.dna.graph.request.ReadAllChildrenRequest)
+ */
+ @Override
+ public void process( ReadAllChildrenRequest request ) {
+ // Figure out where this request is projected ...
+ ProjectedNode projectedNode = project(request.of(), request.inWorkspace(),
request, false);
+ if (projectedNode == null) return;
+
+ // Create the federated request ...
+ FederatedRequest federatedRequest = new FederatedRequest(request);
+ while (projectedNode != null) {
+ if (projectedNode.isPlaceholder()) {
+ PlaceholderNode placeholder = projectedNode.asPlaceholder();
+ // Create a request and set the results ...
+ ReadAllChildrenRequest placeholderRequest = new
ReadAllChildrenRequest(placeholder.location(),
+
request.inWorkspace());
+ placeholderRequest.addChildren(placeholder.children());
+ placeholderRequest.setActualLocationOfNode(placeholder.location());
+ federatedRequest.add(placeholderRequest, true, true, null);
+ } else if (projectedNode.isProxy()) {
+ ProxyNode proxy = projectedNode.asProxy();
+ // Create and submit a request for the projection ...
+ ReadAllChildrenRequest pushDownRequest = new
ReadAllChildrenRequest(proxy.location(), proxy.workspaceName());
+ federatedRequest.add(pushDownRequest, proxy.isSameLocationAsOriginal(),
false, proxy.projection());
+ }
+ projectedNode = projectedNode.next();
+ }
+ // Submit for processing ...
+ submit(federatedRequest);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see
org.jboss.dna.graph.request.processor.RequestProcessor#process(org.jboss.dna.graph.request.ReadAllPropertiesRequest)
+ */
+ @Override
+ public void process( ReadAllPropertiesRequest request ) {
+ // Figure out where this request is projected ...
+ ProjectedNode projectedNode = project(request.at(), request.inWorkspace(),
request, false);
+ if (projectedNode == null) return;
+
+ // Create the federated request ...
+ FederatedRequest federatedRequest = new FederatedRequest(request);
+ while (projectedNode != null) {
+ if (projectedNode.isPlaceholder()) {
+ PlaceholderNode placeholder = projectedNode.asPlaceholder();
+ // Create a request and set the results ...
+ ReadAllPropertiesRequest placeholderRequest = new
ReadAllPropertiesRequest(placeholder.location(),
+
request.inWorkspace());
+ placeholderRequest.addProperties(placeholder.properties().values());
+ placeholderRequest.setActualLocationOfNode(placeholder.location());
+ federatedRequest.add(placeholderRequest, true, true, null);
+ } else if (projectedNode.isProxy()) {
+ ProxyNode proxy = projectedNode.asProxy();
+ // Create and submit a request for the projection ...
+ ReadAllPropertiesRequest pushDownRequest = new
ReadAllPropertiesRequest(proxy.location(), proxy.workspaceName());
+ federatedRequest.add(pushDownRequest, proxy.isSameLocationAsOriginal(),
false, proxy.projection());
+ }
+ projectedNode = projectedNode.next();
+ }
+ // Submit for processing ...
+ submit(federatedRequest);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see
org.jboss.dna.graph.request.processor.RequestProcessor#process(org.jboss.dna.graph.request.ReadPropertyRequest)
+ */
+ @Override
+ public void process( ReadPropertyRequest request ) {
+ // Figure out where this request is projected ...
+ ProjectedNode projectedNode = project(request.on(), request.inWorkspace(),
request, false);
+ if (projectedNode == null) return;
+
+ // Create the federated request ...
+ FederatedRequest federatedRequest = new FederatedRequest(request);
+ while (projectedNode != null) {
+ if (projectedNode.isPlaceholder()) {
+ PlaceholderNode placeholder = projectedNode.asPlaceholder();
+ // Create a request and set the results ...
+ ReadPropertyRequest placeholderRequest = new
ReadPropertyRequest(placeholder.location(), request.inWorkspace(),
+
request.named());
+ Property property = placeholder.properties().get(request.named());
+ placeholderRequest.setProperty(property);
+ placeholderRequest.setActualLocationOfNode(placeholder.location());
+ federatedRequest.add(placeholderRequest, true, true, null);
+ } else if (projectedNode.isProxy()) {
+ ProxyNode proxy = projectedNode.asProxy();
+ // Create and submit a request for the projection ...
+ ReadPropertyRequest pushDownRequest = new
ReadPropertyRequest(proxy.location(), proxy.workspaceName(),
+
request.named());
+ federatedRequest.add(pushDownRequest, proxy.isSameLocationAsOriginal(),
false, proxy.projection());
+ }
+ projectedNode = projectedNode.next();
+ }
+ // Submit for processing ...
+ submit(federatedRequest);
+ }
+
+ /**
+ * {@inheritDoc}
+ * <p>
+ * This implementation creates a single {@link FederatedRequest} and a {@link
ReadBranchRequest} for each {@link ProxyNode}
+ * and one {@link ReadNodeRequest} for each {@link PlaceholderNode}.
+ * </p>
+ *
+ * @see
org.jboss.dna.graph.request.processor.RequestProcessor#process(org.jboss.dna.graph.request.ReadBranchRequest)
+ */
+ @Override
+ public void process( ReadBranchRequest request ) {
+ // Figure out where this request is projected ...
+ ProjectedNode projectedNode = project(request.at(), request.inWorkspace(),
request, false);
+ if (projectedNode == null) return;
+
+ // Create the federated request ...
+ FederatedRequest federatedRequest = new FederatedRequest(request);
+ FederatedWorkspace workspace = getWorkspace(request, request.inWorkspace());
+
+ // And process the branch, creating ReadNodeRequests for each placeholder, and
ReadBranchRequests for each proxy node...
+ if (request.maximumDepth() > 0) {
+ processBranch(federatedRequest, projectedNode, workspace,
request.maximumDepth());
+ }
+
+ // Submit the requests for processing ...
+ submit(federatedRequest);
+ }
+
+ /**
+ * A method used recursively to add {@link ReadNodeRequest}s and {@link
ReadBranchRequest}s for each
+ *
+ * @param federatedRequest
+ * @param projectedNode
+ * @param workspace
+ * @param maxDepth
+ */
+ protected void processBranch( FederatedRequest federatedRequest,
+ ProjectedNode projectedNode,
+ FederatedWorkspace workspace,
+ int maxDepth ) {
+ assert maxDepth > 0;
+ while (projectedNode != null) {
+ if (projectedNode.isPlaceholder()) {
+ PlaceholderNode placeholder = projectedNode.asPlaceholder();
+ // Create a request and set the results ...
+ ReadNodeRequest placeholderRequest = new
ReadNodeRequest(placeholder.location(), workspace.getName());
+ placeholderRequest.addChildren(placeholder.children());
+ placeholderRequest.addProperties(placeholder.properties().values());
+ placeholderRequest.setActualLocationOfNode(placeholder.location());
+ federatedRequest.add(placeholderRequest, true, true, null);
+ if (maxDepth > 1) {
+ ExecutionContext context = getExecutionContext();
+ // For each child of the placeholder node ...
+ for (Location child : placeholder.children()) {
+ ProjectedNode projectedSubnode = workspace.project(context,
child, false);
+ // Call recursively, but reduce the max depth
+ processBranch(federatedRequest, projectedSubnode, workspace,
maxDepth - 1);
+ }
+ }
+ } else if (projectedNode.isProxy()) {
+ ProxyNode proxy = projectedNode.asProxy();
+ // Create and submit a request for the projection ...
+ ReadBranchRequest pushDownRequest = new
ReadBranchRequest(proxy.location(), proxy.workspaceName(), maxDepth);
+ federatedRequest.add(pushDownRequest, proxy.isSameLocationAsOriginal(),
false, proxy.projection());
+ }
+ projectedNode = projectedNode.next();
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see
org.jboss.dna.graph.request.processor.RequestProcessor#process(org.jboss.dna.graph.request.CreateNodeRequest)
+ */
+ @Override
+ public void process( CreateNodeRequest request ) {
+ // Figure out where this request is projected ...
+ ProjectedNode projectedNode = project(request.under(), request.inWorkspace(),
request, true);
+ if (projectedNode == null) return;
+
+ // Create the federated request ...
+ FederatedRequest federatedRequest = new FederatedRequest(request);
+
+ // Any non-read request should be submitted to the first ProxyNode ...
+ while (projectedNode != null) {
+ if (projectedNode.isProxy()) {
+ ProxyNode proxy = projectedNode.asProxy();
+ // Create and submit a request for the projection ...
+ CreateNodeRequest pushDownRequest = new
CreateNodeRequest(proxy.location(), proxy.workspaceName(),
+
request.named(), request.conflictBehavior(),
+
request.properties());
+ federatedRequest.add(pushDownRequest, proxy.isSameLocationAsOriginal(),
false, proxy.projection());
+
+ // Submit the requests for processing and then STOP ...
+ submit(federatedRequest);
+ return;
+ }
+ assert projectedNode.isPlaceholder();
+ projectedNode = projectedNode.next();
+ }
+ // Unable to perform this create ...
+ String msg =
GraphI18n.unableToCreateNodeUnderPlaceholder.text(readable(request.named()),
+
readable(request.under()),
+
request.inWorkspace(),
+ getSourceName());
+ request.setError(new UnsupportedRequestException(msg));
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see
org.jboss.dna.graph.request.processor.RequestProcessor#process(org.jboss.dna.graph.request.UpdatePropertiesRequest)
+ */
+ @Override
+ public void process( UpdatePropertiesRequest request ) {
+ // Figure out where this request is projected ...
+ ProjectedNode projectedNode = project(request.on(), request.inWorkspace(),
request, true);
+ if (projectedNode == null) return;
+
+ // Create the federated request ...
+ FederatedRequest federatedRequest = new FederatedRequest(request);
+
+ // Any non-read request should be submitted to the first ProxyNode ...
+ while (projectedNode != null) {
+ if (projectedNode.isProxy()) {
+ ProxyNode proxy = projectedNode.asProxy();
+ // Create and submit a request for the projection ...
+ UpdatePropertiesRequest pushDownRequest = new
UpdatePropertiesRequest(proxy.location(), proxy.workspaceName(),
+
request.properties());
+ federatedRequest.add(pushDownRequest, proxy.isSameLocationAsOriginal(),
false, proxy.projection());
+
+ // Submit the requests for processing and then STOP ...
+ submit(federatedRequest);
+ return;
+ }
+ assert projectedNode.isPlaceholder();
+ projectedNode = projectedNode.next();
+ }
+ // Unable to perform this update ...
+ String msg = GraphI18n.unableToUpdatePlaceholder.text(readable(request.on()),
request.inWorkspace(), getSourceName());
+ request.setError(new UnsupportedRequestException(msg));
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see
org.jboss.dna.graph.request.processor.RequestProcessor#process(org.jboss.dna.graph.request.DeleteBranchRequest)
+ */
+ @Override
+ public void process( DeleteBranchRequest request ) {
+ // Figure out where this request is projected ...
+ ProjectedNode projectedNode = project(request.at(), request.inWorkspace(),
request, true);
+ if (projectedNode == null) return;
+
+ // Create the federated request ...
+ FederatedRequest federatedRequest = new FederatedRequest(request);
+
+ // A delete should be executed against any ProxyNode that applies ...
+ FederatedWorkspace workspace = getWorkspace(request, request.inWorkspace());
+ boolean submit = deleteBranch(federatedRequest, projectedNode, workspace,
getExecutionContext());
+ if (submit) {
+ // Submit the requests for processing and then STOP ...
+ submit(federatedRequest);
+ } else {
+ // Unable to perform this delete ...
+ String msg = GraphI18n.unableToDeletePlaceholder.text(readable(request.at()),
request.inWorkspace(), getSourceName());
+ request.setError(new UnsupportedRequestException(msg));
+ }
+ }
+
+ protected boolean deleteBranch( FederatedRequest federatedRequest,
+ ProjectedNode projectedNode,
+ FederatedWorkspace workspace,
+ ExecutionContext context ) {
+ boolean submit = false;
+ while (projectedNode != null) {
+ if (projectedNode.isProxy()) {
+ ProxyNode proxy = projectedNode.asProxy();
+ // Is this proxy represent the top level node of the projection?
+ if (proxy.isTopLevelNode()) {
+ // Then we want to delete everything *underneath* the node, but we
don't want to delete
+ // the node itself since it is the node being projected and must
exist in order for the
+ // projection to work.
+ DeleteChildrenRequest pushDownRequest = new
DeleteChildrenRequest(proxy.location(), proxy.workspaceName());
+ federatedRequest.add(pushDownRequest,
proxy.isSameLocationAsOriginal(), false, proxy.projection());
+ } else {
+ // Create and submit a request for the projection ...
+ DeleteBranchRequest pushDownRequest = new
DeleteBranchRequest(proxy.location(), proxy.workspaceName());
+ federatedRequest.add(pushDownRequest,
proxy.isSameLocationAsOriginal(), false, proxy.projection());
+ }
+ submit = true;
+ } else if (projectedNode.isPlaceholder()) {
+ PlaceholderNode placeholder = projectedNode.asPlaceholder();
+ // Create a delete for this placeholder, but mark it completed. This is
needed to know
+ // which placeholders were being deleted.
+ DeleteBranchRequest delete = new
DeleteBranchRequest(placeholder.location(), workspace.getName());
+ delete.setActualLocationOfNode(placeholder.location());
+ federatedRequest.add(delete, true, true, null);
+ // Create and submit a request for each proxy below this placeholder ...
+ // For each child of the placeholder node ...
+ for (Location child : placeholder.children()) {
+ ProjectedNode projectedSubnode = workspace.project(context, child,
true);
+ // Call recursively ...
+ submit = deleteBranch(federatedRequest, projectedSubnode, workspace,
context);
+ }
+ }
+ projectedNode = projectedNode.next();
+ }
+ return submit;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see
org.jboss.dna.graph.request.processor.RequestProcessor#process(org.jboss.dna.graph.request.CopyBranchRequest)
+ */
+ @Override
+ public void process( CopyBranchRequest request ) {
+ // Figure out where the 'from' is projected ...
+ ProjectedNode projectedFromNode = project(request.from(),
request.fromWorkspace(), request, false);
+ if (projectedFromNode == null) return;
+ ProjectedNode projectedIntoNode = project(request.into(),
request.intoWorkspace(), request, true);
+ if (projectedIntoNode == null) return;
+
+ // Limitation: only able to project the copy if the 'from' and
'into' are in the same source & projection ...
+ while (projectedFromNode != null) {
+ if (projectedFromNode.isProxy()) {
+ ProxyNode fromProxy = projectedFromNode.asProxy();
+ // Look for a projectedIntoNode that has the same source/projection ...
+ while (projectedIntoNode != null) {
+ if (projectedIntoNode.isProxy()) {
+ // Both are proxies, so compare the projection ...
+ ProxyNode intoProxy = projectedIntoNode.asProxy();
+ if
(fromProxy.projection().getSourceName().equals(intoProxy.projection().getSourceName()))
break;
+ }
+ projectedIntoNode = projectedIntoNode.next();
+ }
+ if (projectedIntoNode != null) break;
+ }
+ projectedFromNode = projectedFromNode.next();
+ }
+ if (projectedFromNode == null || projectedIntoNode == null) {
+ // The copy is not done within a single source ...
+ String msg =
GraphI18n.copyLimitedToBeWithinSingleSource.text(readable(request.from()),
+
request.fromWorkspace(),
+
readable(request.into()),
+
request.intoWorkspace(),
+
getSourceName());
+ request.setError(new UnsupportedRequestException(msg));
+ return;
+ }
+
+ ProxyNode fromProxy = projectedFromNode.asProxy();
+ ProxyNode intoProxy = projectedIntoNode.asProxy();
+ assert
fromProxy.projection().getSourceName().equals(intoProxy.projection().getSourceName());
+ boolean sameLocation = fromProxy.isSameLocationAsOriginal() &&
intoProxy.isSameLocationAsOriginal();
+
+ // Create the pushed-down request ...
+ CopyBranchRequest pushDown = new CopyBranchRequest(fromProxy.location(),
fromProxy.workspaceName(), intoProxy.location(),
+ intoProxy.workspaceName(),
request.desiredName(),
+ request.conflictBehavior());
+ // Create the federated request ...
+ FederatedRequest federatedRequest = new FederatedRequest(request);
+ federatedRequest.add(pushDown, sameLocation, false, fromProxy.projection(),
intoProxy.projection());
+
+ // Submit the requests for processing and then STOP ...
+ submit(federatedRequest);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see
org.jboss.dna.graph.request.processor.RequestProcessor#process(org.jboss.dna.graph.request.MoveBranchRequest)
+ */
+ @Override
+ public void process( MoveBranchRequest request ) {
+ // Figure out where the 'from' is projected ...
+ ProjectedNode projectedFromNode = project(request.from(), request.inWorkspace(),
request, true);
+ if (projectedFromNode == null) return;
+ ProjectedNode projectedIntoNode = project(request.into(), request.inWorkspace(),
request, true);
+ if (projectedIntoNode == null) return;
+
+ // Limitation: only able to project the move if the 'from' and
'into' are in the same source & projection ...
+ while (projectedFromNode != null) {
+ if (projectedFromNode.isProxy()) {
+ ProxyNode fromProxy = projectedFromNode.asProxy();
+ // Look for a projectedIntoNode that has the same source/projection ...
+ while (projectedIntoNode != null) {
+ if (projectedIntoNode.isProxy()) {
+ // Both are proxies, so compare the projection ...
+ ProxyNode intoProxy = projectedIntoNode.asProxy();
+ if
(fromProxy.projection().getSourceName().equals(intoProxy.projection().getSourceName()))
break;
+ }
+ projectedIntoNode = projectedIntoNode.next();
+ }
+ if (projectedIntoNode != null) break;
+ }
+ projectedFromNode = projectedFromNode.next();
+ }
+ if (projectedFromNode == null || projectedIntoNode == null) {
+ // The move is not done within a single source ...
+ String msg =
GraphI18n.moveLimitedToBeWithinSingleSource.text(readable(request.from()),
+
request.inWorkspace(),
+
readable(request.into()),
+
request.inWorkspace(),
+
getSourceName());
+ request.setError(new UnsupportedRequestException(msg));
+ return;
+ }
+
+ ProxyNode fromProxy = projectedFromNode.asProxy();
+ ProxyNode intoProxy = projectedIntoNode.asProxy();
+ assert
fromProxy.projection().getSourceName().equals(intoProxy.projection().getSourceName());
+ boolean sameLocation = fromProxy.isSameLocationAsOriginal() &&
intoProxy.isSameLocationAsOriginal();
+
+ // Create the pushed-down request ...
+ MoveBranchRequest pushDown = new MoveBranchRequest(fromProxy.location(),
intoProxy.location(), intoProxy.workspaceName(),
+ request.desiredName(),
request.conflictBehavior());
+ // Create the federated request ...
+ FederatedRequest federatedRequest = new FederatedRequest(request);
+ federatedRequest.add(pushDown, sameLocation, false, fromProxy.projection(),
intoProxy.projection());
+
+ // Submit the requests for processing and then STOP ...
+ submit(federatedRequest);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see
org.jboss.dna.graph.request.processor.RequestProcessor#process(org.jboss.dna.graph.request.VerifyWorkspaceRequest)
+ */
+ @Override
+ public void process( VerifyWorkspaceRequest request ) {
+ FederatedWorkspace workspace = getWorkspace(request, request.workspaceName());
+ if (workspace != null) {
+ request.setActualWorkspaceName(workspace.getName());
+
+ // Get the root location ...
+ Location root =
Location.create(getExecutionContext().getValueFactories().getPathFactory().createRootPath());
+ ProjectedNode projectedNode = project(root, workspace.getName(), request,
false);
+ if (projectedNode == null) return;
+
+ // Create the federated request ...
+ FederatedRequest federatedRequest = new FederatedRequest(request);
+ while (projectedNode != null) {
+ if (projectedNode.isPlaceholder()) {
+ PlaceholderNode placeholder = projectedNode.asPlaceholder();
+ // Create a request and set the results ...
+ VerifyNodeExistsRequest placeholderRequest = new
VerifyNodeExistsRequest(root, workspace.getName());
+ placeholderRequest.setActualLocationOfNode(placeholder.location());
+ federatedRequest.add(placeholderRequest, true, true, null);
+ } else if (projectedNode.isProxy()) {
+ ProxyNode proxy = projectedNode.asProxy();
+ // Create and submit a request for the projection ...
+ VerifyNodeExistsRequest pushDownRequest = new
VerifyNodeExistsRequest(proxy.location(), proxy.workspaceName());
+ federatedRequest.add(pushDownRequest,
proxy.isSameLocationAsOriginal(), false, proxy.projection());
+ }
+ projectedNode = projectedNode.next();
+ }
+ // Submit for processing ...
+ submit(federatedRequest);
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see
org.jboss.dna.graph.request.processor.RequestProcessor#process(org.jboss.dna.graph.request.GetWorkspacesRequest)
+ */
+ @Override
+ public void process( GetWorkspacesRequest request ) {
+ request.setAvailableWorkspaceNames(repository.getWorkspaceNames());
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see
org.jboss.dna.graph.request.processor.RequestProcessor#process(org.jboss.dna.graph.request.CreateWorkspaceRequest)
+ */
+ @Override
+ public void process( CreateWorkspaceRequest request ) {
+ String msg =
GraphI18n.federatedSourceDoesNotSupportCreatingWorkspaces.text(getSourceName());
+ request.setError(new InvalidRequestException(msg));
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see
org.jboss.dna.graph.request.processor.RequestProcessor#process(org.jboss.dna.graph.request.CloneWorkspaceRequest)
+ */
+ @Override
+ public void process( CloneWorkspaceRequest request ) {
+ String msg =
GraphI18n.federatedSourceDoesNotSupportCloningWorkspaces.text(getSourceName());
+ request.setError(new InvalidRequestException(msg));
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see
org.jboss.dna.graph.request.processor.RequestProcessor#process(org.jboss.dna.graph.request.DestroyWorkspaceRequest)
+ */
+ @Override
+ public void process( DestroyWorkspaceRequest request ) {
+ String msg =
GraphI18n.federatedSourceDoesNotSupportDestroyingWorkspaces.text(getSourceName());
+ request.setError(new InvalidRequestException(msg));
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.request.processor.RequestProcessor#close()
+ */
+ @Override
+ public void close() {
+ super.close();
+ for (Channel channel : channelBySourceName.values()) {
+ channel.done();
+ }
+ }
+
+ protected void cancel( boolean mayInterruptIfRunning ) {
+ for (Channel channel : channelBySourceName.values()) {
+ channel.cancel(mayInterruptIfRunning);
+ }
+ }
+
+}
Property changes on:
trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/ForkRequestProcessor.java
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Added:
trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/GeneralProjector.java
===================================================================
---
trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/GeneralProjector.java
(rev 0)
+++
trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/GeneralProjector.java 2009-05-18
15:36:58 UTC (rev 911)
@@ -0,0 +1,121 @@
+/*
+ * JBoss DNA (
http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership. Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of
+ * individual contributors.
+ *
+ * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
+ * is licensed to you under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * JBoss DNA is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site:
http://www.fsf.org.
+ */
+package org.jboss.dna.graph.connector.federation;
+
+import java.util.List;
+import java.util.Set;
+import net.jcip.annotations.Immutable;
+import org.jboss.dna.graph.ExecutionContext;
+import org.jboss.dna.graph.Location;
+import org.jboss.dna.graph.property.Path;
+import org.jboss.dna.graph.property.PathFactory;
+
+/**
+ * A Projector for federated repository configurations that are an offset, direct
one-for-one mirror against a single source
+ * repository that is projected below the federated root. In other words, the federated
repository has a single projection with a
+ * single "/something/below/root => /" rule.
+ */
+@Immutable
+final class GeneralProjector extends ProjectorWithPlaceholders {
+
+ /**
+ * Attempt to create an instance of the {@link GeneralProjector} with the supplied
projections using the supplied context.
+ *
+ * @param context the context; may not be null
+ * @param projections the projections in the federated repository; may not be null
+ * @return the offset mirror projector, or null if the projections didn't match
the criteria for such a projector
+ */
+ static GeneralProjector with( ExecutionContext context,
+ List<Projection> projections ) {
+ assert projections != null;
+ assert context != null;
+ return new GeneralProjector(context, projections);
+ }
+
+ private final List<Projection> projections;
+
+ private GeneralProjector( ExecutionContext context,
+ List<Projection> projections ) {
+ super(context, projections);
+ this.projections = projections;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.connector.federation.Projector#project(ExecutionContext,
Location, boolean)
+ */
+ public ProjectedNode project( ExecutionContext context,
+ Location location,
+ boolean requiresUpdate ) {
+ // Find the location of the desired node ...
+ Path path = location.getPath();
+ if (path == null) {
+ // There are only identification properties, so return a ProxyNode for each
source/workspace ...
+ ProjectedNode result = null;
+ for (Projection projection : projections) {
+ if (requiresUpdate && projection.isReadOnly()) continue;
+ ProxyNode proxy = new ProxyNode(projection, location, location, false);
+ if (result == null) {
+ result = proxy;
+ } else {
+ result.add(proxy);
+ }
+ }
+ return result;
+ }
+ // We have a path, so see if the path is one of the existing placeholder nodes
...
+ ProjectedNode result = isPlaceholder(location);
+ if (result != null) return result;
+
+ // Otherwise this node lives below one (or more) of the projections, and we
figure this out each time ...
+ final PathFactory pathFactory = context.getValueFactories().getPathFactory();
+ for (Projection projection : projections) {
+ if (requiresUpdate && projection.isReadOnly()) continue;
+ // Project the federated repository path into the paths as they would exist
in the source ...
+ Set<Path> pathsInSource = projection.getPathsInSource(path,
pathFactory);
+ for (Path pathInSource : pathsInSource) {
+ // Create a ProxyNode for this projection ...
+ Location locationInSource = Location.create(pathInSource);
+ boolean samePath = pathInSource.equals(path);
+ ProxyNode proxy = new ProxyNode(projection, locationInSource, location,
samePath);
+ if (result == null) {
+ result = proxy;
+ } else {
+ result.add(proxy);
+ }
+ }
+ }
+
+ // If there is only one projection ...
+ if (result != null && !result.hasNext() && result.isProxy()) {
+ // Then we want to include the supplied location's ID properties down to
the source ...
+ ProxyNode proxy = result.asProxy();
+ result = new ProxyNode(proxy.projection(),
location.with(result.location().getPath()), location,
+ proxy.isSameLocationAsOriginal());
+ }
+ return result;
+ }
+
+}
Property changes on:
trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/GeneralProjector.java
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Added:
trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/JoinMirrorRequestProcessor.java
===================================================================
---
trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/JoinMirrorRequestProcessor.java
(rev 0)
+++
trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/JoinMirrorRequestProcessor.java 2009-05-18
15:36:58 UTC (rev 911)
@@ -0,0 +1,391 @@
+/*
+ * JBoss DNA (
http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership. Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of
+ * individual contributors.
+ *
+ * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
+ * is licensed to you under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * JBoss DNA is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site:
http://www.fsf.org.
+ */
+package org.jboss.dna.graph.connector.federation;
+
+import java.util.List;
+import java.util.Map;
+import org.jboss.dna.graph.ExecutionContext;
+import org.jboss.dna.graph.Location;
+import org.jboss.dna.graph.cache.CachePolicy;
+import org.jboss.dna.graph.property.DateTime;
+import org.jboss.dna.graph.property.Name;
+import org.jboss.dna.graph.property.Property;
+import org.jboss.dna.graph.request.CacheableRequest;
+import org.jboss.dna.graph.request.CloneWorkspaceRequest;
+import org.jboss.dna.graph.request.CopyBranchRequest;
+import org.jboss.dna.graph.request.CreateNodeRequest;
+import org.jboss.dna.graph.request.CreateWorkspaceRequest;
+import org.jboss.dna.graph.request.DeleteBranchRequest;
+import org.jboss.dna.graph.request.DestroyWorkspaceRequest;
+import org.jboss.dna.graph.request.GetWorkspacesRequest;
+import org.jboss.dna.graph.request.MoveBranchRequest;
+import org.jboss.dna.graph.request.ReadAllChildrenRequest;
+import org.jboss.dna.graph.request.ReadAllPropertiesRequest;
+import org.jboss.dna.graph.request.ReadBlockOfChildrenRequest;
+import org.jboss.dna.graph.request.ReadBranchRequest;
+import org.jboss.dna.graph.request.ReadNextBlockOfChildrenRequest;
+import org.jboss.dna.graph.request.ReadNodeRequest;
+import org.jboss.dna.graph.request.ReadPropertyRequest;
+import org.jboss.dna.graph.request.RemovePropertyRequest;
+import org.jboss.dna.graph.request.RenameNodeRequest;
+import org.jboss.dna.graph.request.Request;
+import org.jboss.dna.graph.request.SetPropertyRequest;
+import org.jboss.dna.graph.request.UpdatePropertiesRequest;
+import org.jboss.dna.graph.request.VerifyNodeExistsRequest;
+import org.jboss.dna.graph.request.VerifyWorkspaceRequest;
+import org.jboss.dna.graph.request.processor.RequestProcessor;
+
+/**
+ * A RequestProcessor used by the {@link JoinRequestProcessor} in cases where there is a
single source-request with the same
+ * location(s). This processor merely copies the results from each source request for
the
+ * {@link #setFederatedRequest(FederatedRequest) current federated request} into the
request passed as a parameter.
+ */
+class JoinMirrorRequestProcessor extends RequestProcessor {
+
+ private FederatedRequest federatedRequest;
+
+ JoinMirrorRequestProcessor( String sourceName,
+ ExecutionContext context,
+ DateTime now,
+ CachePolicy defaultCachePolicy ) {
+ super(sourceName, context, now, defaultCachePolicy);
+
+ }
+
+ /**
+ * @param federatedRequest Sets federatedRequest to the specified value.
+ */
+ void setFederatedRequest( FederatedRequest federatedRequest ) {
+ this.federatedRequest = federatedRequest;
+ }
+
+ protected boolean checkErrorOrCancel( Request request,
+ Request sourceRequest ) {
+ if (sourceRequest.hasError()) {
+ request.setError(sourceRequest.getError());
+ return true;
+ }
+ if (sourceRequest.isCancelled()) {
+ request.cancel();
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see
org.jboss.dna.graph.request.processor.RequestProcessor#process(org.jboss.dna.graph.request.ReadNodeRequest)
+ */
+ @Override
+ public void process( ReadNodeRequest request ) {
+ ReadNodeRequest source =
(ReadNodeRequest)federatedRequest.getFirstProjectedRequest().getRequest();
+ if (checkErrorOrCancel(request, source)) return;
+ request.setActualLocationOfNode(source.getActualLocationOfNode());
+ for (Location childInSource : source.getChildren()) {
+ request.addChild(childInSource);
+ }
+ for (Property propertyInSource : source.getProperties()) {
+ request.addProperties(propertyInSource);
+ }
+ request.setCachePolicy(getDefaultCachePolicy());
+ setCacheableInfo(request, source.getCachePolicy());
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see
org.jboss.dna.graph.request.processor.RequestProcessor#process(org.jboss.dna.graph.request.ReadAllChildrenRequest)
+ */
+ @Override
+ public void process( ReadAllChildrenRequest request ) {
+ ReadAllChildrenRequest source =
(ReadAllChildrenRequest)federatedRequest.getFirstProjectedRequest().getRequest();
+ if (checkErrorOrCancel(request, source)) return;
+ request.setActualLocationOfNode(source.getActualLocationOfNode());
+ for (Location childInSource : source.getChildren()) {
+ request.addChild(childInSource);
+ }
+ request.setCachePolicy(getDefaultCachePolicy());
+ setCacheableInfo(request, source.getCachePolicy());
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see
org.jboss.dna.graph.request.processor.RequestProcessor#process(org.jboss.dna.graph.request.ReadAllPropertiesRequest)
+ */
+ @Override
+ public void process( ReadAllPropertiesRequest request ) {
+ ReadAllPropertiesRequest source =
(ReadAllPropertiesRequest)federatedRequest.getFirstProjectedRequest().getRequest();
+ if (checkErrorOrCancel(request, source)) return;
+ request.setActualLocationOfNode(source.getActualLocationOfNode());
+ for (Property propertyInSource : source.getProperties()) {
+ request.addProperties(propertyInSource);
+ }
+ request.setCachePolicy(getDefaultCachePolicy());
+ setCacheableInfo(request, source.getCachePolicy());
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see
org.jboss.dna.graph.request.processor.RequestProcessor#process(org.jboss.dna.graph.request.ReadBlockOfChildrenRequest)
+ */
+ @Override
+ public void process( ReadBlockOfChildrenRequest request ) {
+ ReadBlockOfChildrenRequest source =
(ReadBlockOfChildrenRequest)federatedRequest.getFirstProjectedRequest().getRequest();
+ if (checkErrorOrCancel(request, source)) return;
+ request.setActualLocationOfNode(source.getActualLocationOfNode());
+ for (Location childInSource : source.getChildren()) {
+ request.addChild(childInSource);
+ }
+ request.setCachePolicy(getDefaultCachePolicy());
+ setCacheableInfo(request, source.getCachePolicy());
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see
org.jboss.dna.graph.request.processor.RequestProcessor#process(org.jboss.dna.graph.request.ReadNextBlockOfChildrenRequest)
+ */
+ @Override
+ public void process( ReadNextBlockOfChildrenRequest request ) {
+ ReadNextBlockOfChildrenRequest source =
(ReadNextBlockOfChildrenRequest)federatedRequest.getFirstProjectedRequest().getRequest();
+ if (checkErrorOrCancel(request, source)) return;
+
request.setActualLocationOfStartingAfterNode(source.getActualLocationOfStartingAfterNode());
+ for (Location childInSource : source.getChildren()) {
+ request.addChild(childInSource);
+ }
+ request.setCachePolicy(getDefaultCachePolicy());
+ setCacheableInfo(request, source.getCachePolicy());
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see
org.jboss.dna.graph.request.processor.RequestProcessor#process(org.jboss.dna.graph.request.ReadPropertyRequest)
+ */
+ @Override
+ public void process( ReadPropertyRequest request ) {
+ ReadPropertyRequest source =
(ReadPropertyRequest)federatedRequest.getFirstProjectedRequest().getRequest();
+ if (checkErrorOrCancel(request, source)) return;
+ request.setActualLocationOfNode(source.getActualLocationOfNode());
+ request.setProperty(source.getProperty());
+ request.setCachePolicy(getDefaultCachePolicy());
+ setCacheableInfo(request, source.getCachePolicy());
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see
org.jboss.dna.graph.request.processor.RequestProcessor#process(org.jboss.dna.graph.request.SetPropertyRequest)
+ */
+ @Override
+ public void process( SetPropertyRequest request ) {
+ SetPropertyRequest source =
(SetPropertyRequest)federatedRequest.getFirstProjectedRequest().getRequest();
+ if (checkErrorOrCancel(request, source)) return;
+ request.setActualLocationOfNode(source.getActualLocationOfNode());
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see
org.jboss.dna.graph.request.processor.RequestProcessor#process(org.jboss.dna.graph.request.VerifyNodeExistsRequest)
+ */
+ @Override
+ public void process( VerifyNodeExistsRequest request ) {
+ VerifyNodeExistsRequest source =
(VerifyNodeExistsRequest)federatedRequest.getFirstProjectedRequest().getRequest();
+ if (checkErrorOrCancel(request, source)) return;
+ request.setActualLocationOfNode(source.getActualLocationOfNode());
+ request.setCachePolicy(getDefaultCachePolicy());
+ setCacheableInfo(request, source.getCachePolicy());
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see
org.jboss.dna.graph.request.processor.RequestProcessor#process(org.jboss.dna.graph.request.RemovePropertyRequest)
+ */
+ @Override
+ public void process( RemovePropertyRequest request ) {
+ RemovePropertyRequest source =
(RemovePropertyRequest)federatedRequest.getFirstProjectedRequest().getRequest();
+ if (checkErrorOrCancel(request, source)) return;
+ request.setActualLocationOfNode(source.getActualLocationOfNode());
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see
org.jboss.dna.graph.request.processor.RequestProcessor#process(org.jboss.dna.graph.request.RenameNodeRequest)
+ */
+ @Override
+ public void process( RenameNodeRequest request ) {
+ RenameNodeRequest source =
(RenameNodeRequest)federatedRequest.getFirstProjectedRequest().getRequest();
+ if (checkErrorOrCancel(request, source)) return;
+ request.setActualLocations(source.getActualLocationBefore(),
source.getActualLocationAfter());
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see
org.jboss.dna.graph.request.processor.RequestProcessor#process(org.jboss.dna.graph.request.ReadBranchRequest)
+ */
+ @Override
+ public void process( ReadBranchRequest request ) {
+ CacheableRequest source =
(CacheableRequest)federatedRequest.getFirstProjectedRequest().getRequest();
+ if (checkErrorOrCancel(request, source)) return;
+ if (source instanceof ReadBranchRequest) {
+ ReadBranchRequest readSource = (ReadBranchRequest)source;
+ request.setActualLocationOfNode(readSource.getActualLocationOfNode());
+ for (Location node : readSource) {
+ List<Location> children = readSource.getChildren(node);
+ if (children != null) request.setChildren(node, children);
+ Map<Name, Property> props = readSource.getPropertiesFor(node);
+ if (props != null) request.setProperties(node, props.values());
+ }
+ } else if (source instanceof ReadNodeRequest) {
+ ReadNodeRequest readSource = (ReadNodeRequest)source;
+ request.setActualLocationOfNode(readSource.getActualLocationOfNode());
+ Location parent = readSource.getActualLocationOfNode();
+ request.setChildren(parent, readSource.getChildren());
+ request.setProperties(parent, readSource.getPropertiesByName().values());
+ }
+ request.setCachePolicy(getDefaultCachePolicy());
+ setCacheableInfo(request, source.getCachePolicy());
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see
org.jboss.dna.graph.request.processor.RequestProcessor#process(org.jboss.dna.graph.request.CreateNodeRequest)
+ */
+ @Override
+ public void process( CreateNodeRequest request ) {
+ CreateNodeRequest source =
(CreateNodeRequest)federatedRequest.getFirstProjectedRequest().getRequest();
+ if (checkErrorOrCancel(request, source)) return;
+ request.setActualLocationOfNode(source.getActualLocationOfNode());
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see
org.jboss.dna.graph.request.processor.RequestProcessor#process(org.jboss.dna.graph.request.UpdatePropertiesRequest)
+ */
+ @Override
+ public void process( UpdatePropertiesRequest request ) {
+ UpdatePropertiesRequest source =
(UpdatePropertiesRequest)federatedRequest.getFirstProjectedRequest().getRequest();
+ if (checkErrorOrCancel(request, source)) return;
+ request.setActualLocationOfNode(source.getActualLocationOfNode());
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see
org.jboss.dna.graph.request.processor.RequestProcessor#process(org.jboss.dna.graph.request.CopyBranchRequest)
+ */
+ @Override
+ public void process( CopyBranchRequest request ) {
+ CopyBranchRequest source =
(CopyBranchRequest)federatedRequest.getFirstProjectedRequest().getRequest();
+ if (checkErrorOrCancel(request, source)) return;
+ request.setActualLocations(source.getActualLocationBefore(),
source.getActualLocationAfter());
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see
org.jboss.dna.graph.request.processor.RequestProcessor#process(org.jboss.dna.graph.request.DeleteBranchRequest)
+ */
+ @Override
+ public void process( DeleteBranchRequest request ) {
+ DeleteBranchRequest source =
(DeleteBranchRequest)federatedRequest.getFirstProjectedRequest().getRequest();
+ if (checkErrorOrCancel(request, source)) return;
+ request.setActualLocationOfNode(source.getActualLocationOfNode());
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see
org.jboss.dna.graph.request.processor.RequestProcessor#process(org.jboss.dna.graph.request.MoveBranchRequest)
+ */
+ @Override
+ public void process( MoveBranchRequest request ) {
+ MoveBranchRequest source =
(MoveBranchRequest)federatedRequest.getFirstProjectedRequest().getRequest();
+ if (checkErrorOrCancel(request, source)) return;
+ request.setActualLocations(source.getActualLocationBefore(),
source.getActualLocationAfter());
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see
org.jboss.dna.graph.request.processor.RequestProcessor#process(org.jboss.dna.graph.request.VerifyWorkspaceRequest)
+ */
+ @Override
+ public void process( VerifyWorkspaceRequest request ) {
+ VerifyNodeExistsRequest source =
(VerifyNodeExistsRequest)federatedRequest.getFirstProjectedRequest().getRequest();
+ if (checkErrorOrCancel(request, source)) return;
+ assert source.getActualLocationOfNode().getPath().isRoot();
+ request.setActualRootLocation(source.getActualLocationOfNode());
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see
org.jboss.dna.graph.request.processor.RequestProcessor#process(org.jboss.dna.graph.request.GetWorkspacesRequest)
+ */
+ @Override
+ public void process( GetWorkspacesRequest request ) {
+ throw new UnsupportedOperationException(); // should never be called
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see
org.jboss.dna.graph.request.processor.RequestProcessor#process(org.jboss.dna.graph.request.CreateWorkspaceRequest)
+ */
+ @Override
+ public void process( CreateWorkspaceRequest request ) {
+ throw new UnsupportedOperationException(); // should never be called
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see
org.jboss.dna.graph.request.processor.RequestProcessor#process(org.jboss.dna.graph.request.CloneWorkspaceRequest)
+ */
+ @Override
+ public void process( CloneWorkspaceRequest request ) {
+ throw new UnsupportedOperationException(); // should never be called
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see
org.jboss.dna.graph.request.processor.RequestProcessor#process(org.jboss.dna.graph.request.DestroyWorkspaceRequest)
+ */
+ @Override
+ public void process( DestroyWorkspaceRequest request ) {
+ throw new UnsupportedOperationException(); // should never be called
+ }
+
+}
Property changes on:
trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/JoinMirrorRequestProcessor.java
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Added:
trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/JoinRequestProcessor.java
===================================================================
---
trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/JoinRequestProcessor.java
(rev 0)
+++
trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/JoinRequestProcessor.java 2009-05-18
15:36:58 UTC (rev 911)
@@ -0,0 +1,981 @@
+/*
+ * JBoss DNA (
http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership. Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of
+ * individual contributors.
+ *
+ * Unless otherwise indicated, all code in JBoss DNA is licensed
+ * to you under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * JBoss DNA is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site:
http://www.fsf.org.
+ */
+package org.jboss.dna.graph.connector.federation;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.NoSuchElementException;
+import java.util.concurrent.BlockingQueue;
+import net.jcip.annotations.NotThreadSafe;
+import org.jboss.dna.graph.ExecutionContext;
+import org.jboss.dna.graph.GraphI18n;
+import org.jboss.dna.graph.Location;
+import org.jboss.dna.graph.connector.federation.FederatedRequest.ProjectedRequest;
+import org.jboss.dna.graph.property.DateTime;
+import org.jboss.dna.graph.property.Name;
+import org.jboss.dna.graph.property.Path;
+import org.jboss.dna.graph.property.PathFactory;
+import org.jboss.dna.graph.property.PathNotFoundException;
+import org.jboss.dna.graph.property.Property;
+import org.jboss.dna.graph.property.PropertyFactory;
+import org.jboss.dna.graph.property.ValueComparators;
+import org.jboss.dna.graph.request.CacheableRequest;
+import org.jboss.dna.graph.request.CloneWorkspaceRequest;
+import org.jboss.dna.graph.request.CopyBranchRequest;
+import org.jboss.dna.graph.request.CreateNodeRequest;
+import org.jboss.dna.graph.request.CreateWorkspaceRequest;
+import org.jboss.dna.graph.request.DeleteBranchRequest;
+import org.jboss.dna.graph.request.DeleteChildrenRequest;
+import org.jboss.dna.graph.request.DestroyWorkspaceRequest;
+import org.jboss.dna.graph.request.GetWorkspacesRequest;
+import org.jboss.dna.graph.request.InvalidRequestException;
+import org.jboss.dna.graph.request.MoveBranchRequest;
+import org.jboss.dna.graph.request.ReadAllChildrenRequest;
+import org.jboss.dna.graph.request.ReadAllPropertiesRequest;
+import org.jboss.dna.graph.request.ReadBranchRequest;
+import org.jboss.dna.graph.request.ReadNodeRequest;
+import org.jboss.dna.graph.request.ReadPropertyRequest;
+import org.jboss.dna.graph.request.Request;
+import org.jboss.dna.graph.request.UpdatePropertiesRequest;
+import org.jboss.dna.graph.request.VerifyNodeExistsRequest;
+import org.jboss.dna.graph.request.VerifyWorkspaceRequest;
+import org.jboss.dna.graph.request.processor.RequestProcessor;
+
+/**
+ *
+ */
+@NotThreadSafe
+class JoinRequestProcessor extends RequestProcessor {
+
+ private final PathFactory pathFactory;
+ private final PropertyFactory propertyFactory;
+ private final JoinMirrorRequestProcessor mirrorProcessor;
+ protected FederatedRequest federatedRequest;
+
+ /**
+ * Create a new join processor
+ *
+ * @param repository the federated repository configuration; never null
+ * @param context the execution context in which this processor is executing; may not
be null
+ * @param now the timestamp representing the current time in UTC; may not be null
+ */
+ public JoinRequestProcessor( FederatedRepository repository,
+ ExecutionContext context,
+ DateTime now ) {
+ super(repository.getSourceName(), context, now,
repository.getDefaultCachePolicy());
+ // this.repository = repository;
+ this.propertyFactory = context.getPropertyFactory();
+ this.pathFactory = context.getValueFactories().getPathFactory();
+ this.mirrorProcessor = new JoinMirrorRequestProcessor(repository.getSourceName(),
context, now,
+
repository.getDefaultCachePolicy());
+ }
+
+ /**
+ * Process all of the {@link FederatedRequest} objects that are in the supplied
collection.
+ *
+ * @param completedFederatedRequests the collection of {@link FederatedRequest} whose
projected requests have already been
+ * processed; may not be null
+ * @see FederatedRepositoryConnection#execute(ExecutionContext,
org.jboss.dna.graph.request.Request)
+ */
+ public void process( final Iterable<FederatedRequest>
completedFederatedRequests ) {
+ for (FederatedRequest federatedRequest : completedFederatedRequests) {
+ // No need to await for the forked request, since it will be done
+ this.federatedRequest = federatedRequest;
+ process(federatedRequest.original());
+ }
+ }
+
+ /**
+ * Process the {@link FederatedRequest} objects that are in the supplied queue. The
queue contains {@link FederatedRequest}
+ * that may have projected requests that have not yet been processed by the
respective source, so this method
+ * {@link FederatedRequest#await() waits} until all source requests have been
processed. This method returns only when it
+ * obtains from the queue a {@link NoMoreFederatedRequests} instance.
+ *
+ * @param federatedRequestQueue the queue containing the federated requests; may not
be null
+ * @see FederatedRepositoryConnection#execute(ExecutionContext,
org.jboss.dna.graph.request.Request)
+ */
+ public void process( final BlockingQueue<FederatedRequest>
federatedRequestQueue ) {
+ FederatedRequest forked = null;
+ try {
+ for (;;) {
+ forked = federatedRequestQueue.take();
+ if (forked instanceof NoMoreFederatedRequests) return;
+ forked.await();
+
+ // Determine whether this is a single mirror request ...
+ Request original = forked.original();
+ ProjectedRequest projectedRequest = forked.getFirstProjectedRequest();
+ boolean sameLocation = projectedRequest != null &&
!projectedRequest.hasNext()
+ && projectedRequest.isSameLocation();
+
+ // Set the cachable information ...
+ if (original instanceof CacheableRequest) {
+ CacheableRequest cacheableOriginal = (CacheableRequest)original;
+ cacheableOriginal.setCachePolicy(getDefaultCachePolicy());
+ while (projectedRequest != null) {
+ Request requestToSource = projectedRequest.getRequest();
+ if (cacheableOriginal != null) {
+ setCacheableInfo(cacheableOriginal,
((CacheableRequest)requestToSource).getCachePolicy());
+ }
+ projectedRequest = projectedRequest.next();
+ }
+ }
+
+ // Now do the join on this request ...
+ if (sameLocation) {
+ Request sourceRequest =
forked.getFirstProjectedRequest().getRequest();
+ if (sourceRequest.hasError()) {
+ original.setError(sourceRequest.getError());
+ } else if (sourceRequest.isCancelled()) {
+ original.cancel();
+ }
+ mirrorProcessor.setFederatedRequest(forked);
+ mirrorProcessor.process(original);
+ } else {
+ this.federatedRequest = forked;
+ process(original);
+ }
+ }
+ } catch (InterruptedException e) {
+ // This happens when the federated connector has been told to shutdown now,
and it shuts down
+ // its executor (the worker pool) immediately by interrupting each in-use
thread.
+ // In this case, we should cancel the current request but should NOT iterate
over any remaining requests.
+ try {
+ if (forked != null) {
+ forked.original().cancel();
+ }
+ } finally {
+ // Clear the interrupted status of the thread ...
+ Thread.interrupted();
+ }
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see
org.jboss.dna.graph.request.processor.RequestProcessor#process(org.jboss.dna.graph.request.VerifyNodeExistsRequest)
+ */
+ @Override
+ public void process( VerifyNodeExistsRequest request ) {
+ ProjectedRequest projectedRequest = federatedRequest.getFirstProjectedRequest();
+ assert projectedRequest != null;
+ request.setCachePolicy(getDefaultCachePolicy());
+ Location actualLocation = request.at();
+ int numMerged = 0;
+ while (projectedRequest != null) {
+ VerifyNodeExistsRequest readFromSource =
(VerifyNodeExistsRequest)projectedRequest.getRequest();
+ if (readFromSource.hasError()) {
+ projectedRequest = projectedRequest.next();
+ continue;
+ }
+ if (readFromSource.isCancelled()) {
+ request.cancel();
+ return;
+ }
+
+ // Accumulate the identification properties ...
+ for (Property propertyInSource :
readFromSource.getActualLocationOfNode().getIdProperties()) {
+ Name name = propertyInSource.getName();
+ Property existing = actualLocation.getIdProperty(name);
+ if (existing != null) {
+ // Merge the property values ...
+ propertyInSource = merge(existing, propertyInSource, propertyFactory,
true);
+ }
+ actualLocation = actualLocation.with(propertyInSource);
+ }
+ setCacheableInfo(request, readFromSource.getCachePolicy());
+ projectedRequest = projectedRequest.next();
+ ++numMerged;
+ }
+ if (numMerged == 0) {
+ // No source requests had results ...
+ setPathNotFound(request, request.at(),
federatedRequest.getFirstProjectedRequest());
+ } else {
+ request.setActualLocationOfNode(actualLocation);
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see
org.jboss.dna.graph.request.processor.RequestProcessor#process(org.jboss.dna.graph.request.ReadNodeRequest)
+ */
+ @Override
+ public void process( ReadNodeRequest request ) {
+ Path federatedPath = request.at().getPath();
+ Map<Name, Property> properties = request.getPropertiesByName();
+ Map<Name, Integer> childSnsIndexes = new HashMap<Name, Integer>();
+ ProjectedRequest projectedRequest = federatedRequest.getFirstProjectedRequest();
+ assert projectedRequest != null;
+ request.setCachePolicy(getDefaultCachePolicy());
+ Location actualLocation = request.at();
+ int numMerged = 0;
+ while (projectedRequest != null) {
+ ReadNodeRequest readFromSource =
(ReadNodeRequest)projectedRequest.getRequest();
+ if (readFromSource.hasError()) {
+ projectedRequest = projectedRequest.next();
+ continue;
+ }
+ if (readFromSource.isCancelled()) {
+ request.cancel();
+ return;
+ }
+
+ // Accumulate the identification properties ...
+ for (Property propertyInSource :
readFromSource.getActualLocationOfNode().getIdProperties()) {
+ Name name = propertyInSource.getName();
+ Property existing = actualLocation.getIdProperty(name);
+ if (existing != null) {
+ // Merge the property values ...
+ propertyInSource = merge(existing, propertyInSource, propertyFactory,
true);
+ }
+ actualLocation = actualLocation.with(propertyInSource);
+ }
+
+ // Add all the children from the source ...
+ for (Location childInSource : readFromSource.getChildren()) {
+ // Project back into the federated repository ...
+ Path childPath = childInSource.getPath();
+ // Correct the same-name-sibling index for the child ...
+ Name childName = childPath.getLastSegment().getName();
+ Integer snsIndex = childSnsIndexes.get(childName);
+ if (snsIndex == null) {
+ snsIndex = new Integer(1);
+ childSnsIndexes.put(childName, snsIndex);
+ } else {
+ snsIndex = new Integer(snsIndex.intValue() + 1);
+ childSnsIndexes.put(childName, snsIndex);
+ }
+ Path newPath = pathFactory.create(federatedPath, childName,
snsIndex.intValue());
+ request.addChild(childInSource.with(newPath));
+ }
+
+ // Add all the properties ...
+ for (Property propertyInSource : readFromSource.getProperties()) {
+ Name name = propertyInSource.getName();
+ Property existing = properties.get(name);
+ if (existing != null) {
+ // Merge the property values ...
+ propertyInSource = merge(existing, propertyInSource, propertyFactory,
true);
+ }
+ properties.put(name, propertyInSource);
+ }
+ setCacheableInfo(request, readFromSource.getCachePolicy());
+ projectedRequest = projectedRequest.next();
+ ++numMerged;
+ }
+ if (numMerged == 0) {
+ // No source requests had results ...
+ setPathNotFound(request, request.at(),
federatedRequest.getFirstProjectedRequest());
+ } else {
+ request.setActualLocationOfNode(actualLocation);
+ }
+ }
+
+ /**
+ * Sets the request {@link Request#setError(Throwable) error} to a {@link
PathNotFoundException} that has the lowest existing
+ * ancestor computed from the {@link PathNotFoundException}s in the projected
requests.
+ *
+ * @param original
+ * @param originalLocation
+ * @param projected
+ */
+ protected void setPathNotFound( Request original,
+ Location originalLocation,
+ ProjectedRequest projected ) {
+ Path lowestExistingInFederated = pathFactory.createRootPath();
+ while (projected != null) {
+ Request projectedRequest = projected.getRequest();
+ Throwable error = projectedRequest.getError();
+ if (error instanceof PathNotFoundException) {
+ PathNotFoundException notFound = (PathNotFoundException)error;
+ Path lowestExisting = notFound.getLowestAncestorThatDoesExist();
+ // Project back to the repository level ...
+ for (Path federatedPath :
projected.getProjection().getPathsInRepository(lowestExisting, pathFactory)) {
+ if (federatedPath.isAtOrBelow(lowestExistingInFederated)) {
+ lowestExistingInFederated = federatedPath;
+ }
+ }
+ }
+ projected = projected.next();
+ }
+ original.setError(new PathNotFoundException(originalLocation,
lowestExistingInFederated));
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see
org.jboss.dna.graph.request.processor.RequestProcessor#process(org.jboss.dna.graph.request.ReadAllChildrenRequest)
+ */
+ @Override
+ public void process( ReadAllChildrenRequest request ) {
+ Path federatedPath = request.of().getPath();
+ Map<Name, Integer> childSnsIndexes = new HashMap<Name, Integer>();
+ ProjectedRequest projectedRequest = federatedRequest.getFirstProjectedRequest();
+ assert projectedRequest != null;
+ request.setCachePolicy(getDefaultCachePolicy());
+ Location actualLocation = request.of();
+ int numMerged = 0;
+ while (projectedRequest != null) {
+ ReadAllChildrenRequest readFromSource =
(ReadAllChildrenRequest)projectedRequest.getRequest();
+ if (readFromSource.hasError()) {
+ projectedRequest = projectedRequest.next();
+ continue;
+ }
+ if (readFromSource.isCancelled()) {
+ request.cancel();
+ return;
+ }
+
+ // Accumulate the identification properties ...
+ for (Property propertyInSource :
readFromSource.getActualLocationOfNode().getIdProperties()) {
+ Name name = propertyInSource.getName();
+ Property existing = actualLocation.getIdProperty(name);
+ if (existing != null) {
+ // Merge the property values ...
+ propertyInSource = merge(existing, propertyInSource, propertyFactory,
true);
+ }
+ actualLocation = actualLocation.with(propertyInSource);
+ }
+
+ // Add all the children from the source ...
+ for (Location childInSource : readFromSource.getChildren()) {
+ // Project back into the federated repository ...
+ Path childPath = childInSource.getPath();
+ // Correct the same-name-sibling index for the child ...
+ Name childName = childPath.getLastSegment().getName();
+ Integer snsIndex = childSnsIndexes.get(childName);
+ if (snsIndex == null) {
+ snsIndex = new Integer(1);
+ childSnsIndexes.put(childName, snsIndex);
+ } else {
+ snsIndex = new Integer(snsIndex.intValue() + 1);
+ childSnsIndexes.put(childName, snsIndex);
+ }
+ Path newPath = pathFactory.create(federatedPath, childName,
snsIndex.intValue());
+ request.addChild(childInSource.with(newPath));
+ }
+
+ setCacheableInfo(request, readFromSource.getCachePolicy());
+ projectedRequest = projectedRequest.next();
+ ++numMerged;
+ }
+ if (numMerged == 0) {
+ // No source requests had results ...
+ setPathNotFound(request, request.of(),
federatedRequest.getFirstProjectedRequest());
+ } else {
+ request.setActualLocationOfNode(actualLocation);
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see
org.jboss.dna.graph.request.processor.RequestProcessor#process(org.jboss.dna.graph.request.ReadAllPropertiesRequest)
+ */
+ @Override
+ public void process( ReadAllPropertiesRequest request ) {
+ Map<Name, Property> properties = request.getPropertiesByName();
+ ProjectedRequest projectedRequest = federatedRequest.getFirstProjectedRequest();
+ assert projectedRequest != null;
+ request.setCachePolicy(getDefaultCachePolicy());
+ Location actualLocation = request.at();
+ int numMerged = 0;
+ while (projectedRequest != null) {
+ ReadAllPropertiesRequest readFromSource =
(ReadAllPropertiesRequest)projectedRequest.getRequest();
+ if (readFromSource.hasError()) {
+ projectedRequest = projectedRequest.next();
+ continue;
+ }
+ if (readFromSource.isCancelled()) {
+ request.cancel();
+ return;
+ }
+
+ // Accumulate the identification properties ...
+ for (Property propertyInSource :
readFromSource.getActualLocationOfNode().getIdProperties()) {
+ Name name = propertyInSource.getName();
+ Property existing = actualLocation.getIdProperty(name);
+ if (existing != null) {
+ // Merge the property values ...
+ propertyInSource = merge(existing, propertyInSource, propertyFactory,
true);
+ }
+ actualLocation = actualLocation.with(propertyInSource);
+ }
+
+ // Add all the properties ...
+ for (Property propertyInSource : readFromSource.getProperties()) {
+ Name name = propertyInSource.getName();
+ Property existing = properties.get(name);
+ if (existing != null) {
+ // Merge the property values ...
+ propertyInSource = merge(existing, propertyInSource, propertyFactory,
true);
+ }
+ properties.put(name, propertyInSource);
+ }
+ setCacheableInfo(request, readFromSource.getCachePolicy());
+ projectedRequest = projectedRequest.next();
+ ++numMerged;
+ }
+ if (numMerged == 0) {
+ // No source requests had results ...
+ setPathNotFound(request, request.at(),
federatedRequest.getFirstProjectedRequest());
+ } else {
+ request.setActualLocationOfNode(actualLocation);
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see
org.jboss.dna.graph.request.processor.RequestProcessor#process(org.jboss.dna.graph.request.ReadPropertyRequest)
+ */
+ @Override
+ public void process( ReadPropertyRequest request ) {
+ ProjectedRequest projectedRequest = federatedRequest.getFirstProjectedRequest();
+ assert projectedRequest != null;
+ request.setCachePolicy(getDefaultCachePolicy());
+ Location actualLocation = request.on();
+ int numMerged = 0;
+ while (projectedRequest != null) {
+ ReadPropertyRequest readFromSource =
(ReadPropertyRequest)projectedRequest.getRequest();
+ if (readFromSource.hasError()) {
+ projectedRequest = projectedRequest.next();
+ continue;
+ }
+ if (readFromSource.isCancelled()) {
+ request.cancel();
+ return;
+ }
+
+ // Accumulate the identification properties ...
+ for (Property propertyInSource :
readFromSource.getActualLocationOfNode().getIdProperties()) {
+ Name name = propertyInSource.getName();
+ Property existing = actualLocation.getIdProperty(name);
+ if (existing != null) {
+ // Merge the property values ...
+ propertyInSource = merge(existing, propertyInSource, propertyFactory,
true);
+ }
+ actualLocation = actualLocation.with(propertyInSource);
+ }
+
+ // Add all the properties ...
+ Property read = readFromSource.getProperty();
+ if (read != null) {
+ Property existing = request.getProperty();
+ if (existing != null) {
+ // Merge the property values ...
+ request.setProperty(merge(existing, read, propertyFactory, true));
+ } else {
+ request.setProperty(read);
+ }
+ }
+ setCacheableInfo(request, readFromSource.getCachePolicy());
+ projectedRequest = projectedRequest.next();
+ ++numMerged;
+ }
+ if (numMerged == 0) {
+ // No source requests had results ...
+ setPathNotFound(request, request.on(),
federatedRequest.getFirstProjectedRequest());
+ } else {
+ request.setActualLocationOfNode(actualLocation);
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see
org.jboss.dna.graph.request.processor.RequestProcessor#process(org.jboss.dna.graph.request.ReadBranchRequest)
+ */
+ @Override
+ public void process( ReadBranchRequest request ) {
+ ProjectedRequest projectedRequest = federatedRequest.getFirstProjectedRequest();
+ assert projectedRequest != null;
+ request.setCachePolicy(getDefaultCachePolicy());
+ Location actualLocation = request.at();
+ int numMerged = 0;
+ while (projectedRequest != null) {
+ CacheableRequest fromSource =
(CacheableRequest)projectedRequest.getRequest();
+ if (fromSource.hasError()) {
+ projectedRequest = projectedRequest.next();
+ continue;
+ }
+ if (fromSource.isCancelled()) {
+ request.cancel();
+ return;
+ }
+
+ Projection projection = projectedRequest.getProjection();
+ if (fromSource instanceof ReadNodeRequest) {
+ ReadNodeRequest readFromSource = (ReadNodeRequest)fromSource;
+ Location parent = readFromSource.getActualLocationOfNode();
+ List<Location> children = readFromSource.getChildren();
+ Map<Name, Property> properties =
readFromSource.getPropertiesByName();
+ projectToFederated(actualLocation, projection, request, parent, children,
properties);
+ } else if (fromSource instanceof ReadBranchRequest) {
+ ReadBranchRequest readFromSource = (ReadBranchRequest)fromSource;
+ for (Location parent : readFromSource) {
+ List<Location> children = readFromSource.getChildren(parent);
+ Map<Name, Property> properties =
readFromSource.getPropertiesFor(parent);
+ projectToFederated(actualLocation, projection, request, parent,
children, properties);
+ }
+ }
+ setCacheableInfo(request, fromSource.getCachePolicy());
+ projectedRequest = projectedRequest.next();
+ ++numMerged;
+ }
+ if (numMerged == 0) {
+ // No source requests had results ...
+ setPathNotFound(request, request.at(),
federatedRequest.getFirstProjectedRequest());
+ } else {
+ request.setActualLocationOfNode(actualLocation);
+ }
+ }
+
+ /**
+ * Project the supplied node information read from a source and update the supplied
request.
+ *
+ * @param ancestorInFederation the federated node under which this information is
being projected; may not be null
+ * @param projection the projection used to make the original source request; may not
be null
+ * @param request the federated request upon which the results are to be recorded;
may not be null
+ * @param parent the location of the parent in the source; may not be null
+ * @param children the location of the children in the source; may be null or empty
+ * @param propertiesByName the properties on the parent in the source; may be null or
empty
+ */
+ protected void projectToFederated( Location ancestorInFederation,
+ Projection projection,
+ ReadBranchRequest request,
+ Location parent,
+ List<Location> children,
+ Map<Name, Property> propertiesByName ) {
+ Path ancestorPath = ancestorInFederation.getPath();
+ if (projection == null) {
+ // This is a placeholder node ...
+ if (children != null) {
+ // Add the children (to any existing children) ...
+ List<Location> existing = request.getChildren(parent);
+ if (existing == null) existing = new
ArrayList<Location>(children.size());
+ for (Location child : children) {
+ existing.add(child);
+ }
+ request.setChildren(parent, existing);
+ }
+ if (propertiesByName != null) {
+ // Add the properties to any existing properties ...
+ Map<Name, Property> propsByName =
request.getPropertiesFor(parent);
+ if (propsByName == null) propsByName = new HashMap<Name,
Property>();
+ for (Property property : propertiesByName.values()) {
+ Property existingProperty = propsByName.get(property.getName());
+ if (existingProperty != null) {
+ // Merge the property values ...
+ property = merge(existingProperty, property, propertyFactory,
true);
+ }
+ propsByName.put(property.getName(), property);
+ }
+ request.setProperties(parent, propsByName.values());
+ }
+ return;
+ }
+ for (Path path : projection.getPathsInRepository(parent.getPath(), pathFactory))
{
+ if (!path.isAtOrBelow(ancestorPath)) continue;
+
+ // Determine the list of children ...
+ Location parentInFederation = parent.with(path);
+ if (children != null) {
+ // Add the children to any existing children ...
+ List<Location> existing = request.getChildren(parentInFederation);
+ if (existing == null) existing = new
ArrayList<Location>(children.size());
+ for (Location child : children) {
+ Path childPath = pathFactory.create(path,
child.getPath().getLastSegment());
+ existing.add(child.with(childPath));
+ }
+ request.setChildren(parentInFederation, existing);
+ }
+
+ // Set or update the properties ...
+ if (propertiesByName != null) {
+ Map<Name, Property> propsByName =
request.getPropertiesFor(parentInFederation);
+ if (propsByName == null) propsByName = new HashMap<Name,
Property>();
+ for (Property property : propertiesByName.values()) {
+ Property existingProperty = propsByName.get(property.getName());
+ if (existingProperty != null) {
+ // Merge the property values ...
+ property = merge(existingProperty, property, propertyFactory,
true);
+ }
+ propsByName.put(property.getName(), property);
+ }
+ request.setProperties(parentInFederation, propsByName.values());
+ }
+ // We're done, since we found a path that's on the ancestor path ...
+ return;
+ }
+ }
+
+ /**
+ * Project the supplied location in a source into its federated location. The
projection is used to find the location under
+ * the supplied ancestor. Any errors are recorded on the original request.
+ *
+ * @param ancestorInFederation the ancestor in the federated repository; may not be
nul
+ * @param projection the projection that should be used; may not be null
+ * @param actualSourceLocation the actual location in the source that is to be
projected back into the federated repository;
+ * may not be null
+ * @param originalRequest the original request, if there are errors; may not be null
+ * @return the location in the federated repository
+ */
+ protected Location projectToFederated( Location ancestorInFederation,
+ Projection projection,
+ Location actualSourceLocation,
+ Request originalRequest ) {
+ Path ancestorPath = ancestorInFederation.getPath();
+ Path actualPathInSource = actualSourceLocation.getPath();
+ // Project the actual location ...
+ for (Path path : projection.getPathsInRepository(actualPathInSource,
pathFactory)) {
+ if (path.isAtOrBelow(ancestorPath)) {
+ return actualSourceLocation.with(path);
+ }
+ }
+ // Record that there was an error projecting the results ...
+ String whereInSource =
actualSourceLocation.getString(getExecutionContext().getNamespaceRegistry());
+ String msg =
GraphI18n.unableToProjectSourceInformationIntoWorkspace.text(whereInSource,
getSourceName(), projection);
+ originalRequest.setError(new InvalidRequestException(msg));
+ return null;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see
org.jboss.dna.graph.request.processor.RequestProcessor#process(org.jboss.dna.graph.request.CreateNodeRequest)
+ */
+ @Override
+ public void process( CreateNodeRequest request ) {
+ ProjectedRequest projected = federatedRequest.getFirstProjectedRequest();
+ assert !projected.hasNext();
+ CreateNodeRequest source = (CreateNodeRequest)projected.getRequest();
+ if (checkErrorOrCancel(request, source)) return;
+ Location sourceLocation = source.getActualLocationOfNode();
+ request.setActualLocationOfNode(projectToFederated(request.under(),
projected.getProjection(), sourceLocation, request));
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see
org.jboss.dna.graph.request.processor.RequestProcessor#process(org.jboss.dna.graph.request.UpdatePropertiesRequest)
+ */
+ @Override
+ public void process( UpdatePropertiesRequest request ) {
+ ProjectedRequest projected = federatedRequest.getFirstProjectedRequest();
+ assert !projected.hasNext();
+ UpdatePropertiesRequest source =
(UpdatePropertiesRequest)projected.getRequest();
+ if (checkErrorOrCancel(request, source)) return;
+ Location sourceLocation = source.getActualLocationOfNode();
+ request.setActualLocationOfNode(projectToFederated(request.on(),
projected.getProjection(), sourceLocation, request));
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see
org.jboss.dna.graph.request.processor.RequestProcessor#process(org.jboss.dna.graph.request.DeleteBranchRequest)
+ */
+ @Override
+ public void process( DeleteBranchRequest request ) {
+ ProjectedRequest projected = federatedRequest.getFirstProjectedRequest();
+ // Go through the projected requests, and look for the top-most node ...
+ Location highest = null;
+ while (projected != null) {
+ // The projected request should either be a DeleteChildrenRequest (if the
node being deleted is
+ // at the top of a projection and therefore required to exist) or a
DeleteBranchRequest (in all
+ // other cases)...
+ Location actual = null;
+ Request sourceRequest = projected.getRequest();
+ if (sourceRequest instanceof DeleteBranchRequest) {
+ DeleteBranchRequest source =
(DeleteBranchRequest)projected.getRequest();
+ actual = source.getActualLocationOfNode();
+ } else {
+ DeleteChildrenRequest source =
(DeleteChildrenRequest)projected.getRequest();
+ actual = source.getActualLocationOfNode();
+ }
+ if (checkErrorOrCancel(request, sourceRequest)) return;
+ if (!projected.isSameLocation() && projected.getProjection() != null)
{
+ actual = projectToFederated(request.at(), projected.getProjection(),
actual, request);
+ }
+ if (highest == null) highest = actual;
+ else if (highest.getPath().isDecendantOf(actual.getPath())) highest =
actual;
+ projected = projected.next();
+ }
+ assert highest != null;
+ request.setActualLocationOfNode(highest);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see
org.jboss.dna.graph.request.processor.RequestProcessor#process(org.jboss.dna.graph.request.CopyBranchRequest)
+ */
+ @Override
+ public void process( CopyBranchRequest request ) {
+ ProjectedRequest projected = federatedRequest.getFirstProjectedRequest();
+ assert !projected.hasNext();
+ CopyBranchRequest source = (CopyBranchRequest)projected.getRequest();
+ if (checkErrorOrCancel(request, source)) return;
+ Location locationBefore = source.getActualLocationBefore();
+ Location locationAfter = source.getActualLocationBefore();
+ locationBefore = projectToFederated(request.from(), projected.getProjection(),
locationBefore, request);
+ locationAfter = projectToFederated(request.into(),
projected.getSecondProjection(), locationAfter, request);
+ request.setActualLocations(locationBefore, locationAfter);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see
org.jboss.dna.graph.request.processor.RequestProcessor#process(org.jboss.dna.graph.request.MoveBranchRequest)
+ */
+ @Override
+ public void process( MoveBranchRequest request ) {
+ ProjectedRequest projected = federatedRequest.getFirstProjectedRequest();
+ assert !projected.hasNext();
+ MoveBranchRequest source = (MoveBranchRequest)projected.getRequest();
+ if (checkErrorOrCancel(request, source)) return;
+ Location locationBefore = source.getActualLocationBefore();
+ Location locationAfter = source.getActualLocationBefore();
+ locationBefore = projectToFederated(request.from(), projected.getProjection(),
locationBefore, request);
+ locationAfter = projectToFederated(request.into(),
projected.getSecondProjection(), locationAfter, request);
+ request.setActualLocations(locationBefore, locationAfter);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see
org.jboss.dna.graph.request.processor.RequestProcessor#process(org.jboss.dna.graph.request.VerifyWorkspaceRequest)
+ */
+ @Override
+ public void process( VerifyWorkspaceRequest request ) {
+ ProjectedRequest projectedRequest = federatedRequest.getFirstProjectedRequest();
+ assert projectedRequest != null;
+ Location actualLocation =
Location.create(getExecutionContext().getValueFactories().getPathFactory().createRootPath());
+ while (projectedRequest != null) {
+ VerifyNodeExistsRequest readFromSource =
(VerifyNodeExistsRequest)projectedRequest.getRequest();
+ if (readFromSource.hasError()) {
+ request.setError(readFromSource.getError());
+ continue;
+ }
+ request.setError(null);
+ if (readFromSource.isCancelled()) {
+ request.cancel();
+ return;
+ }
+
+ // Accumulate the identification properties ...
+ for (Property propertyInSource :
readFromSource.getActualLocationOfNode().getIdProperties()) {
+ Name name = propertyInSource.getName();
+ Property existing = actualLocation.getIdProperty(name);
+ if (existing != null) {
+ // Merge the property values ...
+ propertyInSource = merge(existing, propertyInSource, propertyFactory,
true);
+ }
+ actualLocation = actualLocation.with(propertyInSource);
+ }
+ projectedRequest = projectedRequest.next();
+ }
+ request.setActualRootLocation(actualLocation);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see
org.jboss.dna.graph.request.processor.RequestProcessor#process(org.jboss.dna.graph.request.GetWorkspacesRequest)
+ */
+ @Override
+ public void process( GetWorkspacesRequest request ) {
+ throw new UnsupportedOperationException(); // should never be called, since
it's handled in the ForkProcessor
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see
org.jboss.dna.graph.request.processor.RequestProcessor#process(org.jboss.dna.graph.request.CreateWorkspaceRequest)
+ */
+ @Override
+ public void process( CreateWorkspaceRequest request ) {
+ throw new UnsupportedOperationException(); // should never be called, since
it's handled in the ForkProcessor
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see
org.jboss.dna.graph.request.processor.RequestProcessor#process(org.jboss.dna.graph.request.CloneWorkspaceRequest)
+ */
+ @Override
+ public void process( CloneWorkspaceRequest request ) {
+ throw new UnsupportedOperationException(); // should never be called, since
it's handled in the ForkProcessor
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see
org.jboss.dna.graph.request.processor.RequestProcessor#process(org.jboss.dna.graph.request.DestroyWorkspaceRequest)
+ */
+ @Override
+ public void process( DestroyWorkspaceRequest request ) {
+ throw new UnsupportedOperationException(); // should never be called, since
it's handled in the ForkProcessor
+ }
+
+ protected boolean checkErrorOrCancel( Request request,
+ Request sourceRequest ) {
+ if (sourceRequest.hasError()) {
+ request.setError(sourceRequest.getError());
+ return true;
+ }
+ if (sourceRequest.isCancelled()) {
+ request.cancel();
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Merge the values from the two properties with the same name, returning a new
property with the newly merged values.
+ * <p>
+ * The current algorithm merges the values by concatenating the values from
<code>property1</code> and <code>property2</code>,
+ * and if <code>removeDuplicates</code> is true any values in
<code>property2</code> that are identical to values found in
+ * <code>property1</code> are skipped.
+ * </p>
+ *
+ * @param property1 the first property; may not be null, and must have the same
{@link Property#getName() name} as
+ * <code>property2</code>
+ * @param property2 the second property; may not be null, and must have the same
{@link Property#getName() name} as
+ * <code>property1</code>
+ * @param factory the property factory, used to create the result
+ * @param removeDuplicates true if this method removes any values in the second
property that duplicate values found in the
+ * first property.
+ * @return the property that contains the same {@link Property#getName() name} as the
input properties, but with values that
+ * are merged from both of the input properties
+ */
+ protected Property merge( Property property1,
+ Property property2,
+ PropertyFactory factory,
+ boolean removeDuplicates ) {
+ assert property1 != null;
+ assert property2 != null;
+ assert property1.getName().equals(property2.getName());
+ if (property1.isEmpty()) return property2;
+ if (property2.isEmpty()) return property1;
+
+ // If they are both single-valued, then we can use a more efficient algorithm
...
+ if (property1.isSingle() && property2.isSingle()) {
+ Object value1 = property1.getValues().next();
+ Object value2 = property2.getValues().next();
+ if (removeDuplicates &&
ValueComparators.OBJECT_COMPARATOR.compare(value1, value2) == 0) return property1;
+ return factory.create(property1.getName(), new Object[] {value1, value2});
+ }
+
+ // One or both properties are multi-valued, so use an algorithm that works with
in all cases ...
+ if (!removeDuplicates) {
+ Iterator<?> valueIterator = new DualIterator(property1.getValues(),
property2.getValues());
+ return factory.create(property1.getName(), valueIterator);
+ }
+
+ // First copy all the values from property 1 ...
+ Object[] values = new Object[property1.size() + property2.size()];
+ int index = 0;
+ for (Object property1Value : property1) {
+ values[index++] = property1Value;
+ }
+ assert index == property1.size();
+ // Now add any values of property2 that don't match a value in property1 ...
+ for (Object property2Value : property2) {
+ // Brute force, go through the values of property1 and compare ...
+ boolean matched = false;
+ for (Object property1Value : property1) {
+ if (ValueComparators.OBJECT_COMPARATOR.compare(property1Value,
property2Value) == 0) {
+ // The values are the same ...
+ matched = true;
+ break;
+ }
+ }
+ if (!matched) values[index++] = property2Value;
+ }
+ if (index != values.length) {
+ Object[] newValues = new Object[index];
+ System.arraycopy(values, 0, newValues, 0, index);
+ values = newValues;
+ }
+ return factory.create(property1.getName(), values);
+ }
+
+ protected static class DualIterator implements Iterator<Object> {
+
+ private final Iterator<?>[] iterators;
+ private Iterator<?> current;
+ private int index = 0;
+
+ protected DualIterator( Iterator<?>... iterators ) {
+ assert iterators != null;
+ assert iterators.length > 0;
+ this.iterators = iterators;
+ this.current = this.iterators[0];
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.util.Iterator#hasNext()
+ */
+ public boolean hasNext() {
+ if (this.current != null) return this.current.hasNext();
+ return false;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.util.Iterator#next()
+ */
+ public Object next() {
+ while (this.current != null) {
+ if (this.current.hasNext()) return this.current.next();
+ // Get the next iterator ...
+ if (++this.index < iterators.length) {
+ this.current = this.iterators[this.index];
+ } else {
+ this.current = null;
+ }
+ }
+ throw new NoSuchElementException();
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.util.Iterator#remove()
+ */
+ public void remove() {
+ throw new UnsupportedOperationException();
+ }
+ }
+
+}
Property changes on:
trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/JoinRequestProcessor.java
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Added:
trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/MirrorProjector.java
===================================================================
---
trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/MirrorProjector.java
(rev 0)
+++
trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/MirrorProjector.java 2009-05-18
15:36:58 UTC (rev 911)
@@ -0,0 +1,83 @@
+/*
+ * JBoss DNA (
http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership. Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of
+ * individual contributors.
+ *
+ * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
+ * is licensed to you under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * JBoss DNA is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site:
http://www.fsf.org.
+ */
+package org.jboss.dna.graph.connector.federation;
+
+import java.util.List;
+import net.jcip.annotations.Immutable;
+import org.jboss.dna.graph.ExecutionContext;
+import org.jboss.dna.graph.Location;
+import org.jboss.dna.graph.property.Path;
+import org.jboss.dna.graph.property.PathFactory;
+
+/**
+ * A Projector for federated repository configurations that are a direct one-for-one
mirror against a single source repository. In
+ * other words, the federated repository has a single projection with a single "/
=> /" rule.
+ */
+@Immutable
+final class MirrorProjector implements Projector {
+
+ /**
+ * Attempt to create an instance of the {@link MirrorProjector} with the supplied
projections using the supplied context.
+ *
+ * @param context the context; may not be null
+ * @param projections the projections in the federated repository; may not be null
+ * @return the mirror projector, or null if the projections didn't match the
criteria for such a projector
+ */
+ static MirrorProjector with( ExecutionContext context,
+ List<Projection> projections ) {
+ assert projections != null;
+ assert context != null;
+ if (projections.size() != 1) return null;
+ Projection projection = projections.get(0);
+ assert projection != null;
+ if (projection.getRules().size() != 1) return null;
+ PathFactory pathFactory = context.getValueFactories().getPathFactory();
+ List<Path> topLevelPaths =
projection.getRules().get(0).getTopLevelPathsInRepository(pathFactory);
+ if (topLevelPaths.size() != 1) return null;
+ if (!topLevelPaths.get(0).isRoot()) return null;
+ return new MirrorProjector(projection);
+ }
+
+ private final Projection projection;
+
+ private MirrorProjector( Projection projection ) {
+ this.projection = projection;
+ }
+
+ /**
+ * {@inheritDoc}
+ * <p>
+ * This implementation <i>always<i> returns a single {@link ProxyNode}
for the location in the single projection.
+ * </p>
+ *
+ * @see org.jboss.dna.graph.connector.federation.Projector#project(ExecutionContext,
Location, boolean)
+ */
+ public ProjectedNode project( ExecutionContext context,
+ Location location,
+ boolean requiresUpdate ) {
+ if (requiresUpdate && projection.isReadOnly()) return null;
+ return new ProxyNode(projection, location, location);
+ }
+
+}
Property changes on:
trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/MirrorProjector.java
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Added:
trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/NoMoreFederatedRequests.java
===================================================================
---
trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/NoMoreFederatedRequests.java
(rev 0)
+++
trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/NoMoreFederatedRequests.java 2009-05-18
15:36:58 UTC (rev 911)
@@ -0,0 +1,40 @@
+/*
+ * JBoss DNA (
http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership. Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of
+ * individual contributors.
+ *
+ * Unless otherwise indicated, all code in JBoss DNA is licensed
+ * to you under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * JBoss DNA is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site:
http://www.fsf.org.
+ */
+package org.jboss.dna.graph.connector.federation;
+
+/**
+ * Specialization of {@link FederatedRequest}, which is used to place an instance in a
queue so the {@link JoinRequestProcessor}
+ * knows that all federated requests have been processed.
+ *
+ * @see JoinRequestProcessor#process(java.util.concurrent.BlockingQueue)
+ */
+class NoMoreFederatedRequests extends FederatedRequest {
+
+ /**
+ */
+ NoMoreFederatedRequests() {
+ super(null);
+ }
+
+}
Property changes on:
trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/NoMoreFederatedRequests.java
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Added:
trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/OffsetMirrorProjector.java
===================================================================
---
trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/OffsetMirrorProjector.java
(rev 0)
+++
trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/OffsetMirrorProjector.java 2009-05-18
15:36:58 UTC (rev 911)
@@ -0,0 +1,114 @@
+/*
+ * JBoss DNA (
http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership. Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of
+ * individual contributors.
+ *
+ * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
+ * is licensed to you under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * JBoss DNA is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site:
http://www.fsf.org.
+ */
+package org.jboss.dna.graph.connector.federation;
+
+import java.util.List;
+import net.jcip.annotations.Immutable;
+import org.jboss.dna.graph.ExecutionContext;
+import org.jboss.dna.graph.Location;
+import org.jboss.dna.graph.property.Path;
+import org.jboss.dna.graph.property.PathFactory;
+
+/**
+ * A Projector for federated repository configurations that are an offset, direct
one-for-one mirror against a single source
+ * repository that is projected below the federated root. In other words, the federated
repository has a single projection with a
+ * single "/something/below/root => /" rule.
+ */
+@Immutable
+final class OffsetMirrorProjector extends ProjectorWithPlaceholders {
+
+ /**
+ * Attempt to create an instance of the {@link OffsetMirrorProjector} with the
supplied projections using the supplied
+ * context.
+ *
+ * @param context the context; may not be null
+ * @param projections the projections in the federated repository; may not be null
+ * @return the offset mirror projector, or null if the projections didn't match
the criteria for such a projector
+ */
+ static OffsetMirrorProjector with( ExecutionContext context,
+ List<Projection> projections ) {
+ assert projections != null;
+ assert context != null;
+ if (projections.size() != 1) return null;
+ Projection projection = projections.get(0);
+ assert projection != null;
+ if (projection.getRules().size() != 1) return null;
+ PathFactory pathFactory = context.getValueFactories().getPathFactory();
+ List<Path> topLevelPaths =
projection.getRules().get(0).getTopLevelPathsInRepository(pathFactory);
+ if (topLevelPaths.size() != 1) return null;
+ Path topLevelPath = topLevelPaths.get(0);
+ assert topLevelPath != null;
+ if (topLevelPath.isRoot()) return null;
+ return new OffsetMirrorProjector(context, projections, topLevelPath);
+ }
+
+ private final Projection projection;
+ private final Path offset;
+ private final int offsetSize;
+
+ private OffsetMirrorProjector( ExecutionContext context,
+ List<Projection> projections,
+ Path offset ) {
+ super(context, projections);
+ this.projection = projections.get(0);
+ this.offset = offset;
+ this.offsetSize = offset.size();
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.connector.federation.Projector#project(ExecutionContext,
Location, boolean)
+ */
+ public ProjectedNode project( ExecutionContext context,
+ Location location,
+ boolean requiresUpdate ) {
+ if (requiresUpdate && projection.isReadOnly()) return null;
+ PlaceholderNode placeholder = isPlaceholder(location);
+ if (placeholder != null) return placeholder;
+
+ Path path = location.getPath();
+ Location locationInSource = location;
+ if (path != null) {
+ if (path.size() == offsetSize) {
+ // Make sure the path is the same ...
+ if (path.equals(offset)) {
+ locationInSource =
location.with(context.getValueFactories().getPathFactory().createRootPath());
+ } else {
+ return null; // not in the path
+ }
+ } else {
+ // Make sure the path begins with the offset ...
+ if (path.isAtOrBelow(offset)) {
+ locationInSource = location.with(path.subpath(offsetSize));
+ } else {
+ // Not in the path
+ return null;
+ }
+ }
+ }
+ return new ProxyNode(projection, locationInSource, location);
+ }
+
+}
Property changes on:
trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/OffsetMirrorProjector.java
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Added:
trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/ProjectedNode.java
===================================================================
---
trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/ProjectedNode.java
(rev 0)
+++
trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/ProjectedNode.java 2009-05-18
15:36:58 UTC (rev 911)
@@ -0,0 +1,200 @@
+/*
+ * JBoss DNA (
http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership. Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of
+ * individual contributors.
+ *
+ * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
+ * is licensed to you under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * JBoss DNA is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site:
http://www.fsf.org.
+ */
+package org.jboss.dna.graph.connector.federation;
+
+import java.util.List;
+import java.util.Map;
+import net.jcip.annotations.Immutable;
+import net.jcip.annotations.NotThreadSafe;
+import org.jboss.dna.graph.Location;
+import org.jboss.dna.graph.property.Name;
+import org.jboss.dna.graph.property.Property;
+
+/**
+ * Information about a node that has been projected into the repository from a source.
Each projected node may be followed by a
+ * {@link #next() next} projected node.
+ */
+@Immutable
+abstract class ProjectedNode {
+ private final Location location;
+ private ProjectedNode next;
+
+ protected ProjectedNode( Location location ) {
+ this.location = location;
+ }
+
+ public Location location() {
+ return location;
+ }
+
+ public int size() {
+ return next != null ? next.size() + 1 : 1;
+ }
+
+ public int numberOfProxies() {
+ int base = isProxy() ? 1 : 0;
+ return next != null ? next.numberOfProxies() + base : base;
+ }
+
+ public boolean hasNext() {
+ return next != null;
+ }
+
+ public ProjectedNode next() {
+ return next;
+ }
+
+ /*package*/void add( ProjectedNode next ) {
+ if (this.next != null) {
+ this.next.add(next);
+ } else {
+ this.next = next;
+ }
+ }
+
+ public abstract boolean isPlaceholder();
+
+ public abstract boolean isProxy();
+
+ public abstract PlaceholderNode asPlaceholder();
+
+ public abstract ProxyNode asProxy();
+}
+
+@Immutable
+class PlaceholderNode extends ProjectedNode {
+ private final Map<Name, Property> properties;
+ private final List<Location> children;
+
+ protected PlaceholderNode( Location location,
+ Map<Name, Property> properties,
+ List<Location> children ) {
+ super(location);
+ this.properties = properties;
+ this.children = children;
+ }
+
+ public List<Location> children() {
+ return children;
+ }
+
+ public Map<Name, Property> properties() {
+ return properties;
+ }
+
+ @Override
+ public boolean isPlaceholder() {
+ return true;
+ }
+
+ @Override
+ public PlaceholderNode asPlaceholder() {
+ return this;
+ }
+
+ @Override
+ public boolean isProxy() {
+ return false;
+ }
+
+ @Override
+ public ProxyNode asProxy() {
+ return null;
+ }
+}
+
+@NotThreadSafe
+class ProxyNode extends ProjectedNode {
+ private final Projection projection;
+ private final boolean sameLocationAsOriginal;
+ private final Location federatedLocation;
+
+ protected ProxyNode( Projection projection,
+ Location locationInSource,
+ Location locationInFederated ) {
+ super(locationInSource);
+ this.projection = projection;
+ this.federatedLocation = locationInFederated;
+ this.sameLocationAsOriginal = locationInSource.equals(locationInFederated);
+ }
+
+ protected ProxyNode( Projection projection,
+ Location locationInSource,
+ Location locationInFederated,
+ boolean isSameLocation ) {
+ super(locationInSource);
+ this.projection = projection;
+ this.federatedLocation = locationInFederated;
+ this.sameLocationAsOriginal = isSameLocation;
+ }
+
+ public String source() {
+ return projection.getSourceName();
+ }
+
+ public String workspaceName() {
+ return projection.getWorkspaceName();
+ }
+
+ /*package*/Projection projection() {
+ return projection;
+ }
+
+ public Location federatedLocation() {
+ return federatedLocation;
+ }
+
+ @Override
+ public boolean isPlaceholder() {
+ return false;
+ }
+
+ @Override
+ public PlaceholderNode asPlaceholder() {
+ return null;
+ }
+
+ @Override
+ public boolean isProxy() {
+ return true;
+ }
+
+ @Override
+ public ProxyNode asProxy() {
+ return this;
+ }
+
+ /**
+ * Determine whether this projected node is a top-level node in the source projected
into the federated repository.
+ *
+ * @return true if the projected node is a top-level node in the source, or false
otherwise
+ */
+ public boolean isTopLevelNode() {
+ return federatedLocation != null && federatedLocation.hasPath()
&& projection.isTopLevelPath(federatedLocation.getPath());
+ }
+
+ public boolean isSameLocationAsOriginal() {
+ return this.sameLocationAsOriginal;
+ }
+}
Property changes on:
trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/ProjectedNode.java
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Added:
trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/Projection.java
===================================================================
---
trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/Projection.java
(rev 0)
+++
trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/Projection.java 2009-05-18
15:36:58 UTC (rev 911)
@@ -0,0 +1,887 @@
+/*
+ * JBoss DNA (
http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership. Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of
+ * individual contributors.
+ *
+ * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
+ * is licensed to you under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * JBoss DNA is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site:
http://www.fsf.org.
+ */
+package org.jboss.dna.graph.connector.federation;
+
+import java.io.Serializable;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import net.jcip.annotations.Immutable;
+import org.jboss.dna.common.text.TextEncoder;
+import org.jboss.dna.common.util.CheckArg;
+import org.jboss.dna.common.util.HashCode;
+import org.jboss.dna.common.util.Logger;
+import org.jboss.dna.graph.ExecutionContext;
+import org.jboss.dna.graph.GraphI18n;
+import org.jboss.dna.graph.connector.RepositorySource;
+import org.jboss.dna.graph.property.NamespaceRegistry;
+import org.jboss.dna.graph.property.Path;
+import org.jboss.dna.graph.property.PathFactory;
+
+/**
+ * A projection of content from a source into the integrated/federated repository. Each
project consists of a set of {@link Rule
+ * rules} for a particular source, where each rule defines how content within a source
is
+ * {@link Rule#getPathInRepository(Path, PathFactory) is project into the repository} and
how the repository content is
+ * {@link Rule#getPathInSource(Path, PathFactory) projected into the source}. Different
rule subclasses are used for different
+ * types.
+ */
+@Immutable
+public class Projection implements Comparable<Projection>, Serializable {
+
+ /**
+ * Initial version
+ */
+ private static final long serialVersionUID = 1L;
+ protected static final List<Method> parserMethods;
+ static {
+ parserMethods = new CopyOnWriteArrayList<Method>();
+ try {
+
parserMethods.add(Projection.class.getDeclaredMethod("parsePathRule",
String.class, ExecutionContext.class));
+ } catch (Throwable err) {
+ Logger.getLogger(Projection.class).error(err,
GraphI18n.errorAddingProjectionRuleParseMethod);
+ }
+ }
+
+ /**
+ * Add a static method that can be used to parse {@link
Rule#getString(NamespaceRegistry, TextEncoder) rule definition
+ * strings}. These methods must be static, must accept a {@link String} definition as
the first parameter and an
+ * {@link ExecutionContext} environment reference as the second parameter, and should
return the resulting {@link Rule} (or
+ * null if the definition format could not be understood by the method. Any
exceptions during
+ * {@link Method#invoke(Object, Object...) invocation} will be logged at the
+ * {@link Logger#trace(Throwable, String, Object...) trace} level.
+ *
+ * @param method the method to be added
+ * @see #addRuleParser(ClassLoader, String, String)
+ */
+ public static void addRuleParser( Method method ) {
+ if (method != null) parserMethods.add(method);
+ }
+
+ /**
+ * Add a static method that can be used to parse {@link
Rule#getString(NamespaceRegistry, TextEncoder) rule definition
+ * strings}. These methods must be static, must accept a {@link String} definition as
the first parameter and an
+ * {@link ExecutionContext} environment reference as the second parameter, and should
return the resulting {@link Rule} (or
+ * null if the definition format could not be understood by the method. Any
exceptions during
+ * {@link Method#invoke(Object, Object...) invocation} will be logged at the
+ * {@link Logger#trace(Throwable, String, Object...) trace} level.
+ *
+ * @param classLoader the class loader that should be used to load the class on which
the method is defined; may not be null
+ * @param className the name of the class on which the static method is defined; may
not be null
+ * @param methodName the name of the method
+ * @throws SecurityException if there is a security exception while loading the class
or getting the method
+ * @throws NoSuchMethodException if the method does not exist on the class
+ * @throws ClassNotFoundException if the class could not be found given the supplied
class loader
+ * @throws IllegalArgumentException if the class loader reference is null, or if the
class name or method name are null or
+ * empty
+ * @see #addRuleParser(Method)
+ */
+ public static void addRuleParser( ClassLoader classLoader,
+ String className,
+ String methodName ) throws SecurityException,
NoSuchMethodException, ClassNotFoundException {
+ CheckArg.isNotNull(classLoader, "classLoader");
+ CheckArg.isNotEmpty(className, "className");
+ CheckArg.isNotEmpty(methodName, "methodName");
+ Class<?> clazz = Class.forName(className, true, classLoader);
+ parserMethods.add(clazz.getMethod(className, String.class,
ExecutionContext.class));
+ }
+
+ /**
+ * Remove the rule parser method.
+ *
+ * @param method the method to remove
+ * @return true if the method was removed, or false if the method was not a
registered rule parser method
+ */
+ public static boolean removeRuleParser( Method method ) {
+ return parserMethods.remove(method);
+ }
+
+ /**
+ * Remove the rule parser method.
+ *
+ * @param declaringClassName the name of the class on which the static method is
defined; may not be null
+ * @param methodName the name of the method
+ * @return true if the method was removed, or false if the method was not a
registered rule parser method
+ * @throws IllegalArgumentException if the class loader reference is null, or if the
class name or method name are null or
+ * empty
+ */
+ public static boolean removeRuleParser( String declaringClassName,
+ String methodName ) {
+ CheckArg.isNotEmpty(declaringClassName, "declaringClassName");
+ CheckArg.isNotEmpty(methodName, "methodName");
+ for (Method method : parserMethods) {
+ if (method.getName().equals(methodName) &&
method.getDeclaringClass().getName().equals(declaringClassName)) {
+ return parserMethods.remove(method);
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Parse the string form of a rule definition and return the rule
+ *
+ * @param definition the definition of the rule that is to be parsed
+ * @param context the environment in which this method is being executed; may not be
null
+ * @return the rule, or null if the definition could not be parsed
+ */
+ public static Rule fromString( String definition,
+ ExecutionContext context ) {
+ CheckArg.isNotNull(context, "env");
+ definition = definition != null ? definition.trim() : "";
+ if (definition.length() == 0) return null;
+ for (Method method : parserMethods) {
+ try {
+ Rule rule = (Rule)method.invoke(null, definition, context);
+ if (rule != null) return rule;
+ } catch (Throwable err) {
+ String msg = "Error while parsing project rule definition
\"{0}\" using {1}";
+ context.getLogger(Projection.class).trace(err, msg, definition, method);
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Pattern that identifies the form:
+ *
+ * <pre>
+ * repository_path => source_path [$ exception ]*
+ * </pre>
+ *
+ * where the following groups are captured on the first call to {@link
Matcher#find()}:
+ * <ol>
+ * <li><code>repository_path</code></li>
+ * <li><code>source_path</code></li>
+ * </ol>
+ * and the following groups are captured on subsequent calls to {@link
Matcher#find()}:
+ * <ol>
+ * <li>exception</code></li>
+ * </ol>
+ * <p>
+ * The regular expression is:
+ *
+ * <pre>
+ *
((?:[ˆ=$]|=(?!>))+)(?:(?:=>((?:[ˆ=$]|=(?!>))+))( \$
(?:(?:[ˆ=]|=(?!>))+))*)?
+ * </pre>
+ *
+ * </p>
+ */
+ protected static final String PATH_RULE_PATTERN_STRING =
"((?:[^=$]|=(?!>))+)(?:(?:=>((?:[^=$]|=(?!>))+))( \\$
(?:(?:[^=]|=(?!>))+))*)?";
+ protected static final Pattern PATH_RULE_PATTERN =
Pattern.compile(PATH_RULE_PATTERN_STRING);
+
+ /**
+ * Parse the string definition of a {@link PathRule}. This method is automatically
registered in the {@link #parserMethods
+ * parser methods} by the static initializer of {@link Projection}.
+ *
+ * @param definition the definition
+ * @param context the environment
+ * @return the path rule, or null if the definition is not in the right form
+ */
+ public static PathRule parsePathRule( String definition,
+ ExecutionContext context ) {
+ definition = definition != null ? definition.trim() : "";
+ if (definition.length() == 0) return null;
+ Matcher matcher = PATH_RULE_PATTERN.matcher(definition);
+ if (!matcher.find()) return null;
+ String reposPathStr = matcher.group(1);
+ String sourcePathStr = matcher.group(2);
+ if (reposPathStr == null || sourcePathStr == null) return null;
+ reposPathStr = reposPathStr.trim();
+ sourcePathStr = sourcePathStr.trim();
+ if (reposPathStr.length() == 0 || sourcePathStr.length() == 0) return null;
+ PathFactory pathFactory = context.getValueFactories().getPathFactory();
+ Path repositoryPath = pathFactory.create(reposPathStr);
+ Path sourcePath = pathFactory.create(sourcePathStr);
+
+ // Grab the exceptions ...
+ List<Path> exceptions = new LinkedList<Path>();
+ while (matcher.find()) {
+ String exceptionStr = matcher.group(1);
+ Path exception = pathFactory.create(exceptionStr);
+ exceptions.add(exception);
+ }
+ return new PathRule(repositoryPath, sourcePath, exceptions);
+ }
+
+ private final String sourceName;
+ private final String workspaceName;
+ private final List<Rule> rules;
+ private final boolean simple;
+ private final boolean readOnly;
+ private final int hc;
+
+ /**
+ * Create a new federated projection for the supplied source, using the supplied
rules.
+ *
+ * @param sourceName the name of the source
+ * @param workspaceName the name of the workspace in the source; may be null if the
default workspace is to be used
+ * @param readOnly true if this projection is considered read-only, or false if the
content of the projection may be modified
+ * by the federated clients
+ * @param rules the projection rules
+ * @throws IllegalArgumentException if the source name or rule array is null, empty,
or contains all nulls
+ */
+ public Projection( String sourceName,
+ String workspaceName,
+ boolean readOnly,
+ Rule... rules ) {
+ CheckArg.isNotEmpty(sourceName, "sourceName");
+ CheckArg.isNotEmpty(rules, "rules");
+ this.sourceName = sourceName;
+ this.workspaceName = workspaceName;
+ List<Rule> rulesList = new ArrayList<Rule>();
+ for (Rule rule : rules) {
+ if (rule != null) rulesList.add(rule);
+ }
+ this.readOnly = readOnly;
+ this.rules = Collections.unmodifiableList(rulesList);
+ CheckArg.isNotEmpty(this.rules, "rules");
+ this.simple = computeSimpleProjection(this.rules);
+ this.hc = HashCode.compute(this.sourceName, this.workspaceName);
+ }
+
+ /**
+ * Get the name of the source to which this projection applies.
+ *
+ * @return the source name
+ * @see RepositorySource#getName()
+ */
+ public String getSourceName() {
+ return sourceName;
+ }
+
+ /**
+ * Get the name of the workspace in the source to which this projection applies.
+ *
+ * @return the workspace name, or null if the default workspace of the {@link
#getSourceName() source} is to be used
+ */
+ public String getWorkspaceName() {
+ return workspaceName;
+ }
+
+ /**
+ * Get the rules that define this projection.
+ *
+ * @return the unmodifiable list of immutable rules; never null
+ */
+ public List<Rule> getRules() {
+ return rules;
+ }
+
+ /**
+ * Get the paths in the source that correspond to the supplied path within the
repository. This method computes the paths
+ * given all of the rules. In general, most sources will probably project a node onto
a single repository node. However, some
+ * sources may be configured such that the same node in the repository is a
projection of multiple nodes within the source.
+ *
+ * @param canonicalPathInRepository the canonical path of the node within the
repository; may not be null
+ * @param factory the path factory; may not be null
+ * @return the set of unique paths in the source projected from the repository path;
never null
+ * @throws IllegalArgumentException if the factory reference is null
+ */
+ public Set<Path> getPathsInSource( Path canonicalPathInRepository,
+ PathFactory factory ) {
+ CheckArg.isNotNull(factory, "factory");
+ assert canonicalPathInRepository == null ? true :
canonicalPathInRepository.equals(canonicalPathInRepository.getCanonicalPath());
+ Set<Path> paths = new HashSet<Path>();
+ for (Rule rule : getRules()) {
+ Path pathInSource = rule.getPathInSource(canonicalPathInRepository,
factory);
+ if (pathInSource != null) paths.add(pathInSource);
+ }
+ return paths;
+ }
+
+ /**
+ * Get the paths in the repository that correspond to the supplied path within the
source. This method computes the paths
+ * given all of the rules. In general, most sources will probably project a node onto
a single repository node. However, some
+ * sources may be configured such that the same node in the source is projected into
multiple nodes within the repository.
+ *
+ * @param canonicalPathInSource the canonical path of the node within the source; may
not be null
+ * @param factory the path factory; may not be null
+ * @return the set of unique paths in the repository projected from the source path;
never null
+ * @throws IllegalArgumentException if the factory reference is null
+ */
+ public Set<Path> getPathsInRepository( Path canonicalPathInSource,
+ PathFactory factory ) {
+ CheckArg.isNotNull(factory, "factory");
+ assert canonicalPathInSource == null ? true :
canonicalPathInSource.equals(canonicalPathInSource.getCanonicalPath());
+ Set<Path> paths = new HashSet<Path>();
+ for (Rule rule : getRules()) {
+ Path pathInRepository = rule.getPathInRepository(canonicalPathInSource,
factory);
+ if (pathInRepository != null) paths.add(pathInRepository);
+ }
+ return paths;
+ }
+
+ /**
+ * Get the paths in the repository that serve as top-level nodes exposed by this
projection.
+ *
+ * @param factory the path factory that can be used to create new paths; may not be
null
+ * @return the list of top-level paths, in the proper order and containing no
duplicates; never null
+ */
+ public List<Path> getTopLevelPathsInRepository( PathFactory factory ) {
+ CheckArg.isNotNull(factory, "factory");
+ List<Rule> rules = getRules();
+ Set<Path> uniquePaths = new HashSet<Path>();
+ List<Path> paths = new ArrayList<Path>(rules.size());
+ for (Rule rule : getRules()) {
+ for (Path path : rule.getTopLevelPathsInRepository(factory)) {
+ if (!uniquePaths.contains(path)) {
+ paths.add(path);
+ uniquePaths.add(path);
+ }
+ }
+ }
+ return paths;
+ }
+
+ /**
+ * Determine whether the supplied repositoryPath is considered one of the top-level
nodes in this projection.
+ *
+ * @param repositoryPath path in the repository; may not be null
+ * @return true if the supplied repository path is one of the top-level nodes exposed
by this projection, or false otherwise
+ */
+ public boolean isTopLevelPath( Path repositoryPath ) {
+ for (Rule rule : getRules()) {
+ if (rule.isTopLevelPath(repositoryPath)) return true;
+ }
+ return false;
+ }
+
+ /**
+ * Determine whether this project is a simple projection that only involves for any
one repository path no more than a single
+ * source path.
+ *
+ * @return true if this projection is a simple projection, or false if the projection
is not simple (or it cannot be
+ * determined if it is simple)
+ */
+ public boolean isSimple() {
+ return simple;
+ }
+
+ /**
+ * Determine whether the content projected by this projection is read-only.
+ *
+ * @return true if the content is read-only, or false if it can be modified
+ */
+ public boolean isReadOnly() {
+ return readOnly;
+ }
+
+ protected boolean computeSimpleProjection( List<Rule> rules ) {
+ // Get the set of repository paths for the rules, and see if they overlap ...
+ Set<Path> repositoryPaths = new HashSet<Path>();
+ for (Rule rule : rules) {
+ if (rule instanceof PathRule) {
+ PathRule pathRule = (PathRule)rule;
+ Path repoPath = pathRule.getPathInRepository();
+ if (!repositoryPaths.isEmpty()) {
+ if (repositoryPaths.contains(repoPath)) return false;
+ for (Path path : repositoryPaths) {
+ if (path.isAtOrAbove(repoPath)) return false;
+ if (repoPath.isAtOrAbove(path)) return false;
+ }
+ }
+ repositoryPaths.add(repoPath);
+ } else {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Object#hashCode()
+ */
+ @Override
+ public int hashCode() {
+ return this.hc;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Object#equals(java.lang.Object)
+ */
+ @Override
+ public boolean equals( Object obj ) {
+ if (obj == this) return true;
+ if (obj instanceof Projection) {
+ Projection that = (Projection)obj;
+ if (this.hashCode() != that.hashCode()) return false;
+ if (!this.getSourceName().equals(that.getSourceName())) return false;
+ if (!this.getWorkspaceName().equals(that.getWorkspaceName())) return false;
+ if (!this.getRules().equals(that.getRules())) return false;
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Comparable#compareTo(java.lang.Object)
+ */
+ public int compareTo( Projection that ) {
+ if (this == that) return 0;
+ int diff = this.getSourceName().compareTo(that.getSourceName());
+ if (diff != 0) return diff;
+ diff = this.getWorkspaceName().compareTo(that.getWorkspaceName());
+ if (diff != 0) return diff;
+ Iterator<Rule> thisIter = this.getRules().iterator();
+ Iterator<Rule> thatIter = that.getRules().iterator();
+ while (thisIter.hasNext() && thatIter.hasNext()) {
+ diff = thisIter.next().compareTo(thatIter.next());
+ if (diff != 0) return diff;
+ }
+ if (thisIter.hasNext()) return 1;
+ if (thatIter.hasNext()) return -1;
+ return 0;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Object#toString()
+ */
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ sb.append(this.sourceName);
+ sb.append("::");
+ sb.append(this.workspaceName);
+ sb.append(" { ");
+ boolean first = true;
+ for (Rule rule : this.getRules()) {
+ if (!first) sb.append(" ; ");
+ sb.append(rule.toString());
+ first = false;
+ }
+ sb.append(" }");
+ return sb.toString();
+ }
+
+ /**
+ * A rule used within a project do define how content within a source is projected
into the federated repository. This mapping
+ * is bi-directional, meaning it's possible to determine
+ * <ul>
+ * <li>the path in repository given a path in source; and</li>
+ * <li>the path in source given a path in repository.</li>
+ * </ul>
+ *
+ * @author Randall Hauch
+ */
+ @Immutable
+ public static abstract class Rule implements Comparable<Rule> {
+
+ /**
+ * Get the paths in the repository that serve as top-level nodes exposed by this
rule.
+ *
+ * @param factory the path factory that can be used to create new paths; may not
be null
+ * @return the list of top-level paths, which are ordered and which must be
unique; never null
+ */
+ public abstract List<Path> getTopLevelPathsInRepository( PathFactory
factory );
+
+ /**
+ * Determine if the supplied path is the same as one of the top-level nodes
exposed by this rule.
+ *
+ * @param path the path; may not be null
+ * @return true if the supplied path is also one of the {@link
#getTopLevelPathsInRepository(PathFactory) top-level paths}
+ * , or false otherwise
+ */
+ public abstract boolean isTopLevelPath( Path path );
+
+ /**
+ * Get the path in source that is projected from the supplied repository path, or
null if the supplied repository path is
+ * not projected into the source.
+ *
+ * @param pathInRepository the path in the repository; may not be null
+ * @param factory the path factory; may not be null
+ * @return the path in source if it is projected by this rule, or null otherwise
+ */
+ public abstract Path getPathInSource( Path pathInRepository,
+ PathFactory factory );
+
+ /**
+ * Get the path in repository that is projected from the supplied source path, or
null if the supplied source path is not
+ * projected into the repository.
+ *
+ * @param pathInSource the path in the source; may not be null
+ * @param factory the path factory; may not be null
+ * @return the path in repository if it is projected by this rule, or null
otherwise
+ */
+ public abstract Path getPathInRepository( Path pathInSource,
+ PathFactory factory );
+
+ public abstract String getString( NamespaceRegistry registry,
+ TextEncoder encoder );
+
+ public abstract String getString( TextEncoder encoder );
+
+ public abstract String getString();
+ }
+
+ /**
+ * A rule that is defined with a single {@link #getPathInSource() path in source} and
a single {@link #getPathInRepository()
+ * path in repository}, and which has a set of {@link #getExceptionsToRule() path
exceptions} (relative paths below the path
+ * in source).
+ *
+ * @author Randall Hauch
+ */
+ @Immutable
+ public static class PathRule extends Rule {
+ /** The path of the content as known to the source */
+ private final Path sourcePath;
+ /** The path where the content is to be placed ("projected") into the
repository */
+ private final Path repositoryPath;
+ /** The paths (relative to the source path) that identify exceptions to this rule
*/
+ private final List<Path> exceptions;
+ private final int hc;
+ private final List<Path> topLevelRepositoryPaths;
+
+ public PathRule( Path repositoryPath,
+ Path sourcePath ) {
+ this(repositoryPath, sourcePath, (Path[])null);
+ }
+
+ public PathRule( Path repositoryPath,
+ Path sourcePath,
+ Path... exceptions ) {
+ assert sourcePath != null;
+ assert repositoryPath != null;
+ this.sourcePath = sourcePath;
+ this.repositoryPath = repositoryPath;
+ if (exceptions == null || exceptions.length == 0) {
+ this.exceptions = Collections.emptyList();
+ } else {
+ List<Path> exceptionList = new ArrayList<Path>();
+ for (Path exception : exceptions) {
+ if (exception != null) exceptionList.add(exception);
+ }
+ this.exceptions = Collections.unmodifiableList(exceptionList);
+ }
+ this.hc = HashCode.compute(sourcePath, repositoryPath, exceptions);
+ assert exceptionPathsAreRelative();
+ this.topLevelRepositoryPaths =
Collections.singletonList(getPathInRepository());
+ }
+
+ public PathRule( Path repositoryPath,
+ Path sourcePath,
+ List<Path> exceptions ) {
+ assert sourcePath != null;
+ assert repositoryPath != null;
+ this.sourcePath = sourcePath;
+ this.repositoryPath = repositoryPath;
+ if (exceptions == null || exceptions.isEmpty()) {
+ this.exceptions = Collections.emptyList();
+ } else {
+ this.exceptions = Collections.unmodifiableList(new
ArrayList<Path>(exceptions));
+ }
+ this.hc = HashCode.compute(sourcePath, repositoryPath, exceptions);
+ assert exceptionPathsAreRelative();
+ this.topLevelRepositoryPaths =
Collections.singletonList(getPathInRepository());
+ }
+
+ private boolean exceptionPathsAreRelative() {
+ if (this.exceptions != null) {
+ for (Path path : this.exceptions) {
+ if (path.isAbsolute()) return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * The path where the content is to be placed ("projected") into the
repository.
+ *
+ * @return the projected path of the content in the repository; never null
+ */
+ public Path getPathInRepository() {
+ return repositoryPath;
+ }
+
+ /**
+ * The path of the content as known to the source
+ *
+ * @return the source-specific path of the content; never null
+ */
+ public Path getPathInSource() {
+ return sourcePath;
+ }
+
+ /**
+ * Get whether this rule has any exceptions.
+ *
+ * @return true if this rule has exceptions, or false if it has none.
+ */
+ public boolean hasExceptionsToRule() {
+ return exceptions.size() != 0;
+ }
+
+ /**
+ * Get the paths that define the exceptions to this rule. These paths are always
relative to the
+ * {@link #getPathInSource() path in source}.
+ *
+ * @return the unmodifiable exception paths; never null but possibly empty
+ */
+ public List<Path> getExceptionsToRule() {
+ return exceptions;
+ }
+
+ /**
+ * @param pathInSource
+ * @return true if the source path is included by this rule
+ */
+ protected boolean includes( Path pathInSource ) {
+ // Check whether the path is outside the source-specific path ...
+ if (pathInSource != null &&
this.sourcePath.isAtOrAbove(pathInSource)) {
+
+ // The path is inside the source-specific region, so check the exceptions
...
+ List<Path> exceptions = getExceptionsToRule();
+ if (exceptions.size() != 0) {
+ Path subpathInSource = pathInSource.relativeTo(this.sourcePath);
+ if (subpathInSource.size() != 0) {
+ for (Path exception : exceptions) {
+ if (subpathInSource.isAtOrBelow(exception)) return false;
+ }
+ }
+ }
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see
Rule#getTopLevelPathsInRepository(org.jboss.dna.graph.property.PathFactory)
+ */
+ @Override
+ public List<Path> getTopLevelPathsInRepository( PathFactory factory ) {
+ return topLevelRepositoryPaths;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see
org.jboss.dna.graph.connector.federation.Projection.Rule#isTopLevelPath(org.jboss.dna.graph.property.Path)
+ */
+ @Override
+ public boolean isTopLevelPath( Path path ) {
+ for (Path topLevel : topLevelRepositoryPaths) {
+ if (topLevel.equals(path)) return true;
+ }
+ return false;
+ }
+
+ /**
+ * {@inheritDoc}
+ * <p>
+ * This method considers a path that is at or below the rule's {@link
#getPathInSource() source path} to be included,
+ * except if there are {@link #getExceptionsToRule() exceptions} that explicitly
disallow the path.
+ * </p>
+ *
+ * @see Rule#getPathInSource(Path, PathFactory)
+ */
+ @Override
+ public Path getPathInSource( Path pathInRepository,
+ PathFactory factory ) {
+ assert pathInRepository.equals(pathInRepository.getCanonicalPath());
+ // Project the repository path into the equivalent source path ...
+ Path pathInSource = projectPathInRepositoryToPathInSource(pathInRepository,
factory);
+
+ // Check whether the source path is included by this rule ...
+ return includes(pathInSource) ? pathInSource : null;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see Rule#getPathInRepository(org.jboss.dna.graph.property.Path,
org.jboss.dna.graph.property.PathFactory)
+ */
+ @Override
+ public Path getPathInRepository( Path pathInSource,
+ PathFactory factory ) {
+ assert pathInSource.equals(pathInSource.getCanonicalPath());
+ // Check whether the source path is included by this rule ...
+ if (!includes(pathInSource)) return null;
+
+ // Project the repository path into the equivalent source path ...
+ return projectPathInSourceToPathInRepository(pathInSource, factory);
+ }
+
+ /**
+ * Convert a path defined in the source system into an equivalent path in the
repository system.
+ *
+ * @param pathInSource the path in the source system, which may include the
{@link #getPathInSource()}
+ * @param factory the path factory; may not be null
+ * @return the path in the repository system, which will be normalized and
absolute (including the
+ * {@link #getPathInRepository()}), or null if the path is not at or
under the {@link #getPathInSource()}
+ */
+ protected Path projectPathInSourceToPathInRepository( Path pathInSource,
+ PathFactory factory ) {
+ if (!this.sourcePath.isAtOrAbove(pathInSource)) return null;
+ // Remove the leading source path ...
+ Path relativeSourcePath = pathInSource.relativeTo(this.sourcePath);
+ // Prepend the region's root path ...
+ Path result = factory.create(this.repositoryPath, relativeSourcePath);
+ return result.getNormalizedPath();
+ }
+
+ /**
+ * Convert a path defined in the repository system into an equivalent path in the
source system.
+ *
+ * @param pathInRepository the path in the repository system, which may include
the {@link #getPathInRepository()}
+ * @param factory the path factory; may not be null
+ * @return the path in the source system, which will be normalized and absolute
(including the {@link #getPathInSource()}
+ * ), or null if the path is not at or under the {@link
#getPathInRepository()}
+ */
+ protected Path projectPathInRepositoryToPathInSource( Path pathInRepository,
+ PathFactory factory ) {
+ if (!this.repositoryPath.isAtOrAbove(pathInRepository)) return null;
+ // Find the relative path from the root of this region ...
+ Path pathInRegion = pathInRepository.relativeTo(this.repositoryPath);
+ // Prepend the path in source ...
+ Path result = factory.create(this.sourcePath, pathInRegion);
+ return result.getNormalizedPath();
+ }
+
+ @Override
+ public String getString( NamespaceRegistry registry,
+ TextEncoder encoder ) {
+ StringBuilder sb = new StringBuilder();
+ sb.append(this.getPathInRepository().getString(registry, encoder));
+ sb.append(" => ");
+ sb.append(this.getPathInSource().getString(registry, encoder));
+ if (this.getExceptionsToRule().size() != 0) {
+ for (Path exception : this.getExceptionsToRule()) {
+ sb.append(" $ ");
+ sb.append(exception.getString(registry, encoder));
+ }
+ }
+ return sb.toString();
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see Rule#getString(org.jboss.dna.common.text.TextEncoder)
+ */
+ @Override
+ public String getString( TextEncoder encoder ) {
+ StringBuilder sb = new StringBuilder();
+ sb.append(this.getPathInRepository().getString(encoder));
+ sb.append(" => ");
+ sb.append(this.getPathInSource().getString(encoder));
+ if (this.getExceptionsToRule().size() != 0) {
+ for (Path exception : this.getExceptionsToRule()) {
+ sb.append(" $ ");
+ sb.append(exception.getString(encoder));
+ }
+ }
+ return sb.toString();
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see Rule#getString()
+ */
+ @Override
+ public String getString() {
+ return getString(Path.JSR283_ENCODER);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Object#hashCode()
+ */
+ @Override
+ public int hashCode() {
+ return hc;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Object#equals(java.lang.Object)
+ */
+ @Override
+ public boolean equals( Object obj ) {
+ if (obj == this) return true;
+ if (obj instanceof PathRule) {
+ PathRule that = (PathRule)obj;
+ if (!this.getPathInRepository().equals(that.getPathInRepository()))
return false;
+ if (!this.getPathInSource().equals(that.getPathInSource())) return
false;
+ if (!this.getExceptionsToRule().equals(that.getExceptionsToRule()))
return false;
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Comparable#compareTo(java.lang.Object)
+ */
+ public int compareTo( Rule other ) {
+ if (other == this) return 0;
+ if (other instanceof PathRule) {
+ PathRule that = (PathRule)other;
+ int diff =
this.getPathInRepository().compareTo(that.getPathInRepository());
+ if (diff != 0) return diff;
+ diff = this.getPathInSource().compareTo(that.getPathInSource());
+ if (diff != 0) return diff;
+ Iterator<Path> thisIter = this.getExceptionsToRule().iterator();
+ Iterator<Path> thatIter = that.getExceptionsToRule().iterator();
+ while (thisIter.hasNext() && thatIter.hasNext()) {
+ diff = thisIter.next().compareTo(thatIter.next());
+ if (diff != 0) return diff;
+ }
+ if (thisIter.hasNext()) return 1;
+ if (thatIter.hasNext()) return -1;
+ return 0;
+ }
+ return other.getClass().getName().compareTo(this.getClass().getName());
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Object#toString()
+ */
+ @Override
+ public String toString() {
+ return getString();
+ }
+ }
+}
Property changes on:
trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/Projection.java
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Added:
trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/ProjectionParser.java
===================================================================
---
trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/ProjectionParser.java
(rev 0)
+++
trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/ProjectionParser.java 2009-05-18
15:36:58 UTC (rev 911)
@@ -0,0 +1,245 @@
+/*
+ * JBoss DNA (
http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership. Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of
+ * individual contributors.
+ *
+ * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
+ * is licensed to you under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * JBoss DNA is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site:
http://www.fsf.org.
+ */
+package org.jboss.dna.graph.connector.federation;
+
+import java.lang.reflect.Method;
+import java.util.Collections;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.concurrent.CopyOnWriteArrayList;
+import net.jcip.annotations.ThreadSafe;
+import org.jboss.dna.common.text.TextEncoder;
+import org.jboss.dna.common.util.CheckArg;
+import org.jboss.dna.common.util.Logger;
+import org.jboss.dna.common.util.StringUtil;
+import org.jboss.dna.graph.ExecutionContext;
+import org.jboss.dna.graph.GraphI18n;
+import org.jboss.dna.graph.connector.federation.Projection.Rule;
+import org.jboss.dna.graph.property.NamespaceRegistry;
+
+/**
+ * A parser library for {@link Projection projections} and {@link Projection.Rule
projection rules}.
+ */
+@ThreadSafe
+public class ProjectionParser {
+ private static final ProjectionParser INSTANCE;
+
+ static {
+ INSTANCE = new ProjectionParser();
+ try {
+ INSTANCE.addRuleParser(Projection.class, "parsePathRule");
+ assert INSTANCE.parserMethods.size() == 1;
+ } catch (Throwable err) {
+ Logger.getLogger(Projection.class).error(err,
GraphI18n.errorAddingProjectionRuleParseMethod);
+ }
+ }
+
+ /**
+ * Get the shared projection parser, which is by default populated with the standard
parser rules.
+ *
+ * @return the parser; never null
+ */
+ public static ProjectionParser getInstance() {
+ return INSTANCE;
+ }
+
+ private final List<Method> parserMethods = new
CopyOnWriteArrayList<Method>();
+
+ public ProjectionParser() {
+ }
+
+ /**
+ * Add a static method that can be used to parse {@link
Rule#getString(NamespaceRegistry, TextEncoder) rule definition
+ * strings}. These methods must be static, must accept a {@link String} definition as
the first parameter and an
+ * {@link ExecutionContext} environment reference as the second parameter, and should
return the resulting {@link Rule} (or
+ * null if the definition format could not be understood by the method. Any
exceptions during
+ * {@link Method#invoke(Object, Object...) invocation} will be logged at the
+ * {@link Logger#trace(Throwable, String, Object...) trace} level.
+ *
+ * @param method the method to be added
+ * @see #addRuleParser(ClassLoader, String, String)
+ */
+ public void addRuleParser( Method method ) {
+ if (method != null) parserMethods.add(method);
+ }
+
+ /**
+ * Add a static method that can be used to parse {@link
Rule#getString(NamespaceRegistry, TextEncoder) rule definition
+ * strings}. These methods must be static, must accept a {@link String} definition as
the first parameter and an
+ * {@link ExecutionContext} environment reference as the second parameter, and should
return the resulting {@link Rule} (or
+ * null if the definition format could not be understood by the method. Any
exceptions during
+ * {@link Method#invoke(Object, Object...) invocation} will be logged at the
+ * {@link Logger#trace(Throwable, String, Object...) trace} level.
+ *
+ * @param clazz the class on which the static method is defined; may not be null
+ * @param methodName the name of the method
+ * @throws SecurityException if there is a security exception while loading the class
or getting the method
+ * @throws NoSuchMethodException if the method does not exist on the class
+ * @throws IllegalArgumentException if the class loader reference is null, or if the
class name or method name are null or
+ * empty
+ * @see #addRuleParser(Method)
+ */
+ public void addRuleParser( Class<?> clazz,
+ String methodName ) throws SecurityException,
NoSuchMethodException {
+ CheckArg.isNotNull(clazz, "clazz");
+ CheckArg.isNotEmpty(methodName, "methodName");
+ parserMethods.add(clazz.getMethod(methodName, String.class,
ExecutionContext.class));
+ }
+
+ /**
+ * Add a static method that can be used to parse {@link
Rule#getString(NamespaceRegistry, TextEncoder) rule definition
+ * strings}. These methods must be static, must accept a {@link String} definition as
the first parameter and an
+ * {@link ExecutionContext} environment reference as the second parameter, and should
return the resulting {@link Rule} (or
+ * null if the definition format could not be understood by the method. Any
exceptions during
+ * {@link Method#invoke(Object, Object...) invocation} will be logged at the
+ * {@link Logger#trace(Throwable, String, Object...) trace} level.
+ *
+ * @param classLoader the class loader that should be used to load the class on which
the method is defined; may not be null
+ * @param className the name of the class on which the static method is defined; may
not be null
+ * @param methodName the name of the method
+ * @throws SecurityException if there is a security exception while loading the class
or getting the method
+ * @throws NoSuchMethodException if the method does not exist on the class
+ * @throws ClassNotFoundException if the class could not be found given the supplied
class loader
+ * @throws IllegalArgumentException if the class loader reference is null, or if the
class name or method name are null or
+ * empty
+ * @see #addRuleParser(Method)
+ */
+ public void addRuleParser( ClassLoader classLoader,
+ String className,
+ String methodName ) throws SecurityException,
NoSuchMethodException, ClassNotFoundException {
+ CheckArg.isNotNull(classLoader, "classLoader");
+ CheckArg.isNotEmpty(className, "className");
+ CheckArg.isNotEmpty(methodName, "methodName");
+ Class<?> clazz = Class.forName(className, true, classLoader);
+ parserMethods.add(clazz.getMethod(methodName, String.class,
ExecutionContext.class));
+ }
+
+ /**
+ * Remove the rule parser method.
+ *
+ * @param method the method to remove
+ * @return true if the method was removed, or false if the method was not a
registered rule parser method
+ */
+ public boolean removeRuleParser( Method method ) {
+ return parserMethods.remove(method);
+ }
+
+ /**
+ * Remove the rule parser method.
+ *
+ * @param declaringClassName the name of the class on which the static method is
defined; may not be null
+ * @param methodName the name of the method
+ * @return true if the method was removed, or false if the method was not a
registered rule parser method
+ * @throws IllegalArgumentException if the class loader reference is null, or if the
class name or method name are null or
+ * empty
+ */
+ public boolean removeRuleParser( String declaringClassName,
+ String methodName ) {
+ CheckArg.isNotEmpty(declaringClassName, "declaringClassName");
+ CheckArg.isNotEmpty(methodName, "methodName");
+ for (Method method : parserMethods) {
+ if (method.getName().equals(methodName) &&
method.getDeclaringClass().getName().equals(declaringClassName)) {
+ return parserMethods.remove(method);
+ }
+ }
+ return false;
+ }
+
+ /**
+ * @return parserMethods
+ */
+ /*package*/List<Method> getParserMethods() {
+ return Collections.unmodifiableList(parserMethods);
+ }
+
+ /**
+ * Parse the string form of a rule definition and return the rule
+ *
+ * @param definition the definition of the rule that is to be parsed
+ * @param context the environment in which this method is being executed; may not be
null
+ * @return the rule, or null if the definition could not be parsed
+ */
+ public Rule ruleFromString( String definition,
+ ExecutionContext context ) {
+ CheckArg.isNotNull(context, "env");
+ definition = definition != null ? definition.trim() : "";
+ if (definition.length() == 0) return null;
+ Logger logger = context.getLogger(getClass());
+ for (Method method : parserMethods) {
+ try {
+ Rule rule = (Rule)method.invoke(null, definition, context);
+ if (rule != null) {
+ if (logger.isTraceEnabled()) {
+ String msg = "Success parsing project rule definition
\"{0}\" using {1}";
+ logger.trace(msg, definition, method);
+ }
+ return rule;
+ } else if (logger.isTraceEnabled()) {
+ String msg = "Unable to parse project rule definition
\"{0}\" using {1}";
+ logger.trace(msg, definition, method);
+ }
+ } catch (Throwable err) {
+ String msg = "Error while parsing project rule definition
\"{0}\" using {1}";
+ logger.trace(err, msg, definition, method);
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Parse string forms of an arry of rule definitions and return the rules
+ *
+ * @param context the environment in which this method is being executed; may not be
null
+ * @param definitions the definition of the rules that are to be parsed
+ * @return the rule, or null if the definition could not be parsed
+ */
+ public Rule[] rulesFromStrings( ExecutionContext context,
+ String... definitions ) {
+ List<Rule> rules = new LinkedList<Rule>();
+ for (String definition : definitions) {
+ Rule rule = ruleFromString(definition, context);
+ if (rule != null) rules.add(rule);
+ }
+ return rules.toArray(new Rule[rules.size()]);
+ }
+
+ /**
+ * Parse a single string containing one or more string forms of rule definitions, and
return the rules. The string contains
+ * each rule on a separate line.
+ *
+ * @param context the environment in which this method is being executed; may not be
null
+ * @param definitions the definitions of the rules that are to be parsed, each
definition separated by a newline character.
+ * @return the rule, or null if the definition could not be parsed
+ */
+ public Rule[] rulesFromString( ExecutionContext context,
+ String definitions ) {
+ List<String> lines = StringUtil.splitLines(definitions);
+ List<Rule> rules = new LinkedList<Rule>();
+ for (String definition : lines) {
+ Rule rule = ruleFromString(definition, context);
+ if (rule != null) rules.add(rule);
+ }
+ return rules.toArray(new Rule[rules.size()]);
+ }
+}
Property changes on:
trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/ProjectionParser.java
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Added:
trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/Projector.java
===================================================================
--- trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/Projector.java
(rev 0)
+++
trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/Projector.java 2009-05-18
15:36:58 UTC (rev 911)
@@ -0,0 +1,50 @@
+/*
+ * JBoss DNA (
http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership. Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of
+ * individual contributors.
+ *
+ * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
+ * is licensed to you under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * JBoss DNA is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site:
http://www.fsf.org.
+ */
+package org.jboss.dna.graph.connector.federation;
+
+import net.jcip.annotations.Immutable;
+import org.jboss.dna.graph.ExecutionContext;
+import org.jboss.dna.graph.Location;
+
+/**
+ * Interface for the components that compute the {@link ProjectedNode} for a given
location. Implementations may be provided for
+ * different configurations of {@link FederatedWorkspace#getProjections() projections}.
+ */
+@Immutable
+interface Projector {
+
+ /**
+ * Project the supplied location in the federated repository into the equivalent
projected node(s).
+ *
+ * @param context the execution context in which the content is being accessed; may
not be null
+ * @param location the location in the federated repository; may not be null
+ * @param requiresUpdate true if the operation for which this projection is needed
will update the content in some way, or
+ * false if read-only operations will be performed
+ * @return the projected node, or null if the node does not exist in any projection
or if the operation requires update and no
+ * writable projection applies
+ */
+ ProjectedNode project( ExecutionContext context,
+ Location location,
+ boolean requiresUpdate );
+}
Property changes on:
trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/Projector.java
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Added:
trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/ProjectorWithPlaceholders.java
===================================================================
---
trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/ProjectorWithPlaceholders.java
(rev 0)
+++
trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/ProjectorWithPlaceholders.java 2009-05-18
15:36:58 UTC (rev 911)
@@ -0,0 +1,126 @@
+/*
+ * JBoss DNA (
http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership. Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of
+ * individual contributors.
+ *
+ * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
+ * is licensed to you under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * JBoss DNA is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site:
http://www.fsf.org.
+ */
+package org.jboss.dna.graph.connector.federation;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.UUID;
+import net.jcip.annotations.Immutable;
+import org.jboss.dna.graph.ExecutionContext;
+import org.jboss.dna.graph.Location;
+import org.jboss.dna.graph.property.Name;
+import org.jboss.dna.graph.property.Path;
+import org.jboss.dna.graph.property.PathFactory;
+import org.jboss.dna.graph.property.Property;
+import com.google.common.collect.Multimaps;
+import com.google.common.collect.TreeMultimap;
+
+/**
+ * A Projector for federated repository configurations that are an offset, direct
one-for-one mirror against a single source
+ * repository that is projected below the federated root. In other words, the federated
repository has a single projection with a
+ * single "/something/below/root => /" rule.
+ */
+@Immutable
+abstract class ProjectorWithPlaceholders implements Projector {
+
+ private Map<Path, PlaceholderNode> placeholderNodesByPath;
+ private Map<UUID, PlaceholderNode> placeholderNodesByUuid;
+
+ ProjectorWithPlaceholders( ExecutionContext context,
+ List<Projection> projections ) {
+ placeholderNodesByPath = new HashMap<Path, PlaceholderNode>();
+ placeholderNodesByUuid = new HashMap<UUID, PlaceholderNode>();
+ // Use the execution context of the source to load the projected nodes ...
+ Collection<PlaceholderNode> placeholders = new
ArrayList<PlaceholderNode>();
+ loadPlaceholderNodes(context, projections, placeholders);
+ for (PlaceholderNode placeholder : placeholders) {
+ placeholderNodesByPath.put(placeholder.location().getPath(), placeholder);
+ placeholderNodesByUuid.put(placeholder.location().getUuid(), placeholder);
+ }
+ }
+
+ /**
+ * Determine whether the specified location is a placeholder node.
+ *
+ * @param location the location of the node; may not be null
+ * @return the placeholder node, or null if the supplied location does not designate
a placeholder node
+ */
+ public PlaceholderNode isPlaceholder( Location location ) {
+ Path path = location.getPath();
+ if (path != null) {
+ return placeholderNodesByPath.get(path);
+ }
+ UUID uuid = location.getUuid();
+ if (uuid != null) {
+ return placeholderNodesByUuid.get(uuid);
+ }
+ return null;
+ }
+
+ /**
+ * Load the placeholder nodes for this repository. This method does not modify any
state of this instance, but instead
+ * populates the supplied map.
+ * <p>
+ * A placeholder is created for each node above the projections' {@link
Projection#getTopLevelPathsInRepository(PathFactory)
+ * top-level paths} in the federated repository. Thus, the projected node immediately
above a top-level path will contain the
+ * top-level path as a child.
+ * </p>
+ *
+ * @param context the context in which the placeholder nodes should be materialized;
may not be null
+ * @param projections the projections; may not be null
+ * @param placeholderNodes the collection into which should be placed the
materialized placeholders
+ */
+ protected static void loadPlaceholderNodes( ExecutionContext context,
+ Iterable<Projection> projections,
+ Collection<PlaceholderNode>
placeholderNodes ) {
+ final PathFactory pathFactory = context.getValueFactories().getPathFactory();
+ TreeMultimap<Path, Location> childrenForParent =
Multimaps.newTreeMultimap();
+ for (Projection projection : projections) {
+ // Collect the paths to all of the top-level nodes under each of their
parents ...
+ for (Path path : projection.getTopLevelPathsInRepository(pathFactory)) {
+ if (path.isRoot()) continue;
+ while (!path.isRoot()) {
+ // Create a projected node for the parent of this path ...
+ Path parent = path.getParent();
+ childrenForParent.put(parent, Location.create(path));
+ path = parent;
+ }
+ }
+ }
+ // Now, we want to create a placeholder for each parent, with the list of all
children ...
+ for (Path parentPath : childrenForParent.keySet()) {
+ // Get the children for this parent ...
+ List<Location> children = new
ArrayList<Location>(childrenForParent.get(parentPath));
+ Map<Name, Property> properties = Collections.emptyMap();
+ Location location = Location.create(parentPath, UUID.randomUUID());
+ PlaceholderNode placeholder = new PlaceholderNode(location, properties,
children);
+ placeholderNodes.add(placeholder);
+ }
+ }
+
+}
Property changes on:
trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/ProjectorWithPlaceholders.java
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Added:
trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/package-info.java
===================================================================
---
trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/package-info.java
(rev 0)
+++
trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/package-info.java 2009-05-18
15:36:58 UTC (rev 911)
@@ -0,0 +1,306 @@
+/*
+ * JBoss DNA (
http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership. Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of
+ * individual contributors.
+ *
+ * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
+ * is licensed to you under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * JBoss DNA is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site:
http://www.fsf.org.
+ */
+/**
+ * JBoss DNA provides a federated connector that is able to access repository content
from multiple external systems
+ * and make that content look like it exists in a single unified repository. Like other
connectors, a
+ * {@link org.jboss.dna.graph.connector.RepositorySource} implementation is provided,
called {@link FederatedRepositorySource},
+ * that can be set up and configured to be used by a {@link org.jboss.dna.graph.Graph} or
a JCR repository.
+ *
+ * <h3>Projections</h3>
+ * <p>
+ * Each federated repository source provides a unified repository consisting of
information that is dynamically federated
+ * from multiple other RepositorySource instances. The connector is configured with a
number of <strong>projections</strong>
+ * that each describe where in the unified repository the federated connector should
place the content from another source.
+ * Projections consist of the name of the source containing the content and a number of
<strong>rules</strong> that
+ * define the path mappings, where each rule is defined as a string with this format:
+ * </p>
+ * <pre>
+ * pathInFederatedRepository => pathInSourceRepository
+ * </pre>
+ * <p>
+ * Here, the <code>pathInFederatedRepository</code> is the string
representation of the path in the unified
+ * (or federated) repository, and <code>pathInSourceRepository</code> is the
string representation of the path of the
+ * actual content in the underlying source. For example:
+ * </p>
+ * <pre>
+ * / => /
+ * </pre>
+ * <p>
+ * is a trivial rule that states that all of the content in the underlying source should
be mapped into the unified
+ * repository such that the locations are the same. Therefore, a node at
<code>/a/b/c</code> in the source would
+ * appear in the unified repository at <code>/a/b/c</code>. This is called a
<strong>mirror projection</strong>,
+ * since the unified repository mirrors the underlying source repository.
+ * </p>
+ * <p>
+ * Another example is an <strong>offset projection</strong>, which is similar
to the mirror projection except that
+ * the federated path includes an offset not found in the source:
+ * </p>
+ * <pre>
+ * /alpha/beta => /
+ * </pre>
+ * <p>
+ * Here, a node at <code>/a/b/c</code> in the source would actually appear in
the unified repository at
+ * <code>/alpha/beta/a/b/c</code>. The offset path
(<code>/alpha/beta</code> in this example) can have 1 or more segments.
+ * (If there are no segments, then it reduces to a mirror projection.)
+ * </p>
+ * <p>
+ * Often a rule will map a path in one source into another path in the unified source:
+ * </p>
+ * <pre>
+ * /alpha/beta => /foo/bar
+ * </pre>
+ * <p>
+ * Here, the content at <code>/foo/bar</code> is projected in the unified
repository under <code>/alpha/beta</code>,
+ * meaning that the <code>/foo/bar</code> prefix never even appears in the
unified repository. So the node at
+ * <code>/foo/bar/baz/raz</code> would appear in the unified repository at
<code>/alpha/beta/baz/raz</code>. Again,
+ * the size of the two paths in the rule don't matter.
+ * </p>
+ * <h3>Multiple Projections</h3>
+ * <p>
+ * Federated repositories that use a single projection are useful, but they aren't as
interesting or powerful as
+ * those that use multiple projections. Consider a federated repository that is defined
by two projections:
+ * </p>
+ * <pre>
+ * / => / for source "S1"
+ * /alpha => /foo/bar for source "S2"
+ * </pre>
+ * <p>
+ * And consider that S1 contains the following structure:
+ * </p>
+ * <pre>
+ * +- a
+ * | +- i
+ * | +- j
+ * +- b
+ * +- k
+ * +- m
+ * +- n
+ * </pre>
+ * and S2 contains the following:
+ * <pre>
+ * +- foo
+ * +- bar
+ * | +- baz
+ * | | +- taz
+ * | | +- zaz
+ * | +- raz
+ * +- bum
+ * +- bot
+ * </pre>
+ * <p>
+ * The unified repository would then have this structure:
+ * </p>
+ * <pre>
+ * +- a
+ * | +- i
+ * | +- j
+ * +- b
+ * | +- k
+ * | +- m
+ * | +- n
+ * +- alpha
+ * +- baz
+ * +- taz
+ * | +- zaz
+ * +- raz
+ * </pre>
+ * <p>
+ * Note how the <code>/foo/bum</code> branch does not even appear in the
unified repository, since it is outside of the
+ * branch being projected. Also, the <code>/alpha</code> node doesn't
exist in S1 or S2; it's what is called a
+ * <strong>placeholder</strong> node that exists purely so that the nodes
below it have a place to exist.
+ * Placeholders are somewhat special: they allow any structure below them (including
other placeholder nodes or real
+ * projected nodes), but they cannot be modified.
+ * </p>
+ * <p>
+ * Even more interesting are cases that involve more projections. Consider a federated
repository that contains
+ * information about different kinds of automobiles, aircraft, and spacecraft, except
that the information
+ * about each kind of vehicle exists in a different source (and possibly a different
<i>kind</i> of source, such as
+ * a database, or file, or web service).
+ * <p>
+ * First, the sources. The "Cars" source contains the following structure:
+ * </p>
+ * <pre>
+ * +- Cars
+ * +- Hybrid
+ * | +- Toyota Prius
+ * | +- Toyota Highlander
+ * | +- Nissan Altima
+ * +- Sports
+ * | +- Aston Martin DB9
+ * | +- Infinity G37
+ * +- Luxury
+ * | +- Cadillac DTS
+ * | +- Bentley Continental
+ * | +- Lexus IS350
+ * +- Utility
+ * +- Land Rover LR2
+ * +- Land Rover LR3
+ * +- Hummer H3
+ * +- Ford F-150
+ * </pre>
+ * <p>
+ * The "Aircraft" source contains the following structure:
+ * </p>
+ * <pre>
+ * +- Aviation
+ * +- Business
+ * | +- Gulfstream V
+ * | +- Learjet 45
+ * +- Commercial
+ * | +- Boeing 777
+ * | +- Boeing 767
+ * | +- Boeing 787
+ * | +- Boeing 757
+ * | +- Airbus A380
+ * | +- Airbus A340
+ * | +- Airbus A310
+ * | +- Embraer RJ-175
+ * +- Vintage
+ * | +- Fokker Trimotor
+ * | +- P-38 Lightning
+ * | +- A6M Zero
+ * | +- Bf 109
+ * | +- Wright Flyer
+ * +- Homebuilt
+ * +- Long-EZ
+ * +- Cirrus VK-30
+ * +- Van's RV-4
+ * </pre>
+ * <p>
+ * Finally, our "Spacecraft" source contains the following structure:
+ * </p>
+ * <pre>
+ * +- Space Vehicles
+ * +- Manned
+ * | +- Space Shuttle
+ * | +- Soyuz
+ * | +- Skylab
+ * | +- ISS
+ * +- Unmanned
+ * | +- Sputnik
+ * | +- Explorer
+ * | +- Vanguard
+ * | +- Pioneer
+ * | +- Marsnik
+ * | +- Mariner
+ * | +- Mars Pathfinder
+ * | +- Mars Observer
+ * | +- Mars Polar Lander
+ * +- Launch Vehicles
+ * | +- Saturn V
+ * | +- Aries
+ * | +- Delta
+ * | +- Delta II
+ * | +- Orion
+ * +- X-Prize
+ * +- SpaceShipOne
+ * +- WildFire
+ * +- Spirit of Liberty
+ * </pre>
+ * <p>
+ * So, we can define our unified "Vehicles" source with the following
projections:
+ * </p>
+ * <pre>
+ * /Vehicles => / for source "Cars"
+ * /Vehicles/Aircraft => /Aviation for source
"Aircraft"
+ * /Vehicles/Spacecraft => /Space Vehicles for source "Cars"
+ * </pre>
+ * <p>
+ * The result is a unified repository with the following structure:
+ * </p>
+ * <pre>
+ * +- Vehicles
+ * +- Cars
+ * | +- Hybrid
+ * | | +- Toyota Prius
+ * | | +- Toyota Highlander
+ * | | +- Nissan Altima
+ * | +- Sports
+ * | | +- Aston Martin DB9
+ * | | +- Infinity G37
+ * | +- Luxury
+ * | | +- Cadillac DTS
+ * | | +- Bentley Continental
+ * | +- Lexus IS350
+ * | +- Utility
+ * | +- Land Rover LR2
+ * | +- Land Rover LR3
+ * | +- Hummer H3
+ * | +- Ford F-150
+ * +- Aircraft
+ * | +- Business
+ * | | +- Gulfstream V
+ * | | +- Learjet 45
+ * | +- Commercial
+ * | | +- Boeing 777
+ * | | +- Boeing 767
+ * | | +- Boeing 787
+ * | | +- Boeing 757
+ * | | +- Airbus A380
+ * | | +- Airbus A340
+ * | | +- Airbus A310
+ * | | +- Embraer RJ-175
+ * | +- Vintage
+ * | | +- Fokker Trimotor
+ * | | +- P-38 Lightning
+ * | | +- A6M Zero
+ * | | +- Bf 109
+ * | | +- Wright Flyer
+ * | +- Homebuilt
+ * | +- Long-EZ
+ * | +- Cirrus VK-30
+ * | +- Van's RV-4
+ * +- Spacecraft
+ * +- Manned
+ * | +- Space Shuttle
+ * | +- Soyuz
+ * | +- Skylab
+ * | +- ISS
+ * +- Unmanned
+ * | +- Sputnik
+ * | +- Explorer
+ * | +- Vanguard
+ * | +- Pioneer
+ * | +- Marsnik
+ * | +- Mariner
+ * | +- Mars Pathfinder
+ * | +- Mars Observer
+ * | +- Mars Polar Lander
+ * +- Launch Vehicles
+ * | +- Saturn V
+ * | +- Aries
+ * | +- Delta
+ * | +- Delta II
+ * | +- Orion
+ * +- X-Prize
+ * +- SpaceShipOne
+ * +- WildFire
+ * +- Spirit of Liberty
+ * </pre>
+ * <p>
+ * Other combinations are of course possible.
+ * </p>
+ */
+package org.jboss.dna.graph.connector.federation;
+
Property changes on:
trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/federation/package-info.java
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Modified:
trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/inmemory/InMemoryRequestProcessor.java
===================================================================
---
trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/inmemory/InMemoryRequestProcessor.java 2009-05-15
19:28:48 UTC (rev 910)
+++
trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/inmemory/InMemoryRequestProcessor.java 2009-05-18
15:36:58 UTC (rev 911)
@@ -157,7 +157,27 @@
break;
}
}
- node = workspace.createNode(getExecutionContext(), parentNode, request.named(),
uuid);
+ switch (request.conflictBehavior()) {
+ case APPEND:
+ case DO_NOT_REPLACE:
+ node = workspace.createNode(getExecutionContext(), parentNode,
request.named(), uuid);
+ break;
+ case REPLACE:
+ // See if the node already exists (this doesn't record an error on
the request) ...
+ node = getTargetNode(workspace, null,
Location.create(pathFactory.create(parent, request.named()), uuid));
+ if (node != null) {
+ workspace.removeNode(getExecutionContext(), node);
+ }
+ node = workspace.createNode(getExecutionContext(), parentNode,
request.named(), uuid);
+ break;
+ case UPDATE:
+ // See if the node already exists (this doesn't record an error on
the request) ...
+ node = getTargetNode(workspace, null,
Location.create(pathFactory.create(parent, request.named()), uuid));
+ if (node == null) {
+ node = workspace.createNode(getExecutionContext(), parentNode,
request.named(), uuid);
+ } // otherwise, we found it and we're setting any properties below
+ break;
+ }
assert node != null;
Path path =
getExecutionContext().getValueFactories().getPathFactory().create(parent,
node.getName());
// Now add the properties to the supplied node ...
@@ -381,7 +401,7 @@
node = workspace.getNode(path);
}
}
- if (node == null) {
+ if (node == null && request != null) {
if (path == null) {
if (uuid == null) {
// Missing both path and UUID ...
Modified:
trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/CloneWorkspaceRequest.java
===================================================================
---
trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/CloneWorkspaceRequest.java 2009-05-15
19:28:48 UTC (rev 910)
+++
trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/CloneWorkspaceRequest.java 2009-05-18
15:36:58 UTC (rev 911)
@@ -24,6 +24,7 @@
package org.jboss.dna.graph.request;
import org.jboss.dna.common.util.CheckArg;
+import org.jboss.dna.common.util.HashCode;
import org.jboss.dna.graph.Location;
import org.jboss.dna.graph.request.CreateWorkspaceRequest.CreateConflictBehavior;
@@ -178,11 +179,23 @@
/**
* {@inheritDoc}
*
+ * @see org.jboss.dna.graph.request.Request#cancel()
+ */
+ @Override
+ public void cancel() {
+ super.cancel();
+ this.actualLocationOfRoot = null;
+ this.actualWorkspaceName = null;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
* @see java.lang.Object#hashCode()
*/
@Override
public int hashCode() {
- return desiredNameOfTargetWorkspace.hashCode();
+ return HashCode.compute(nameOfWorkspaceToBeCloned,
desiredNameOfTargetWorkspace);
}
/**
Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/CompositeRequest.java
===================================================================
---
trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/CompositeRequest.java 2009-05-15
19:28:48 UTC (rev 910)
+++
trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/CompositeRequest.java 2009-05-18
15:36:58 UTC (rev 911)
@@ -46,6 +46,8 @@
private static final long serialVersionUID = 1L;
+ public static final int UNKNOWN_NUMBER_OF_REQUESTS = Integer.MAX_VALUE;
+
/**
* Return a request that either wraps multiple requests, or the single request if
only one is supplied.
*
@@ -195,8 +197,8 @@
* @param requests the modifiable list of requests; may not be null
* @param readOnly true if all of the requests are {@link Request#isReadOnly()
read-only}
*/
- /*package*/CompositeRequest( List<? extends Request> requests,
- boolean readOnly ) {
+ protected CompositeRequest( List<? extends Request> requests,
+ boolean readOnly ) {
// Iterate through the requests and set the cancelled flag of each request to
this object's flag ...
final AtomicBoolean flag = super.getCancelledFlag();
for (Request request : requests) {
@@ -207,6 +209,16 @@
}
/**
+ * Create a composite request from the supplied list of requests. This is useful only
for subclasses.
+ *
+ * @param readOnly true if all of the requests are {@link Request#isReadOnly()
read-only}
+ */
+ protected CompositeRequest( boolean readOnly ) {
+ this.requests = Collections.emptyList();
+ this.readOnly = false;
+ }
+
+ /**
* Return the unmodifiable requests contained in this composite request.
*
* @return requests
Modified:
trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/CopyBranchRequest.java
===================================================================
---
trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/CopyBranchRequest.java 2009-05-15
19:28:48 UTC (rev 910)
+++
trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/CopyBranchRequest.java 2009-05-18
15:36:58 UTC (rev 911)
@@ -24,6 +24,7 @@
package org.jboss.dna.graph.request;
import org.jboss.dna.common.util.CheckArg;
+import org.jboss.dna.common.util.HashCode;
import org.jboss.dna.graph.GraphI18n;
import org.jboss.dna.graph.Location;
import org.jboss.dna.graph.NodeConflictBehavior;
@@ -267,6 +268,28 @@
/**
* {@inheritDoc}
*
+ * @see java.lang.Object#hashCode()
+ */
+ @Override
+ public int hashCode() {
+ return HashCode.compute(from, fromWorkspace, into, intoWorkspace);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.request.Request#cancel()
+ */
+ @Override
+ public void cancel() {
+ super.cancel();
+ this.actualFromLocation = null;
+ this.actualIntoLocation = null;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
Modified:
trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/CreateNodeRequest.java
===================================================================
---
trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/CreateNodeRequest.java 2009-05-15
19:28:48 UTC (rev 910)
+++
trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/CreateNodeRequest.java 2009-05-18
15:36:58 UTC (rev 911)
@@ -30,6 +30,7 @@
import java.util.LinkedList;
import java.util.List;
import org.jboss.dna.common.util.CheckArg;
+import org.jboss.dna.common.util.HashCode;
import org.jboss.dna.graph.GraphI18n;
import org.jboss.dna.graph.Location;
import org.jboss.dna.graph.NodeConflictBehavior;
@@ -325,6 +326,27 @@
/**
* {@inheritDoc}
*
+ * @see org.jboss.dna.graph.request.Request#cancel()
+ */
+ @Override
+ public void cancel() {
+ super.cancel();
+ this.actualLocation = null;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Object#hashCode()
+ */
+ @Override
+ public int hashCode() {
+ return HashCode.compute(under, childName, workspaceName);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
Modified:
trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/CreateWorkspaceRequest.java
===================================================================
---
trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/CreateWorkspaceRequest.java 2009-05-15
19:28:48 UTC (rev 910)
+++
trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/CreateWorkspaceRequest.java 2009-05-18
15:36:58 UTC (rev 911)
@@ -141,6 +141,18 @@
/**
* {@inheritDoc}
*
+ * @see org.jboss.dna.graph.request.Request#cancel()
+ */
+ @Override
+ public void cancel() {
+ super.cancel();
+ this.actualWorkspaceName = null;
+ this.actualLocationOfRoot = null;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
* @see java.lang.Object#hashCode()
*/
@Override
Modified:
trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/DeleteBranchRequest.java
===================================================================
---
trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/DeleteBranchRequest.java 2009-05-15
19:28:48 UTC (rev 910)
+++
trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/DeleteBranchRequest.java 2009-05-18
15:36:58 UTC (rev 911)
@@ -24,6 +24,7 @@
package org.jboss.dna.graph.request;
import org.jboss.dna.common.util.CheckArg;
+import org.jboss.dna.common.util.HashCode;
import org.jboss.dna.graph.GraphI18n;
import org.jboss.dna.graph.Location;
import org.jboss.dna.graph.property.Path;
@@ -134,6 +135,27 @@
/**
* {@inheritDoc}
*
+ * @see org.jboss.dna.graph.request.Request#cancel()
+ */
+ @Override
+ public void cancel() {
+ super.cancel();
+ this.actualLocation = null;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Object#hashCode()
+ */
+ @Override
+ public int hashCode() {
+ return HashCode.compute(at, workspaceName);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
Modified:
trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/GetWorkspacesRequest.java
===================================================================
---
trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/GetWorkspacesRequest.java 2009-05-15
19:28:48 UTC (rev 910)
+++
trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/GetWorkspacesRequest.java 2009-05-18
15:36:58 UTC (rev 911)
@@ -71,6 +71,17 @@
/**
* {@inheritDoc}
*
+ * @see org.jboss.dna.graph.request.Request#cancel()
+ */
+ @Override
+ public void cancel() {
+ super.cancel();
+ this.availableWorkspaceNames = null;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
* @see java.lang.Object#hashCode()
*/
@Override
Modified:
trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/MoveBranchRequest.java
===================================================================
---
trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/MoveBranchRequest.java 2009-05-15
19:28:48 UTC (rev 910)
+++
trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/MoveBranchRequest.java 2009-05-18
15:36:58 UTC (rev 911)
@@ -24,6 +24,7 @@
package org.jboss.dna.graph.request;
import org.jboss.dna.common.util.CheckArg;
+import org.jboss.dna.common.util.HashCode;
import org.jboss.dna.graph.GraphI18n;
import org.jboss.dna.graph.Location;
import org.jboss.dna.graph.NodeConflictBehavior;
@@ -282,6 +283,28 @@
/**
* {@inheritDoc}
*
+ * @see org.jboss.dna.graph.request.Request#cancel()
+ */
+ @Override
+ public void cancel() {
+ super.cancel();
+ this.actualOldLocation = null;
+ this.actualNewLocation = null;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Object#hashCode()
+ */
+ @Override
+ public int hashCode() {
+ return HashCode.compute(from, workspaceName, into);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
Modified:
trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/ReadAllChildrenRequest.java
===================================================================
---
trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/ReadAllChildrenRequest.java 2009-05-15
19:28:48 UTC (rev 910)
+++
trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/ReadAllChildrenRequest.java 2009-05-18
15:36:58 UTC (rev 911)
@@ -27,6 +27,7 @@
import java.util.LinkedList;
import java.util.List;
import org.jboss.dna.common.util.CheckArg;
+import org.jboss.dna.common.util.HashCode;
import org.jboss.dna.graph.GraphI18n;
import org.jboss.dna.graph.Location;
import org.jboss.dna.graph.connector.RepositoryConnection;
@@ -110,6 +111,23 @@
}
/**
+ * Add to the list of children that has been read the supplied children with the
given path and identification properties. The
+ * children are added in order.
+ *
+ * @param children the locations of the children that were read
+ * @throws IllegalArgumentException if the parameter is null
+ * @see #addChild(Location)
+ * @see #addChild(Path, Property)
+ * @see #addChild(Path, Property, Property...)
+ */
+ public void addChildren( Iterable<Location> children ) {
+ CheckArg.isNotNull(children, "children");
+ for (Location child : children) {
+ if (child != null) this.children.add(child);
+ }
+ }
+
+ /**
* Add to the list of children that has been read the child with the given path and
identification properties. The children
* should be added in order.
*
@@ -190,6 +208,27 @@
/**
* {@inheritDoc}
*
+ * @see org.jboss.dna.graph.request.Request#cancel()
+ */
+ @Override
+ public void cancel() {
+ super.cancel();
+ this.actualOf = null;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Object#hashCode()
+ */
+ @Override
+ public int hashCode() {
+ return HashCode.compute(of, workspaceName);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
Modified:
trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/ReadAllPropertiesRequest.java
===================================================================
---
trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/ReadAllPropertiesRequest.java 2009-05-15
19:28:48 UTC (rev 910)
+++
trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/ReadAllPropertiesRequest.java 2009-05-18
15:36:58 UTC (rev 911)
@@ -28,6 +28,7 @@
import java.util.Iterator;
import java.util.Map;
import org.jboss.dna.common.util.CheckArg;
+import org.jboss.dna.common.util.HashCode;
import org.jboss.dna.graph.GraphI18n;
import org.jboss.dna.graph.Location;
import org.jboss.dna.graph.connector.RepositoryConnection;
@@ -146,6 +147,18 @@
}
/**
+ * Add a property that was read from the {@link RepositoryConnection}
+ *
+ * @param properties the properties that were read
+ * @throws IllegalArgumentException if the property is null
+ */
+ public void addProperties( Iterable<Property> properties ) {
+ for (Property property : properties) {
+ this.properties.put(property.getName(), property);
+ }
+ }
+
+ /**
* Get the number of children for this node.
*
* @return the number of children, or {@link #UNKNOWN_NUMBER_OF_CHILDREN} if the
number of children was not yet read
@@ -196,6 +209,27 @@
/**
* {@inheritDoc}
*
+ * @see org.jboss.dna.graph.request.Request#cancel()
+ */
+ @Override
+ public void cancel() {
+ super.cancel();
+ this.actualLocation = null;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Object#hashCode()
+ */
+ @Override
+ public int hashCode() {
+ return HashCode.compute(at, workspaceName);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
Modified:
trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/ReadBlockOfChildrenRequest.java
===================================================================
---
trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/ReadBlockOfChildrenRequest.java 2009-05-15
19:28:48 UTC (rev 910)
+++
trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/ReadBlockOfChildrenRequest.java 2009-05-18
15:36:58 UTC (rev 911)
@@ -27,6 +27,7 @@
import java.util.List;
import org.jboss.dna.common.text.Inflector;
import org.jboss.dna.common.util.CheckArg;
+import org.jboss.dna.common.util.HashCode;
import org.jboss.dna.graph.GraphI18n;
import org.jboss.dna.graph.Location;
import org.jboss.dna.graph.connector.RepositoryConnection;
@@ -157,6 +158,23 @@
}
/**
+ * Add to the list of children that has been read the supplied children with the
given path and identification properties. The
+ * children are added in order.
+ *
+ * @param children the locations of the children that were read
+ * @throws IllegalArgumentException if the parameter is null
+ * @see #addChild(Location)
+ * @see #addChild(Path, Property)
+ * @see #addChild(Path, Property, Property...)
+ */
+ public void addChildren( Iterable<Location> children ) {
+ CheckArg.isNotNull(children, "children");
+ for (Location child : children) {
+ if (child != null) this.children.add(child);
+ }
+ }
+
+ /**
* Add to the list of children that has been read the child with the given path and
identification properties. The children
* should be added in order.
*
@@ -235,6 +253,28 @@
/**
* {@inheritDoc}
*
+ * @see org.jboss.dna.graph.request.Request#cancel()
+ */
+ @Override
+ public void cancel() {
+ super.cancel();
+ this.actualLocation = null;
+ this.children.clear();
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Object#hashCode()
+ */
+ @Override
+ public int hashCode() {
+ return HashCode.compute(of, workspaceName);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
Modified:
trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/ReadBranchRequest.java
===================================================================
---
trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/ReadBranchRequest.java 2009-05-15
19:28:48 UTC (rev 910)
+++
trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/ReadBranchRequest.java 2009-05-18
15:36:58 UTC (rev 911)
@@ -32,6 +32,7 @@
import java.util.NoSuchElementException;
import net.jcip.annotations.NotThreadSafe;
import org.jboss.dna.common.util.CheckArg;
+import org.jboss.dna.common.util.HashCode;
import org.jboss.dna.graph.GraphI18n;
import org.jboss.dna.graph.Location;
import org.jboss.dna.graph.connector.RepositoryConnection;
@@ -382,6 +383,28 @@
/**
* {@inheritDoc}
*
+ * @see org.jboss.dna.graph.request.Request#cancel()
+ */
+ @Override
+ public void cancel() {
+ super.cancel();
+ this.actualLocation = null;
+ this.nodes.clear();
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Object#hashCode()
+ */
+ @Override
+ public int hashCode() {
+ return HashCode.compute(at, workspaceName);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
Modified:
trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/ReadNextBlockOfChildrenRequest.java
===================================================================
---
trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/ReadNextBlockOfChildrenRequest.java 2009-05-15
19:28:48 UTC (rev 910)
+++
trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/ReadNextBlockOfChildrenRequest.java 2009-05-18
15:36:58 UTC (rev 911)
@@ -26,6 +26,7 @@
import java.util.LinkedList;
import java.util.List;
import org.jboss.dna.common.util.CheckArg;
+import org.jboss.dna.common.util.HashCode;
import org.jboss.dna.graph.GraphI18n;
import org.jboss.dna.graph.Location;
import org.jboss.dna.graph.connector.RepositoryConnection;
@@ -125,6 +126,23 @@
}
/**
+ * Add to the list of children that has been read the supplied children with the
given path and identification properties. The
+ * children are added in order.
+ *
+ * @param children the locations of the children that were read
+ * @throws IllegalArgumentException if the parameter is null
+ * @see #addChild(Location)
+ * @see #addChild(Path, Property)
+ * @see #addChild(Path, Property, Property...)
+ */
+ public void addChildren( Iterable<Location> children ) {
+ CheckArg.isNotNull(children, "children");
+ for (Location child : children) {
+ if (child != null) this.children.add(child);
+ }
+ }
+
+ /**
* Add to the list of children that has been read the child with the given path and
identification properties. The children
* should be added in order.
*
@@ -204,6 +222,28 @@
/**
* {@inheritDoc}
*
+ * @see org.jboss.dna.graph.request.Request#cancel()
+ */
+ @Override
+ public void cancel() {
+ super.cancel();
+ this.actualStartingAfter = null;
+ this.children.clear();
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Object#hashCode()
+ */
+ @Override
+ public int hashCode() {
+ return HashCode.compute(startingAfter, workspaceName);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/ReadNodeRequest.java
===================================================================
---
trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/ReadNodeRequest.java 2009-05-15
19:28:48 UTC (rev 910)
+++
trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/ReadNodeRequest.java 2009-05-18
15:36:58 UTC (rev 911)
@@ -30,6 +30,7 @@
import java.util.List;
import java.util.Map;
import org.jboss.dna.common.util.CheckArg;
+import org.jboss.dna.common.util.HashCode;
import org.jboss.dna.graph.GraphI18n;
import org.jboss.dna.graph.Location;
import org.jboss.dna.graph.connector.RepositoryConnection;
@@ -138,6 +139,18 @@
}
/**
+ * Add a property that was read from the {@link RepositoryConnection}
+ *
+ * @param properties the properties that were read
+ * @throws IllegalArgumentException if the property is null
+ */
+ public void addProperties( Iterable<Property> properties ) {
+ for (Property property : properties) {
+ this.properties.put(property.getName(), property);
+ }
+ }
+
+ /**
* Get the children that were read from the {@link RepositoryConnection} after the
request was processed. Each child is
* represented by a location.
*
@@ -157,6 +170,23 @@
}
/**
+ * Add to the list of children that has been read the supplied children with the
given path and identification properties. The
+ * children are added in order.
+ *
+ * @param children the locations of the children that were read
+ * @throws IllegalArgumentException if the parameter is null
+ * @see #addChild(Location)
+ * @see #addChild(Path, Property)
+ * @see #addChild(Path, Property, Property...)
+ */
+ public void addChildren( Iterable<Location> children ) {
+ CheckArg.isNotNull(children, "children");
+ for (Location child : children) {
+ if (child != null) this.children.add(child);
+ }
+ }
+
+ /**
* Add to the list of children that has been read the child with the given path and
identification properties. The children
* should be added in order.
*
@@ -235,6 +265,29 @@
/**
* {@inheritDoc}
*
+ * @see org.jboss.dna.graph.request.Request#cancel()
+ */
+ @Override
+ public void cancel() {
+ super.cancel();
+ this.actualLocation = null;
+ this.children.clear();
+ this.properties.clear();
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Object#hashCode()
+ */
+ @Override
+ public int hashCode() {
+ return HashCode.compute(at, workspaceName);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
Modified:
trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/ReadPropertyRequest.java
===================================================================
---
trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/ReadPropertyRequest.java 2009-05-15
19:28:48 UTC (rev 910)
+++
trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/ReadPropertyRequest.java 2009-05-18
15:36:58 UTC (rev 911)
@@ -24,6 +24,7 @@
package org.jboss.dna.graph.request;
import org.jboss.dna.common.util.CheckArg;
+import org.jboss.dna.common.util.HashCode;
import org.jboss.dna.graph.GraphI18n;
import org.jboss.dna.graph.Location;
import org.jboss.dna.graph.connector.RepositoryConnection;
@@ -153,6 +154,28 @@
/**
* {@inheritDoc}
*
+ * @see org.jboss.dna.graph.request.Request#cancel()
+ */
+ @Override
+ public void cancel() {
+ super.cancel();
+ this.actualLocation = null;
+ this.property = null;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Object#hashCode()
+ */
+ @Override
+ public int hashCode() {
+ return HashCode.compute(on, workspaceName, propertyName);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
Modified:
trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/RemovePropertyRequest.java
===================================================================
---
trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/RemovePropertyRequest.java 2009-05-15
19:28:48 UTC (rev 910)
+++
trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/RemovePropertyRequest.java 2009-05-18
15:36:58 UTC (rev 911)
@@ -24,6 +24,7 @@
package org.jboss.dna.graph.request;
import org.jboss.dna.common.util.CheckArg;
+import org.jboss.dna.common.util.HashCode;
import org.jboss.dna.graph.GraphI18n;
import org.jboss.dna.graph.Location;
import org.jboss.dna.graph.property.Name;
@@ -141,6 +142,27 @@
/**
* {@inheritDoc}
*
+ * @see org.jboss.dna.graph.request.Request#cancel()
+ */
+ @Override
+ public void cancel() {
+ super.cancel();
+ this.actualLocation = null;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Object#hashCode()
+ */
+ @Override
+ public int hashCode() {
+ return HashCode.compute(from, workspaceName, propertyName);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
Modified:
trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/RenameNodeRequest.java
===================================================================
---
trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/RenameNodeRequest.java 2009-05-15
19:28:48 UTC (rev 910)
+++
trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/RenameNodeRequest.java 2009-05-18
15:36:58 UTC (rev 911)
@@ -24,6 +24,7 @@
package org.jboss.dna.graph.request;
import org.jboss.dna.common.util.CheckArg;
+import org.jboss.dna.common.util.HashCode;
import org.jboss.dna.graph.GraphI18n;
import org.jboss.dna.graph.Location;
import org.jboss.dna.graph.property.Name;
@@ -180,6 +181,28 @@
/**
* {@inheritDoc}
*
+ * @see org.jboss.dna.graph.request.Request#cancel()
+ */
+ @Override
+ public void cancel() {
+ super.cancel();
+ this.actualNewLocation = null;
+ this.actualOldLocation = null;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Object#hashCode()
+ */
+ @Override
+ public int hashCode() {
+ return HashCode.compute(at, workspaceName);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
Modified:
trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/SetPropertyRequest.java
===================================================================
---
trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/SetPropertyRequest.java 2009-05-15
19:28:48 UTC (rev 910)
+++
trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/SetPropertyRequest.java 2009-05-18
15:36:58 UTC (rev 911)
@@ -24,6 +24,7 @@
package org.jboss.dna.graph.request;
import org.jboss.dna.common.util.CheckArg;
+import org.jboss.dna.common.util.HashCode;
import org.jboss.dna.graph.GraphI18n;
import org.jboss.dna.graph.Location;
import org.jboss.dna.graph.property.Path;
@@ -141,6 +142,27 @@
/**
* {@inheritDoc}
*
+ * @see org.jboss.dna.graph.request.Request#cancel()
+ */
+ @Override
+ public void cancel() {
+ super.cancel();
+ this.actualLocation = null;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Object#hashCode()
+ */
+ @Override
+ public int hashCode() {
+ return HashCode.compute(on, workspaceName, property.getName());
+ }
+
+ /**
+ * {@inheritDoc}
+ *
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
Modified:
trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/UpdatePropertiesRequest.java
===================================================================
---
trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/UpdatePropertiesRequest.java 2009-05-15
19:28:48 UTC (rev 910)
+++
trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/UpdatePropertiesRequest.java 2009-05-18
15:36:58 UTC (rev 911)
@@ -26,6 +26,7 @@
import java.util.Collections;
import java.util.Map;
import org.jboss.dna.common.util.CheckArg;
+import org.jboss.dna.common.util.HashCode;
import org.jboss.dna.graph.GraphI18n;
import org.jboss.dna.graph.Location;
import org.jboss.dna.graph.property.Name;
@@ -159,6 +160,27 @@
/**
* {@inheritDoc}
*
+ * @see org.jboss.dna.graph.request.Request#cancel()
+ */
+ @Override
+ public void cancel() {
+ super.cancel();
+ this.actualLocation = null;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Object#hashCode()
+ */
+ @Override
+ public int hashCode() {
+ return HashCode.compute(on, workspaceName);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
Modified:
trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/VerifyNodeExistsRequest.java
===================================================================
---
trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/VerifyNodeExistsRequest.java 2009-05-15
19:28:48 UTC (rev 910)
+++
trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/VerifyNodeExistsRequest.java 2009-05-18
15:36:58 UTC (rev 911)
@@ -24,6 +24,7 @@
package org.jboss.dna.graph.request;
import org.jboss.dna.common.util.CheckArg;
+import org.jboss.dna.common.util.HashCode;
import org.jboss.dna.graph.GraphI18n;
import org.jboss.dna.graph.Location;
@@ -126,6 +127,27 @@
/**
* {@inheritDoc}
*
+ * @see org.jboss.dna.graph.request.Request#cancel()
+ */
+ @Override
+ public void cancel() {
+ super.cancel();
+ this.actualLocation = null;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see java.lang.Object#hashCode()
+ */
+ @Override
+ public int hashCode() {
+ return HashCode.compute(at, workspaceName);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
Modified:
trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/VerifyWorkspaceRequest.java
===================================================================
---
trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/VerifyWorkspaceRequest.java 2009-05-15
19:28:48 UTC (rev 910)
+++
trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/VerifyWorkspaceRequest.java 2009-05-18
15:36:58 UTC (rev 911)
@@ -106,6 +106,18 @@
/**
* {@inheritDoc}
*
+ * @see org.jboss.dna.graph.request.Request#cancel()
+ */
+ @Override
+ public void cancel() {
+ super.cancel();
+ this.actualLocationOfRoot = null;
+ this.actualWorkspaceName = null;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
* @see java.lang.Object#hashCode()
*/
@Override
Modified:
trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/processor/RequestProcessor.java
===================================================================
---
trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/processor/RequestProcessor.java 2009-05-15
19:28:48 UTC (rev 910)
+++
trunk/dna-graph/src/main/java/org/jboss/dna/graph/request/processor/RequestProcessor.java 2009-05-18
15:36:58 UTC (rev 911)
@@ -111,7 +111,7 @@
*
* @return the repository source name; never null or empty
*/
- public String getSourceName() {
+ public final String getSourceName() {
return sourceName;
}
@@ -120,7 +120,7 @@
*
* @return the execution context; never null
*/
- public ExecutionContext getExecutionContext() {
+ public final ExecutionContext getExecutionContext() {
return this.context;
}
@@ -129,17 +129,27 @@
*
* @return the current time in UTC; never null
*/
- protected DateTime getNowInUtc() {
+ protected final DateTime getNowInUtc() {
return this.nowInUtc;
}
/**
+ * @return defaultCachePolicy
+ */
+ protected final CachePolicy getDefaultCachePolicy() {
+ return defaultCachePolicy;
+ }
+
+ /**
* 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);
+ // Set it only if the request has no cache policy already ...
+ if (request.getCachePolicy() == null && defaultCachePolicy != null) {
+ request.setCachePolicy(defaultCachePolicy);
+ }
request.setTimeLoaded(nowInUtc);
}
@@ -151,7 +161,18 @@
*/
protected void setCacheableInfo( CacheableRequest request,
CachePolicy cachePolicy ) {
- request.setCachePolicy(cachePolicy);
+ if (cachePolicy == null) cachePolicy = defaultCachePolicy;
+ if (cachePolicy != null) {
+ if (request.getCachePolicy() != null) {
+ // Set the supplied only if less than the current ...
+ if (request.getCachePolicy().getTimeToLive() >
cachePolicy.getTimeToLive()) {
+ request.setCachePolicy(cachePolicy);
+ }
+ } else {
+ // There is no current policy, so set the supplied policy ...
+ request.setCachePolicy(cachePolicy);
+ }
+ }
request.setTimeLoaded(nowInUtc);
}
@@ -175,6 +196,8 @@
process((CreateNodeRequest)request);
} else if (request instanceof DeleteBranchRequest) {
process((DeleteBranchRequest)request);
+ } else if (request instanceof DeleteChildrenRequest) {
+ process((DeleteChildrenRequest)request);
} else if (request instanceof MoveBranchRequest) {
process((MoveBranchRequest)request);
} else if (request instanceof ReadAllChildrenRequest) {
@@ -229,6 +252,7 @@
if (request == null) return;
int numberOfErrors = 0;
List<Throwable> errors = null;
+ // Iterate over the requests in this composite, but only iterate once so that
for (Request embedded : request) {
assert embedded != null;
if (embedded.isCancelled()) return;
@@ -252,9 +276,12 @@
errorString.append("\n");
errorString.append("\t" + error.getMessage());
}
- String msg =
GraphI18n.multipleErrorsWhileExecutingRequests.text(numberOfErrors,
-
request.size(),
-
errorString.toString());
+ String msg = null;
+ if (request.size() == CompositeRequest.UNKNOWN_NUMBER_OF_REQUESTS) {
+ msg =
GraphI18n.multipleErrorsWhileExecutingManyRequests.text(numberOfErrors,
errorString.toString());
+ } else {
+ msg = GraphI18n.multipleErrorsWhileExecutingRequests.text(numberOfErrors,
request.size(), errorString.toString());
+ }
request.setError(new RepositorySourceException(getSourceName(), msg));
}
}
Modified: trunk/dna-graph/src/main/resources/org/jboss/dna/graph/GraphI18n.properties
===================================================================
--- trunk/dna-graph/src/main/resources/org/jboss/dna/graph/GraphI18n.properties 2009-05-15
19:28:48 UTC (rev 910)
+++ trunk/dna-graph/src/main/resources/org/jboss/dna/graph/GraphI18n.properties 2009-05-18
15:36:58 UTC (rev 911)
@@ -55,6 +55,7 @@
executedRequest = Executed {0}
closingRequestProcessor = Closing request processor
closedRequestProcessor = Closed request processor
+multipleErrorsWhileExecutingManyRequests = {0} of the many requests resulted in errors:
{1}
multipleErrorsWhileExecutingRequests = {0} of the {1} requests resulted in errors: {2}
unsupportedRequestType = Requests of type "{0}" are unsupported; actual request
was to {1}
unableToAddMoreRequestsToAlreadyExecutedBatch = Unable to add more requests to a batch of
graph requests that has already been executed
@@ -75,9 +76,29 @@
unableToFindRepositorySourceWithName = Unable to find a repository source named
"{0}"
# In-memory connector
-inMemoryConnectorName = In-Memory Connector
inMemoryNodeDoesNotExist = Could not find an existing node at {0}
errorSerializingInMemoryCachePolicyInSource = Error serializing a {0} instance owned by
the {1} in-memory repository
inMemoryConnectorRequestsMustHavePathOrUuid = In-Memory connector can only process
requests with a path and/or UUID
workspaceDoesNotExistInRepository = The workspace "{0}" does not exist in the
"{1}" in-memory repository
workspaceAlreadyExistsInRepository = The workspace "{0}" already exists in the
"{1}" in-memory repository
+
+# Federation connector
+namePropertyIsRequiredForFederatedRepositorySource = The "{0}" property is
required on each federated repository source
+propertyIsRequiredForFederatedRepositorySource = The "{0}" property is required
on the "{1}" federated repository source
+federatedRepositorySourceMustBeInitialized = The "{0}" federated repository
source must be initialized before it can be used
+errorReadingConfigurationForFederatedRepositorySource = Error with the "{0}"
federated repository source's configuration at "{3}" in the "{2}"
workspace of "{1}"
+errorAddingProjectionRuleParseMethod = Error while adding a parsing method for a
federation projection rule
+requiredNodeDoesNotExistRelativeToNode = The required node "{0}" does not exist
relative to "{1}" in "{2}" workspace of the "{3}"
repository
+unableToObtainConnectionToFederatedSource = Unable to obtain a connection to source
"{0}" being federated by the "{1}" workspace of the "{2}"
federated repository
+workspaceDoesNotExistInFederatedRepository = The workspace "{0}" does not exist
in the "{1}" federated repository
+locationCannotBeProjectedIntoWorkspaceAndSource = {0} does not exist in the
"{1}" workspace of the "{2}" federated repository
+unableToAddRequestToChannelThatIsDone = Unable to add a request to the "{0}"
channel since it is done: {1}
+federatedSourceDoesNotSupportCreatingWorkspaces = {0} is a source that does not allow
creating workspaces
+federatedSourceDoesNotSupportCloningWorkspaces = {0} is a source that does not allow
cloning workspaces
+federatedSourceDoesNotSupportDestroyingWorkspaces = {0} is a source that does not allow
destroying workspaces
+unableToProjectSourceInformationIntoWorkspace = Unable to project source information at
{0} in the "{1} federated repository source using projection {2}
+unableToCreateNodeUnderPlaceholder = Unable to create node "{0}" under {1} in
the "{2}" workspace of the "{3}" federarted repository because the
parent is a placeholder
+unableToUpdatePlaceholder = Unable to update node {0} in the "{1}" workspace of
the "{2}" federarted repository because the node is a placeholder
+unableToDeletePlaceholder = Unable to delete node {0} in the "{1}" workspace of
the "{2}" federarted repository because the node is a placeholder
+copyLimitedToBeWithinSingleSource = Unable to copy {0} in the "{1}" workspace
of the "{3}" federarted repository into {2} in the "{3}" workspace:
copy is only supported when the original and new locations are within the same source
+moveLimitedToBeWithinSingleSource = Unable to move {0} in the "{1}" workspace
of the "{3}" federarted repository into {2} in the "{3}" workspace:
move is only supported when the original and new locations are within the same source
Added:
trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/MockRepositoryConnection.java
===================================================================
---
trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/MockRepositoryConnection.java
(rev 0)
+++
trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/MockRepositoryConnection.java 2009-05-18
15:36:58 UTC (rev 911)
@@ -0,0 +1,146 @@
+/*
+ * JBoss DNA (
http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership. Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of
+ * individual contributors.
+ *
+ * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
+ * is licensed to you under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * JBoss DNA is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site:
http://www.fsf.org.
+ */
+package org.jboss.dna.graph.connector;
+
+import java.util.LinkedList;
+import java.util.Queue;
+import java.util.concurrent.TimeUnit;
+import javax.transaction.xa.XAResource;
+import org.jboss.dna.graph.ExecutionContext;
+import org.jboss.dna.graph.cache.CachePolicy;
+import org.jboss.dna.graph.request.Request;
+import org.jboss.dna.graph.request.processor.RequestProcessor;
+
+/**
+ * A simple connection to a mock repository, where the connection accepts requests but
does very little with them (other than
+ * record that requests were processed in the Queue supplied in the constructor).
+ */
+public class MockRepositoryConnection implements RepositoryConnection {
+
+ private final String sourceName;
+ private final Queue<Request> processed;
+
+ /**
+ * Create a new connection
+ *
+ * @param sourceName the name of the source that this connection represents; may not
be null
+ */
+ public MockRepositoryConnection( String sourceName ) {
+ this(sourceName, new LinkedList<Request>());
+ }
+
+ /**
+ * Create a new connection
+ *
+ * @param sourceName the name of the source that this connection represents; may not
be null
+ * @param processed a queue into which should be placed all requests that were {@link
#execute(ExecutionContext, Request)
+ * executed}; may be null
+ */
+ public MockRepositoryConnection( String sourceName,
+ Queue<Request> processed ) {
+ assert sourceName != null;
+ this.sourceName = sourceName;
+ this.processed = processed != null ? processed : new
LinkedList<Request>();
+ }
+
+ /**
+ * Get the list of requests that have been processed.
+ *
+ * @return the queue of processed requests; never null
+ */
+ public Queue<Request> getProcessedRequests() {
+ return processed;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.connector.RepositoryConnection#close()
+ */
+ public void close() {
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see
org.jboss.dna.graph.connector.RepositoryConnection#execute(org.jboss.dna.graph.ExecutionContext,
+ * org.jboss.dna.graph.request.Request)
+ */
+ public void execute( ExecutionContext context,
+ Request request ) throws RepositorySourceException {
+ RequestProcessor processor = new MockRepositoryRequestProcessor(sourceName,
context, processed);
+ try {
+ processor.process(request);
+ } finally {
+ processor.close();
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.connector.RepositoryConnection#getDefaultCachePolicy()
+ */
+ public CachePolicy getDefaultCachePolicy() {
+ return null;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.connector.RepositoryConnection#getSourceName()
+ */
+ public String getSourceName() {
+ return sourceName;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.connector.RepositoryConnection#getXAResource()
+ */
+ public XAResource getXAResource() {
+ return null;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.jboss.dna.graph.connector.RepositoryConnection#ping(long,
java.util.concurrent.TimeUnit)
+ */
+ public boolean ping( long time,
+ TimeUnit unit ) {
+ return true;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see
org.jboss.dna.graph.connector.RepositoryConnection#setListener(org.jboss.dna.graph.connector.RepositorySourceListener)
+ */
+ public void setListener( RepositorySourceListener listener ) {
+ throw new UnsupportedOperationException();
+ }
+
+}
Property changes on:
trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/MockRepositoryConnection.java
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Added:
trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/MockRepositoryRequestProcessor.java
===================================================================
---
trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/MockRepositoryRequestProcessor.java
(rev 0)
+++
trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/MockRepositoryRequestProcessor.java 2009-05-18
15:36:58 UTC (rev 911)
@@ -0,0 +1,296 @@
+/*
+ * JBoss DNA (
http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership. Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of
+ * individual contributors.
+ *
+ * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
+ * is licensed to you under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * JBoss DNA is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site:
http://www.fsf.org.
+ */
+package org.jboss.dna.graph.connector;
+
+import java.util.Queue;
+import org.jboss.dna.graph.ExecutionContext;
+import org.jboss.dna.graph.request.CloneWorkspaceRequest;
+import org.jboss.dna.graph.request.CopyBranchRequest;
+import org.jboss.dna.graph.request.CreateNodeRequest;
+import org.jboss.dna.graph.request.CreateWorkspaceRequest;
+import org.jboss.dna.graph.request.DeleteBranchRequest;
+import org.jboss.dna.graph.request.DestroyWorkspaceRequest;
+import org.jboss.dna.graph.request.GetWorkspacesRequest;
+import org.jboss.dna.graph.request.MoveBranchRequest;
+import org.jboss.dna.graph.request.ReadAllChildrenRequest;
+import org.jboss.dna.graph.request.ReadAllPropertiesRequest;
+import org.jboss.dna.graph.request.ReadBlockOfChildrenRequest;
+import org.jboss.dna.graph.request.ReadBranchRequest;
+import org.jboss.dna.graph.request.ReadNextBlockOfChildrenRequest;
+import org.jboss.dna.graph.request.ReadNodeRequest;
+import org.jboss.dna.graph.request.ReadPropertyRequest;
+import org.jboss.dna.graph.request.RemovePropertyRequest;
+import org.jboss.dna.graph.request.RenameNodeRequest;
+import org.jboss.dna.graph.request.Request;
+import org.jboss.dna.graph.request.SetPropertyRequest;
+import org.jboss.dna.graph.request.UpdatePropertiesRequest;
+import org.jboss.dna.graph.request.VerifyNodeExistsRequest;
+import org.jboss.dna.graph.request.VerifyWorkspaceRequest;
+import org.jboss.dna.graph.request.processor.RequestProcessor;
+
+/**
+ *
+ */
+public class MockRepositoryRequestProcessor extends RequestProcessor {
+
+ private Queue<Request> processed;
+
+ /**
+ * @param sourceName
+ * @param context
+ * @param processed
+ */
+ public MockRepositoryRequestProcessor( String sourceName,
+ ExecutionContext context,
+ Queue<Request> processed ) {
+ super(sourceName, context);
+ assert processed != null;
+ this.processed = processed;
+ }
+
+ protected void record( Request request ) {
+ processed.add(request);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see
org.jboss.dna.graph.request.processor.RequestProcessor#process(org.jboss.dna.graph.request.VerifyWorkspaceRequest)
+ */
+ @Override
+ public void process( VerifyWorkspaceRequest request ) {
+ record(request);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see
org.jboss.dna.graph.request.processor.RequestProcessor#process(org.jboss.dna.graph.request.GetWorkspacesRequest)
+ */
+ @Override
+ public void process( GetWorkspacesRequest request ) {
+ record(request);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see
org.jboss.dna.graph.request.processor.RequestProcessor#process(org.jboss.dna.graph.request.CreateWorkspaceRequest)
+ */
+ @Override
+ public void process( CreateWorkspaceRequest request ) {
+ record(request);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see
org.jboss.dna.graph.request.processor.RequestProcessor#process(org.jboss.dna.graph.request.CloneWorkspaceRequest)
+ */
+ @Override
+ public void process( CloneWorkspaceRequest request ) {
+ record(request);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see
org.jboss.dna.graph.request.processor.RequestProcessor#process(org.jboss.dna.graph.request.DestroyWorkspaceRequest)
+ */
+ @Override
+ public void process( DestroyWorkspaceRequest request ) {
+ record(request);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see
org.jboss.dna.graph.request.processor.RequestProcessor#process(org.jboss.dna.graph.request.CopyBranchRequest)
+ */
+ @Override
+ public void process( CopyBranchRequest request ) {
+ record(request);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see
org.jboss.dna.graph.request.processor.RequestProcessor#process(org.jboss.dna.graph.request.CreateNodeRequest)
+ */
+ @Override
+ public void process( CreateNodeRequest request ) {
+ record(request);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see
org.jboss.dna.graph.request.processor.RequestProcessor#process(org.jboss.dna.graph.request.DeleteBranchRequest)
+ */
+ @Override
+ public void process( DeleteBranchRequest request ) {
+ record(request);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see
org.jboss.dna.graph.request.processor.RequestProcessor#process(org.jboss.dna.graph.request.MoveBranchRequest)
+ */
+ @Override
+ public void process( MoveBranchRequest request ) {
+ record(request);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see
org.jboss.dna.graph.request.processor.RequestProcessor#process(org.jboss.dna.graph.request.ReadAllChildrenRequest)
+ */
+ @Override
+ public void process( ReadAllChildrenRequest request ) {
+ record(request);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see
org.jboss.dna.graph.request.processor.RequestProcessor#process(org.jboss.dna.graph.request.ReadAllPropertiesRequest)
+ */
+ @Override
+ public void process( ReadAllPropertiesRequest request ) {
+ record(request);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see
org.jboss.dna.graph.request.processor.RequestProcessor#process(org.jboss.dna.graph.request.UpdatePropertiesRequest)
+ */
+ @Override
+ public void process( UpdatePropertiesRequest request ) {
+ record(request);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see
org.jboss.dna.graph.request.processor.RequestProcessor#process(org.jboss.dna.graph.request.ReadBlockOfChildrenRequest)
+ */
+ @Override
+ public void process( ReadBlockOfChildrenRequest request ) {
+ record(request);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see
org.jboss.dna.graph.request.processor.RequestProcessor#process(org.jboss.dna.graph.request.ReadBranchRequest)
+ */
+ @Override
+ public void process( ReadBranchRequest request ) {
+ record(request);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see
org.jboss.dna.graph.request.processor.RequestProcessor#process(org.jboss.dna.graph.request.ReadNextBlockOfChildrenRequest)
+ */
+ @Override
+ public void process( ReadNextBlockOfChildrenRequest request ) {
+ record(request);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see
org.jboss.dna.graph.request.processor.RequestProcessor#process(org.jboss.dna.graph.request.ReadNodeRequest)
+ */
+ @Override
+ public void process( ReadNodeRequest request ) {
+ record(request);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see
org.jboss.dna.graph.request.processor.RequestProcessor#process(org.jboss.dna.graph.request.ReadPropertyRequest)
+ */
+ @Override
+ public void process( ReadPropertyRequest request ) {
+ record(request);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see
org.jboss.dna.graph.request.processor.RequestProcessor#process(org.jboss.dna.graph.request.RemovePropertyRequest)
+ */
+ @Override
+ public void process( RemovePropertyRequest request ) {
+ record(request);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see
org.jboss.dna.graph.request.processor.RequestProcessor#process(org.jboss.dna.graph.request.RenameNodeRequest)
+ */
+ @Override
+ public void process( RenameNodeRequest request ) {
+ record(request);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see
org.jboss.dna.graph.request.processor.RequestProcessor#process(org.jboss.dna.graph.request.SetPropertyRequest)
+ */
+ @Override
+ public void process( SetPropertyRequest request ) {
+ record(request);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see
org.jboss.dna.graph.request.processor.RequestProcessor#process(org.jboss.dna.graph.request.VerifyNodeExistsRequest)
+ */
+ @Override
+ public void process( VerifyNodeExistsRequest request ) {
+ record(request);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see
org.jboss.dna.graph.request.processor.RequestProcessor#processUnknownRequest(org.jboss.dna.graph.request.Request)
+ */
+ @Override
+ protected void processUnknownRequest( Request request ) {
+ record(request);
+ }
+
+}
Property changes on:
trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/MockRepositoryRequestProcessor.java
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Added:
trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/federation/AbstractFederatedRepositorySourceIntegrationTest.java
===================================================================
---
trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/federation/AbstractFederatedRepositorySourceIntegrationTest.java
(rev 0)
+++
trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/federation/AbstractFederatedRepositorySourceIntegrationTest.java 2009-05-18
15:36:58 UTC (rev 911)
@@ -0,0 +1,319 @@
+/*
+ * JBoss DNA (
http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership. Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of
+ * individual contributors.
+ *
+ * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
+ * is licensed to you under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * JBoss DNA is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site:
http://www.fsf.org.
+ */
+package org.jboss.dna.graph.connector.federation;
+
+import static org.hamcrest.core.Is.is;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.fail;
+import static org.mockito.Mockito.stub;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import org.jboss.dna.common.statistic.Stopwatch;
+import org.jboss.dna.common.util.CheckArg;
+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.connector.RepositoryConnection;
+import org.jboss.dna.graph.connector.RepositoryConnectionFactory;
+import org.jboss.dna.graph.connector.RepositoryContext;
+import org.jboss.dna.graph.connector.inmemory.InMemoryRepositorySource;
+import org.jboss.dna.graph.connector.test.AbstractConnectorTest;
+import org.jboss.dna.graph.property.Name;
+import org.jboss.dna.graph.property.Path;
+import org.jboss.dna.graph.property.PathNotFoundException;
+import org.jboss.dna.graph.property.Property;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.mockito.MockitoAnnotations;
+import org.mockito.MockitoAnnotations.Mock;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
+
+/**
+ *
+ */
+public abstract class AbstractFederatedRepositorySourceIntegrationTest {
+
+ private static final Stopwatch FEDERATED_TIMER = new Stopwatch();
+ private static final Stopwatch SOURCE_TIMER = new Stopwatch();
+
+ protected FederatedRepositorySource source;
+ private String sourceName;
+ private String repositoryName;
+ private String configurationSourceName;
+ private String configurationWorkspaceName;
+ private InMemoryRepositorySource configRepositorySource;
+ private RepositoryConnection configRepositoryConnection;
+ protected ExecutionContext context;
+ private Map<String, InMemoryRepositorySource> sources;
+ protected Graph federated;
+ @Mock
+ private RepositoryConnectionFactory connectionFactory;
+ @Mock
+ private RepositoryContext repositoryContext;
+
+ /**
+ * @throws java.lang.Exception
+ */
+ @Before
+ public void beforeEach() throws Exception {
+ MockitoAnnotations.initMocks(this);
+ context = new ExecutionContext();
+ configurationSourceName = "configuration";
+ configurationWorkspaceName = "configSpace";
+ repositoryName = "Test Repository";
+
+ // Set up the source ...
+ source = new FederatedRepositorySource();
+ source.setName(repositoryName);
+ sourceName = "federated source";
+ source.setName(sourceName);
+ source.setConfigurationSourceName(configurationSourceName);
+ source.setConfigurationWorkspaceName(configurationWorkspaceName);
+ source.setConfigurationPath("/a/b/Test Repository");
+ source.initialize(repositoryContext);
+
+ // Set up the configuration repository ...
+ configRepositorySource = new InMemoryRepositorySource();
+ configRepositorySource.setName("Configuration Repository");
+ configRepositorySource.setDefaultWorkspaceName(configurationWorkspaceName);
+ Graph config = Graph.create(configRepositorySource, context);
+ config.create("/a");
+ config.create("/a/b");
+ config.create("/a/b/Test Repository");
+ config.create("/a/b/Test Repository/dna:workspaces");
+
+ // Set up the map of sources ...
+ sources = new HashMap<String, InMemoryRepositorySource>();
+
+ // Stub the RepositoryContext and RepositoryConnectionFactory instances ...
+ configRepositoryConnection = configRepositorySource.getConnection();
+ stub(repositoryContext.getExecutionContext()).toReturn(context);
+
stub(repositoryContext.getRepositoryConnectionFactory()).toReturn(connectionFactory);
+
stub(connectionFactory.createConnection(configurationSourceName)).toReturn(configRepositoryConnection);
+ stub(connectionFactory.createConnection(sourceName)).toAnswer(new
Answer<RepositoryConnection>() {
+ public RepositoryConnection answer( InvocationOnMock invocation ) throws
Throwable {
+ return source.getConnection();
+ }
+ });
+
+ // Create the graph to the federated repository ...
+ federated = Graph.create(sourceName, connectionFactory, context);
+ }
+
+ @AfterClass
+ public static void afterAll() {
+ System.out.println("Results for federated reads: " +
FEDERATED_TIMER.getSimpleStatistics());
+ System.out.println("Results for source reads: " +
FEDERATED_TIMER.getSimpleStatistics());
+ }
+
+ /**
+ * Add to the supplied workspace in the federated repository a projection from the
workspace in the supplied source.
+ *
+ * @param federatedWorkspace
+ * @param projectionName
+ * @param sourceName
+ * @param workspaceName
+ * @param projectionRules
+ */
+ protected void addProjection( String federatedWorkspace,
+ String projectionName,
+ String sourceName,
+ String workspaceName,
+ String... projectionRules ) {
+ CheckArg.isNotNull(federatedWorkspace, "federatedWorkspace");
+ CheckArg.isNotNull(projectionName, "projectionName");
+ CheckArg.isNotNull(sourceName, "sourceName");
+ CheckArg.isNotNull(workspaceName, "workspaceName");
+ CheckArg.isNotEmpty(projectionRules, "projectionRules");
+ assertThat(source.getConfigurationPath().endsWith("/"), is(true));
+ String wsPath = source.getConfigurationPath() + "dna:workspaces/" +
federatedWorkspace;
+ String projectionPath = wsPath + "/dna:projections/" + projectionName;
+ Graph config = Graph.create(configRepositorySource, context);
+ config.useWorkspace(configurationWorkspaceName);
+ config.createIfMissing(wsPath);
+ config.createIfMissing(wsPath + "/dna:projections");
+ config.createAt(projectionPath).with(FederatedLexicon.PROJECTION_RULES,
(Object[])projectionRules).with(FederatedLexicon.SOURCE_NAME,
+
sourceName).with(FederatedLexicon.WORKSPACE_NAME,
+
workspaceName).and();
+ // Make sure the source and workspace exist ...
+ graphFor(sourceName, workspaceName);
+ }
+
+ /**
+ * Obtain a graph to the named source and workspace. If the source does not exist, it
is created. Also, if the supplied
+ * workspace does not exist, it is also created.
+ *
+ * @param sourceName the name of the source; may not be null
+ * @param workspaceName the name of the workspace; may not be null
+ * @return the resulting graph; never null
+ */
+ protected Graph graphFor( String sourceName,
+ String workspaceName ) {
+ CheckArg.isNotNull(sourceName, "sourceName");
+ CheckArg.isNotNull(workspaceName, "workspaceName");
+ InMemoryRepositorySource source = sources.get(sourceName);
+ if (source == null) {
+ // Add a new source with this name ...
+ source = new InMemoryRepositorySource();
+ source.setName(sourceName);
+ sources.put(sourceName, source);
+ final InMemoryRepositorySource newSource = source;
+ // Stub the repository connection factory to return a new connection for this
source ...
+ stub(connectionFactory.createConnection(sourceName)).toAnswer(new
Answer<RepositoryConnection>() {
+ public RepositoryConnection answer( InvocationOnMock invocation ) throws
Throwable {
+ return newSource.getConnection();
+ }
+ });
+ }
+ // Make sure there's a workspace for it ...
+ Graph sourceGraph = Graph.create(sourceName, connectionFactory, context);
+ if (sourceGraph.getWorkspaces().contains(workspaceName)) {
+ sourceGraph.useWorkspace(workspaceName);
+ } else {
+ sourceGraph.createWorkspace().named(workspaceName);
+ }
+ return sourceGraph;
+ }
+
+ /**
+ * Assert that the node does not exist in the federated repository given by the
supplied path nor in the underlying source
+ * given by the path, source name, and workspace name.
+ *
+ * @param pathInFederated
+ * @param pathInSource
+ * @param sourceName
+ * @param workspaceName
+ */
+ protected void assertNoNode( String pathInFederated,
+ String pathInSource,
+ String sourceName,
+ String workspaceName ) {
+ try {
+ FEDERATED_TIMER.start();
+ federated.getNodeAt(pathInFederated);
+ FEDERATED_TIMER.stop();
+ fail("Did not expect to find federated node \"" +
pathInFederated + "\"");
+ } catch (PathNotFoundException e) {
+ // expected
+ }
+ try {
+ SOURCE_TIMER.start();
+ graphFor(sourceName, workspaceName).getNodeAt(pathInSource);
+ SOURCE_TIMER.stop();
+ fail("Did not expect to find source node \"" + pathInSource +
"\" in workspace \"" + workspaceName
+ + "\" of source \"" + sourceName +
"\"");
+ } catch (PathNotFoundException e) {
+ // expected
+ }
+ }
+
+ /**
+ * Assert that the node in the federated repository given by the supplied path
represents the same node in the underlying
+ * source given by the path, source name, and workspace name.
+ *
+ * @param pathInFederated
+ * @param pathInSource
+ * @param sourceName
+ * @param workspaceName
+ * @param extraChildren
+ */
+ protected void assertSameNode( String pathInFederated,
+ String pathInSource,
+ String sourceName,
+ String workspaceName,
+ String... extraChildren ) {
+ FEDERATED_TIMER.start();
+ Node fedNode = federated.getNodeAt(pathInFederated);
+ FEDERATED_TIMER.stop();
+ SOURCE_TIMER.start();
+ Node sourceNode = graphFor(sourceName, workspaceName).getNodeAt(pathInSource);
+ SOURCE_TIMER.stop();
+ // The name should match ...
+ Path fedPath = fedNode.getLocation().getPath();
+ Path sourcePath = sourceNode.getLocation().getPath();
+ if (!fedPath.isRoot() && !sourcePath.isRoot()) {
+ assertThat(fedNode.getLocation().getPath().getLastSegment().getName(),
+
is(sourceNode.getLocation().getPath().getLastSegment().getName()));
+ }
+ // The children should match ...
+ List<Path.Segment> fedChildren = new ArrayList<Path.Segment>();
+ List<Path.Segment> sourceChildren = new ArrayList<Path.Segment>();
+ for (Location child : fedNode.getChildren()) {
+ fedChildren.add(child.getPath().getLastSegment());
+ }
+ for (Location child : sourceNode.getChildren()) {
+ sourceChildren.add(child.getPath().getLastSegment());
+ }
+ // Add any extra children to the 'sourceChildren' ...
+ for (String extraChild : extraChildren) {
+
sourceChildren.add(context.getValueFactories().getPathFactory().createSegment(extraChild));
+ }
+ assertThat(fedChildren, is(sourceChildren));
+ // The properties should match ...
+ Map<Name, Property> fedProps = fedNode.getPropertiesByName();
+ Map<Name, Property> sourceProps = sourceNode.getPropertiesByName();
+ assertThat(fedProps, is(sourceProps));
+
+ // Now, try to get the children only ...
+ FEDERATED_TIMER.start();
+ List<Location> children = federated.getChildren().of(pathInFederated);
+ FEDERATED_TIMER.stop();
+ fedChildren.clear();
+ for (Location child : children) {
+ fedChildren.add(child.getPath().getLastSegment());
+ }
+ assertThat(fedChildren, is(sourceChildren));
+
+ // And try to get the properties only ...
+ FEDERATED_TIMER.start();
+ fedProps = federated.getPropertiesByName().on(pathInFederated);
+ FEDERATED_TIMER.stop();
+ assertThat(fedProps, is(sourceProps));
+
+ // And try to get the properties one by one ...
+ for (Property sourceProp : sourceProps.values()) {
+ FEDERATED_TIMER.start();
+ Property fedProp =
federated.getProperty(sourceProp.getName()).on(pathInFederated);
+ FEDERATED_TIMER.stop();
+ assertThat(fedProp, is(sourceProp));
+ }
+
+ // Try reading a subgraph of depth 2 ...
+ FEDERATED_TIMER.start();
+ Subgraph fedSubgraph = federated.getSubgraphOfDepth(2).at(pathInFederated);
+ FEDERATED_TIMER.stop();
+ SOURCE_TIMER.start();
+ Subgraph sourceSubgraph = graphFor(sourceName,
workspaceName).getSubgraphOfDepth(2).at(pathInSource);
+ SOURCE_TIMER.stop();
+ if (extraChildren.length == 0) {
+ // Can only compare the graphs when there are no extra children ...
+ AbstractConnectorTest.assertEquivalentSubgraphs(fedSubgraph, sourceSubgraph,
true, false);
+ }
+ }
+}
Property changes on:
trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/federation/AbstractFederatedRepositorySourceIntegrationTest.java
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Added:
trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/federation/AbstractProjectorTest.java
===================================================================
---
trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/federation/AbstractProjectorTest.java
(rev 0)
+++
trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/federation/AbstractProjectorTest.java 2009-05-18
15:36:58 UTC (rev 911)
@@ -0,0 +1,116 @@
+/*
+ * JBoss DNA (
http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership. Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of
+ * individual contributors.
+ *
+ * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
+ * is licensed to you under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * JBoss DNA is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site:
http://www.fsf.org.
+ */
+package org.jboss.dna.graph.connector.federation;
+
+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.ArrayList;
+import java.util.List;
+import java.util.UUID;
+import org.jboss.dna.graph.ExecutionContext;
+import org.jboss.dna.graph.Location;
+import org.jboss.dna.graph.connector.federation.Projection.Rule;
+import org.jboss.dna.graph.property.Path;
+import org.junit.Before;
+
+/**
+ * @param <ProjectorType>
+ */
+public abstract class AbstractProjectorTest<ProjectorType extends Projector> {
+
+ protected ExecutionContext context;
+ protected List<Projection> projections;
+ protected ProjectorType projector;
+ protected Location locationA;
+ protected Location locationB;
+ protected Location locationAB;
+ protected Location locationABCD;
+
+ @Before
+ public void beforeEach() {
+ this.context = new ExecutionContext();
+ this.projections = new ArrayList<Projection>();
+ this.locationA = Location.create(path("/a"));
+ this.locationB = Location.create(path("/b"));
+ this.locationAB = Location.create(path("/a/b"));
+ this.locationABCD = Location.create(path("/a/b/c/d"));
+ }
+
+ protected Rule[] rule( String... rule ) {
+ Rule[] rules = new Rule[rule.length];
+ for (int i = 0; i != rule.length; ++i) {
+ rules[i] = Projection.fromString(rule[i], context);
+ }
+ return rules;
+ }
+
+ protected Path path( String path ) {
+ return context.getValueFactories().getPathFactory().create(path);
+ }
+
+ protected Path path( Path parent,
+ String relativePath ) {
+ return context.getValueFactories().getPathFactory().create(parent,
relativePath);
+ }
+
+ protected void addProjection( String sourceName,
+ String workspaceName,
+ String... rules ) {
+ projections.add(new Projection(sourceName, workspaceName, false, rule(rules)));
+ }
+
+ protected Path.Segment segment( String segment ) {
+ return context.getValueFactories().getPathFactory().createSegment(segment);
+ }
+
+ protected Location location( String path ) {
+ return Location.create(path(path));
+ }
+
+ protected Location location( UUID uuid ) {
+ return Location.create(uuid);
+ }
+
+ protected void assertNoProjectedNodeAt( String path ) {
+ ProjectedNode node = projector.project(context, location(path), false);
+ assertThat(node, is(nullValue()));
+ }
+
+ protected void assertPlacholderHasChildren( String parent,
+ String... childSegments ) {
+ Path parentPath = path(parent);
+ ProjectedNode node = projector.project(context, Location.create(parentPath),
false);
+ assertThat(node, is(notNullValue()));
+ assertThat(node.isPlaceholder(), is(true));
+ PlaceholderNode placeholder = node.asPlaceholder();
+ List<Location> locations = new ArrayList<Location>();
+ for (String childSegment : childSegments) {
+ Path childPath = path(parentPath, childSegment);
+ locations.add(Location.create(childPath));
+ }
+ assertThat(placeholder.children(), is(locations));
+ }
+}
Property changes on:
trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/federation/AbstractProjectorTest.java
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Added:
trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/federation/BranchedMirrorProjectorTest.java
===================================================================
---
trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/federation/BranchedMirrorProjectorTest.java
(rev 0)
+++
trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/federation/BranchedMirrorProjectorTest.java 2009-05-18
15:36:58 UTC (rev 911)
@@ -0,0 +1,147 @@
+/*
+ * JBoss DNA (
http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership. Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of
+ * individual contributors.
+ *
+ * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
+ * is licensed to you under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * JBoss DNA is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site:
http://www.fsf.org.
+ */
+package org.jboss.dna.graph.connector.federation;
+
+import static org.hamcrest.core.Is.is;
+import static org.junit.Assert.assertThat;
+import org.jboss.dna.graph.Location;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ *
+ */
+public class BranchedMirrorProjectorTest extends
AbstractProjectorTest<BranchedMirrorProjector> {
+
+ private String mirrorSourceName;
+ private String mirrorWorkspaceName;
+ private String branchSourceName;
+ private String branchWorkspaceName;
+
+ @Before
+ @Override
+ public void beforeEach() {
+ super.beforeEach();
+ this.mirrorSourceName = "source1";
+ this.mirrorWorkspaceName = "workspace1";
+ this.branchSourceName = "source2";
+ this.branchWorkspaceName = "workspace2";
+ addProjection(mirrorSourceName, mirrorWorkspaceName, "/ => /");
+ addProjection(branchSourceName, branchWorkspaceName, "/system =>
/system");
+ this.projector = BranchedMirrorProjector.with(context, projections);
+ }
+
+ protected void assertProjectedIntoMirror( String federatedPath ) {
+ Location location = location(federatedPath);
+ ProjectedNode node = projector.project(context, location, false);
+ assertThat(node.isProxy(), is(true));
+ ProxyNode proxy = node.asProxy();
+ assertThat(proxy.location(), is(location));
+ assertThat(proxy.source(), is(mirrorSourceName));
+ assertThat(proxy.workspaceName(), is(mirrorWorkspaceName));
+ assertThat(proxy.hasNext(), is(false));
+ }
+
+ protected void assertProjectedIntoBranch( String federatedPath ) {
+ assertProjectedIntoBranch(federatedPath, federatedPath);
+ }
+
+ protected void assertProjectedIntoBranch( String federatedPath,
+ String expectedPathInSource ) {
+ Location location = location(federatedPath);
+ ProjectedNode node = projector.project(context, location, false);
+ assertThat(node.isProxy(), is(true));
+ ProxyNode proxy = node.asProxy();
+ assertThat(proxy.source(), is(branchSourceName));
+ assertThat(proxy.workspaceName(), is(branchWorkspaceName));
+ assertThat(proxy.location().getPath(), is(path(expectedPathInSource)));
+ assertThat(proxy.hasNext(), is(false));
+ }
+
+ @Test
+ public void shouldAlwaysReturnProxyNodeForLocationInMirrorSource() {
+ assertProjectedIntoMirror("/a");
+ assertProjectedIntoMirror("/a/b/c/d");
+ assertProjectedIntoMirror("/system[2]/b/c/d"); // technically not in
the branch!
+ }
+
+ @Test
+ public void
shouldAlwaysReturnProxyNodeForLocationInBranchOnlyOneLevelDeepWithOneForOneMapping() {
+ assertProjectedIntoBranch("/system");
+ assertProjectedIntoBranch("/system[1]");
+ assertProjectedIntoBranch("/system/d");
+ assertProjectedIntoBranch("/system[1]/d");
+ }
+
+ @Test
+ public void shouldReturnForRootLocationAProxyForMirrorAndPlaceholderForBranch() {
+ Location location = location("/");
+ ProjectedNode node = projector.project(context, location, false);
+ assertThat(node.isProxy(), is(true));
+ ProxyNode proxy = node.asProxy();
+ assertThat(proxy.source(), is(mirrorSourceName));
+ assertThat(proxy.workspaceName(), is(mirrorWorkspaceName));
+ assertThat(proxy.location().getPath(), is(path("/")));
+ assertThat(proxy.hasNext(), is(true));
+
+ ProjectedNode next = node.next();
+ assertThat(next.isPlaceholder(), is(true));
+ PlaceholderNode placeholder = next.asPlaceholder();
+ assertThat(placeholder.isPlaceholder(), is(true));
+ assertThat(placeholder.location().getPath(), is(path("/")));
+ assertThat(placeholder.children().size(), is(1));
+ assertThat(placeholder.children().get(0).getPath(),
is(path("/system")));
+ assertThat(placeholder.hasNext(), is(false));
+ }
+
+ @Test
+ public void
shouldAlwaysReturnProxyNodeForLocationInBranchMultipleLevelsDeepWithOneForOneMapping() {
+ projections.clear();
+ addProjection(mirrorSourceName, mirrorWorkspaceName, "/ => /");
+ addProjection(branchSourceName, branchWorkspaceName, "/a/b/c =>
/a/b/c");
+ this.projector = BranchedMirrorProjector.with(context, projections);
+ assertProjectedIntoBranch("/a/b/c");
+ assertProjectedIntoBranch("/a/b/c/d");
+ }
+
+ @Test
+ public void
shouldAlwaysReturnProxyNodeForLocationInBranchMultipleLevelsDeepWithDissimilarMapping() {
+ projections.clear();
+ addProjection(mirrorSourceName, mirrorWorkspaceName, "/ => /");
+ addProjection(branchSourceName, branchWorkspaceName, "/a/b/c =>
/d/e");
+ this.projector = BranchedMirrorProjector.with(context, projections);
+ assertProjectedIntoBranch("/a/b/c", "/d/e");
+ assertProjectedIntoBranch("/a/b/c/f", "/d/e/f");
+ }
+
+ @Test
+ public void
shouldAlwaysReturnProxyNodeForLocationInBranchMultipleLevelsDeepWithRootMapping() {
+ projections.clear();
+ addProjection(mirrorSourceName, mirrorWorkspaceName, "/ => /");
+ addProjection(branchSourceName, branchWorkspaceName, "/a/b/c =>
/");
+ this.projector = BranchedMirrorProjector.with(context, projections);
+ assertProjectedIntoBranch("/a/b/c", "/");
+ assertProjectedIntoBranch("/a/b/c/f", "/f");
+ }
+}
Property changes on:
trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/federation/BranchedMirrorProjectorTest.java
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Added:
trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/federation/FederatedRepositoryConnectionTest.java
===================================================================
---
trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/federation/FederatedRepositoryConnectionTest.java
(rev 0)
+++
trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/federation/FederatedRepositoryConnectionTest.java 2009-05-18
15:36:58 UTC (rev 911)
@@ -0,0 +1,108 @@
+/*
+ * JBoss DNA (
http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership. Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of
+ * individual contributors.
+ *
+ * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
+ * is licensed to you under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * JBoss DNA is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site:
http://www.fsf.org.
+ */
+package org.jboss.dna.graph.connector.federation;
+
+import static org.hamcrest.core.Is.is;
+import static org.hamcrest.core.IsNull.nullValue;
+import static org.junit.Assert.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.stub;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import org.jboss.dna.graph.cache.CachePolicy;
+import org.jboss.dna.graph.request.CompositeRequest;
+import org.jboss.dna.graph.request.Request;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.MockitoAnnotations;
+import org.mockito.MockitoAnnotations.Mock;
+
+/**
+ * @author Randall Hauch
+ */
+public class FederatedRepositoryConnectionTest {
+
+ private FederatedRepositoryConnection connection;
+ @Mock
+ private FederatedRepository repository;
+
+ @Before
+ public void beforeEach() {
+ MockitoAnnotations.initMocks(this);
+ connection = new FederatedRepositoryConnection(repository);
+ }
+
+ @Test
+ public void shouldHaveRepository() {
+ assertThat(connection.getRepository(), is(repository));
+ }
+
+ @Test
+ public void shouldHaveNoXaResource() {
+ assertThat(connection.getXAResource(), is(nullValue()));
+ }
+
+ @Test
+ public void shouldGetDefaultCachePolicyFromRepository() {
+ CachePolicy cachePolicy = mock(CachePolicy.class);
+ stub(repository.getDefaultCachePolicy()).toReturn(cachePolicy);
+ assertThat(connection.getDefaultCachePolicy(), is(cachePolicy));
+ verify(repository, times(1)).getDefaultCachePolicy();
+ }
+
+ @Test
+ public void shouldGetSourceNameFromRepository() {
+ String name = "Something";
+ stub(repository.getSourceName()).toReturn(name);
+ assertThat(connection.getSourceName(), is(name));
+ verify(repository, times(1)).getSourceName();
+ }
+
+ @Test
+ public void shouldProcessNonCompositeRequestSynchronously() {
+ Request request = mock(Request.class);
+ assertThat(connection.shouldProcessSynchronously(request), is(true));
+ }
+
+ @Test
+ public void shouldProcessCompositeRequestWithOneRequestSynchronously() {
+ CompositeRequest request = mock(CompositeRequest.class);
+ stub(request.size()).toReturn(1);
+ assertThat(connection.shouldProcessSynchronously(request), is(true));
+ }
+
+ @Test
+ public void shouldProcessCompositeRequestWithMultipleRequestsAsynchronously() {
+ CompositeRequest request = mock(CompositeRequest.class);
+ stub(request.size()).toReturn(2);
+ assertThat(connection.shouldProcessSynchronously(request), is(false));
+ }
+
+ @Test
+ public void shouldAllowCloseToBeCalledRepeatedly() {
+ for (int i = 0; i != 10; ++i) {
+ connection.close();
+ }
+ }
+}
Property changes on:
trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/federation/FederatedRepositoryConnectionTest.java
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Added:
trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/federation/FederatedRepositorySourceTest.java
===================================================================
---
trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/federation/FederatedRepositorySourceTest.java
(rev 0)
+++
trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/federation/FederatedRepositorySourceTest.java 2009-05-18
15:36:58 UTC (rev 911)
@@ -0,0 +1,242 @@
+/*
+ * JBoss DNA (
http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership. Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of
+ * individual contributors.
+ *
+ * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
+ * is licensed to you under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * JBoss DNA is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site:
http://www.fsf.org.
+ */
+package org.jboss.dna.graph.connector.federation;
+
+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 static org.mockito.Matchers.argThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.stub;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Hashtable;
+import java.util.Map;
+import javax.naming.Context;
+import javax.naming.Name;
+import javax.naming.RefAddr;
+import javax.naming.Reference;
+import javax.naming.spi.ObjectFactory;
+import javax.security.auth.callback.CallbackHandler;
+import org.jboss.dna.graph.ExecutionContext;
+import org.jboss.dna.graph.Graph;
+import org.jboss.dna.graph.connector.RepositoryConnection;
+import org.jboss.dna.graph.connector.RepositoryConnectionFactory;
+import org.jboss.dna.graph.connector.RepositoryContext;
+import org.jboss.dna.graph.connector.inmemory.InMemoryRepositorySource;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.ArgumentMatcher;
+import org.mockito.MockitoAnnotations;
+import org.mockito.MockitoAnnotations.Mock;
+
+/**
+ *
+ */
+public class FederatedRepositorySourceTest {
+
+ private FederatedRepositorySource source;
+ private String sourceName;
+ private String repositoryName;
+ private String configurationSourceName;
+ private InMemoryRepositorySource configRepositorySource;
+ private RepositoryConnection configRepositoryConnection;
+ private ExecutionContext context;
+ @Mock
+ private RepositoryConnection connection;
+ @Mock
+ private RepositoryConnectionFactory connectionFactory;
+ @Mock
+ private RepositoryContext repositoryContext;
+
+ /**
+ * @throws java.lang.Exception
+ */
+ @Before
+ public void beforeEach() throws Exception {
+ MockitoAnnotations.initMocks(this);
+ context = new ExecutionContext();
+ configurationSourceName = "configuration";
+ repositoryName = "Test Repository";
+ source = new FederatedRepositorySource();
+ source.setName(repositoryName);
+ sourceName = "federated source";
+ source.setName(sourceName);
+ source.setConfigurationSourceName(configurationSourceName);
+ source.setConfigurationWorkspaceName("configSpace");
+ source.setConfigurationPath("/a/b/Test Repository");
+ source.initialize(repositoryContext);
+ configRepositorySource = new InMemoryRepositorySource();
+ configRepositorySource.setName("Configuration Repository");
+
+ Graph configRepository = Graph.create(configRepositorySource, context);
+ configRepository.createWorkspace().named("configSpace");
+ Graph.Batch batch = configRepository.batch();
+ batch.create("/a").and();
+ batch.create("/a/b").and();
+ batch.create("/a/b/Test
Repository").with(FederatedLexicon.DEFAULT_WORKSPACE_NAME,
"fedSpace").and();
+ batch.create("/a/b/Test Repository/dna:workspaces").and();
+ batch.create("/a/b/Test Repository/dna:workspaces/fedSpace").and();
+ batch.create("/a/b/Test
Repository/dna:workspaces/fedSpace/dna:cache").with(FederatedLexicon.PROJECTION_RULES,
"/ => /").with(FederatedLexicon.SOURCE_NAME,
+
"cache
source").with(FederatedLexicon.WORKSPACE_NAME,
+
"cacheSpace").with(FederatedLexicon.TIME_TO_EXPIRE,
+
100000).and();
+ batch.create("/a/b/Test
Repository/dna:workspaces/fedSpace/dna:projections").and();
+ batch.create("/a/b/Test
Repository/dna:workspaces/fedSpace/dna:projections/projection1").with(FederatedLexicon.PROJECTION_RULES,
+
"/ => /s1").with(FederatedLexicon.SOURCE_NAME,
+
"source 1").with(FederatedLexicon.WORKSPACE_NAME,
+
"s1 workspace").and();
+ batch.create("/a/b/Test
Repository/dna:workspaces/fedSpace/dna:projections/projection2").with(FederatedLexicon.PROJECTION_RULES,
+
"/ => /s2").with(FederatedLexicon.SOURCE_NAME,
+
"source 2").with(FederatedLexicon.WORKSPACE_NAME,
+
"s2 worskspace").and();
+ batch.execute();
+
+ configRepositoryConnection = configRepositorySource.getConnection();
+ stub(repositoryContext.getExecutionContext()).toReturn(context);
+
stub(repositoryContext.getRepositoryConnectionFactory()).toReturn(connectionFactory);
+
stub(connectionFactory.createConnection(configurationSourceName)).toReturn(configRepositoryConnection);
+ }
+
+ protected static CallbackHandler anyCallbackHandler() {
+ return argThat(new ArgumentMatcher<CallbackHandler>() {
+ @Override
+ public boolean matches( Object callback ) {
+ return callback != null;
+ }
+ });
+ }
+
+ @Test
+ public void shouldReturnNonNullCapabilities() {
+ assertThat(source.getCapabilities(), is(notNullValue()));
+ }
+
+ @Test
+ public void shouldSupportSameNameSiblings() {
+ assertThat(source.getCapabilities().supportsSameNameSiblings(), is(true));
+ }
+
+ @Test
+ public void shouldSupportUpdates() {
+ assertThat(source.getCapabilities().supportsUpdates(), is(true));
+ }
+
+ @Test
+ public void shouldCreateConnectionsByAuthenticateUsingFederationRepository() throws
Exception {
+ connection = source.getConnection();
+ assertThat(connection, is(notNullValue()));
+ }
+
+ @Test
+ public void shouldHaveNullSourceNameUponConstruction() {
+ source = new FederatedRepositorySource();
+ assertThat(source.getName(), is(nullValue()));
+ }
+
+ @Test
+ public void shouldAllowSettingName() {
+ source.setName("Something");
+ assertThat(source.getName(), is("Something"));
+ source.setName("another name");
+ assertThat(source.getName(), is("another name"));
+ }
+
+ @Test
+ public void shouldAllowSettingNameToNull() {
+ source.setName("some name");
+ source.setName(null);
+ assertThat(source.getName(), is(nullValue()));
+ }
+
+ @Test
+ public void shouldHaveDefaultRetryLimit() {
+ assertThat(source.getRetryLimit(),
is(FederatedRepositorySource.DEFAULT_RETRY_LIMIT));
+ }
+
+ @Test
+ public void shouldSetRetryLimitToZeroWhenSetWithNonPositiveValue() {
+ source.setRetryLimit(0);
+ assertThat(source.getRetryLimit(), is(0));
+ source.setRetryLimit(-1);
+ assertThat(source.getRetryLimit(), is(0));
+ source.setRetryLimit(-100);
+ assertThat(source.getRetryLimit(), is(0));
+ }
+
+ @Test
+ public void shouldAllowRetryLimitToBeSet() {
+ for (int i = 0; i != 100; ++i) {
+ source.setRetryLimit(i);
+ assertThat(source.getRetryLimit(), is(i));
+ }
+ }
+
+ @Test
+ public void shouldCreateJndiReferenceAndRecreatedObjectFromReference() throws
Exception {
+ int retryLimit = 100;
+ source.setRetryLimit(retryLimit);
+ source.setName("Some source");
+ source.setConfigurationSourceName("config source");
+ source.setConfigurationPath("/a/b/c");
+
+ Reference ref = source.getReference();
+ assertThat(ref.getClassName(), is(FederatedRepositorySource.class.getName()));
+ assertThat(ref.getFactoryClassName(),
is(FederatedRepositorySource.class.getName()));
+
+ Map<String, Object> refAttributes = new HashMap<String, Object>();
+ Enumeration<RefAddr> enumeration = ref.getAll();
+ while (enumeration.hasMoreElements()) {
+ RefAddr addr = enumeration.nextElement();
+ refAttributes.put(addr.getType(), addr.getContent());
+ }
+
+ assertThat((String)refAttributes.remove(FederatedRepositorySource.SOURCE_NAME),
is(source.getName()));
+ assertThat((String)refAttributes.remove(FederatedRepositorySource.RETRY_LIMIT),
is(Integer.toString(retryLimit)));
+
assertThat((String)refAttributes.remove(FederatedRepositorySource.CONFIGURATION_WORKSPACE_NAME),
+ is(source.getConfigurationWorkspaceName()));
+
assertThat((String)refAttributes.remove(FederatedRepositorySource.CONFIGURATION_SOURCE_NAME),
+ is(source.getConfigurationSourceName()));
+
assertThat((String)refAttributes.remove(FederatedRepositorySource.CONFIGURATION_PATH),
is("/a/b/c/"));
+ assertThat(refAttributes.isEmpty(), is(true));
+
+ // Recreate the object, use a newly constructed source ...
+ ObjectFactory factory = new FederatedRepositorySource();
+ Name name = mock(Name.class);
+ Context context = mock(Context.class);
+ Hashtable<?, ?> env = new Hashtable<Object, Object>();
+ FederatedRepositorySource recoveredSource =
(FederatedRepositorySource)factory.getObjectInstance(ref, name, context, env);
+ assertThat(recoveredSource, is(notNullValue()));
+
+ assertThat(recoveredSource.getName(), is(source.getName()));
+ assertThat(recoveredSource.getRetryLimit(), is(source.getRetryLimit()));
+ assertThat(recoveredSource.getConfigurationSourceName(),
is(source.getConfigurationSourceName()));
+ assertThat(recoveredSource.getConfigurationWorkspaceName(),
is(source.getConfigurationWorkspaceName()));
+ assertThat(recoveredSource.getConfigurationPath(),
is(source.getConfigurationPath()));
+
+ assertThat(recoveredSource.equals(source), is(true));
+ assertThat(source.equals(recoveredSource), is(true));
+ }
+}
Property changes on:
trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/federation/FederatedRepositorySourceTest.java
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Added:
trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/federation/FederatedRepositorySourceUsingMirrorAndBranchProjectionsTest.java
===================================================================
---
trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/federation/FederatedRepositorySourceUsingMirrorAndBranchProjectionsTest.java
(rev 0)
+++
trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/federation/FederatedRepositorySourceUsingMirrorAndBranchProjectionsTest.java 2009-05-18
15:36:58 UTC (rev 911)
@@ -0,0 +1,215 @@
+/*
+ * JBoss DNA (
http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership. Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of
+ * individual contributors.
+ *
+ * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
+ * is licensed to you under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * JBoss DNA is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site:
http://www.fsf.org.
+ */
+package org.jboss.dna.graph.connector.federation;
+
+import static org.hamcrest.core.Is.is;
+import static org.junit.Assert.assertThat;
+import java.util.Set;
+import org.jboss.dna.graph.Graph;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * An integration test that verifies the behavior of a {@link FederatedRepositorySource}
configured with a federated workspace
+ * using a single mirror projection to an underlying source.
+ */
+public class FederatedRepositorySourceUsingMirrorAndBranchProjectionsTest
+ extends AbstractFederatedRepositorySourceIntegrationTest {
+
+ private String mirrorSourceName;
+ private String mirrorWorkspaceName;
+ private String branchSourceName;
+ private String branchWorkspaceName;
+
+ @Override
+ @Before
+ public void beforeEach() throws Exception {
+ super.beforeEach();
+
+ // Set up the mirror projection ...
+ mirrorSourceName = "Mirror Source";
+ mirrorWorkspaceName = "Mirror Workspace";
+ addProjection("fedSpace", "Mirror Projection",
mirrorSourceName, mirrorWorkspaceName, "/ => /");
+
+ // Set up the branch projection ...
+ branchSourceName = mirrorSourceName;
+ branchWorkspaceName = "Branch Workspace";
+ addProjection("fedSpace", "Branch Projection",
branchSourceName, branchWorkspaceName, "/Aircraft => /Aircraft");
+
+ // Add some data to the mirror source ...
+ Graph source = graphFor(mirrorSourceName, mirrorWorkspaceName);
+
source.importXmlFrom(getClass().getClassLoader().getResource("cars.xml").toURI()).into("/");
+
+ // Add some data to the mirror source ...
+ Graph branch = graphFor(branchSourceName, branchWorkspaceName);
+
branch.importXmlFrom(getClass().getClassLoader().getResource("aircraft.xml").toURI()).into("/");
+ }
+
+ /**
+ * Assert that the node in the federated repository given by the supplied path
represents the same node in the underlying
+ * mirror source given by the same path.
+ *
+ * @param pathToNode the path to the node in the federated repository and in the
underlying source
+ */
+ protected void assertMirrorNode( String pathToNode ) {
+ assertSameNode(pathToNode, pathToNode, mirrorSourceName, mirrorWorkspaceName);
+ }
+
+ /**
+ * Assert that the node in the federated repository given by the supplied path
represents the same node in the underlying
+ * branch source given by the same path.
+ *
+ * @param pathToNode the path to the node in the federated repository and in the
underlying source
+ */
+ protected void assertBranchNode( String pathToNode ) {
+ assertThat(pathToNode.startsWith("/Aircraft"), is(true));
+ assertSameNode(pathToNode, pathToNode, branchSourceName, branchWorkspaceName);
+ }
+
+ /**
+ * Assert that the node does not exist in the federated repository nor in the mirror
source or branch source.
+ *
+ * @param pathToNode the path to the node in the federated repository and in the
underlying source
+ */
+ protected void assertNoNode( String pathToNode ) {
+ assertNoNode(pathToNode, pathToNode, mirrorSourceName, mirrorWorkspaceName);
+ assertNoNode(pathToNode, pathToNode, branchSourceName, branchWorkspaceName);
+ }
+
+ @Test
+ public void shouldListAllFederatedWorkspaces() {
+ Set<String> workspaces = federated.getWorkspaces();
+ assertThat(workspaces.contains("fedSpace"), is(true));
+ assertThat(workspaces.size(), is(1));
+ }
+
+ @Test
+ public void shouldFederateRootNodeFromMirrorAndBranch() {
+ // The root of the federated repository should have the content from the mirror
but should have
+ // an additional child that is the root of the branch ...
+ assertSameNode("/", "/", mirrorSourceName,
mirrorWorkspaceName, "Aircraft");
+ }
+
+ @Test
+ public void shouldFederateNodesInMirrorSource() {
+ assertMirrorNode("/Cars");
+ assertMirrorNode("/Cars/Hybrid");
+ assertMirrorNode("/Cars/Hybrid/Toyota Prius");
+ assertMirrorNode("/Cars/Hybrid/Toyota Highlander");
+ assertMirrorNode("/Cars/Hybrid/Nissan Altima");
+ assertMirrorNode("/Cars/Sports/Aston Martin DB9");
+ assertMirrorNode("/Cars/Sports/Infiniti G37");
+ assertMirrorNode("/Cars/Luxury/Cadillac DTS");
+ assertMirrorNode("/Cars/Luxury/Bentley Continental");
+ assertMirrorNode("/Cars/Luxury/Lexus IS350");
+ assertMirrorNode("/Cars/Utility/Land Rover LR2");
+ assertMirrorNode("/Cars/Utility/Land Rover LR3");
+ assertMirrorNode("/Cars/Utility/Hummer H3");
+ assertMirrorNode("/Cars/Utility/Ford F-150");
+ }
+
+ @Test
+ public void shouldFederateNodesInBranchSource() {
+ assertBranchNode("/Aircraft");
+ assertBranchNode("/Aircraft/Business");
+ assertBranchNode("/Aircraft/Business/Gulfstream V");
+ assertBranchNode("/Aircraft/Business/Learjet 45");
+ }
+
+ @Test
+ public void shouldCreateNodeUnderRootInMirrorSource() {
+ federated.createAt("/Hovercraft").with("prop1",
"value1").and();
+ assertMirrorNode("/Hovercraft");
+ // And make sure the parent node is the same ...
+ assertSameNode("/", "/", mirrorSourceName,
mirrorWorkspaceName, "Aircraft");
+ }
+
+ @Test
+ public void shouldCreateNodeWellBelowRootInMirrorSource() {
+ federated.createAt("/Cars/Hybrid/MyNewHybrid").with("prop1",
"value1").and();
+ assertMirrorNode("/Cars/Hybrid/MyNewHybrid");
+ // And make sure the parent node is the same ...
+ assertMirrorNode("/Cars/Hybrid");
+ }
+
+ @Test
+ public void shouldCreateNodeUnderRootInBranchSource() {
+ federated.createAt("/Aircraft/Hovercraft").with("prop1",
"value1").and();
+ assertBranchNode("/Aircraft/Hovercraft");
+ // And make sure the parent node is the same ...
+ assertBranchNode("/Aircraft");
+ }
+
+ @Test
+ public void shouldCreateNodeWellBelowRootInBranchSource() {
+
federated.createAt("/Aircraft/Business/HondaJet").with("prop1",
"value1").and();
+ assertBranchNode("/Aircraft/Business/HondaJet");
+ // And make sure the parent node is the same ...
+ assertBranchNode("/Aircraft/Business");
+ }
+
+ @Test
+ public void shouldDeleteNodeUnderRootInMirrorSource() {
+ // Create a new node (since there's only one node under the root) ...
+ federated.createAt("/Hovercraft").with("prop1",
"value1").and();
+ assertMirrorNode("/Hovercraft");
+ assertSameNode("/", "/", mirrorSourceName,
mirrorWorkspaceName, "Aircraft");
+ // Now delete it ...
+ federated.delete("/Hovercraft");
+ assertNoNode("/Hovercraft");
+ }
+
+ @Test
+ public void shouldDeleteNodeWellBelowRootInMirrorSource() {
+ federated.delete("/Cars/Luxury/Cadillac DTS");
+ assertNoNode("/Cars/Luxury/Cadillac DTS");
+ assertMirrorNode("/Cars/Luxury/Lexus IS350");
+ assertMirrorNode("/Cars/Luxury");
+ }
+
+ @Test
+ public void shouldDeleteNodeUnderRootInBranchSource() {
+ federated.delete("/Aircraft/Business");
+ assertNoNode("/Aircraft/Business");
+ assertBranchNode("/Aircraft");
+ assertBranchNode("/Aircraft/Commercial");
+ }
+
+ @Test
+ public void shouldDeleteNodeWellBelowRootInBranchSource() {
+ federated.delete("/Aircraft/Business/Learjet 45");
+ assertNoNode("/Aircraft/Business/Learjet 45");
+ assertBranchNode("/Aircraft/Business/Gulfstream V");
+ assertBranchNode("/Aircraft/Business");
+ assertBranchNode("/Aircraft/Commercial");
+ }
+
+ @Test
+ public void shouldDeleteEverythingInMirrorAndOffsetIfDeletingRoot() {
+ federated.delete("/");
+ // The branch node should still exist, since it's a placeholder ...
+ assertThat(federated.getChildren().of("/").size(), is(1));
+ assertBranchNode("/Aircraft");
+ }
+}
Property changes on:
trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/federation/FederatedRepositorySourceUsingMirrorAndBranchProjectionsTest.java
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Added:
trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/federation/FederatedRepositorySourceUsingMirrorProjectionTest.java
===================================================================
---
trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/federation/FederatedRepositorySourceUsingMirrorProjectionTest.java
(rev 0)
+++
trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/federation/FederatedRepositorySourceUsingMirrorProjectionTest.java 2009-05-18
15:36:58 UTC (rev 911)
@@ -0,0 +1,141 @@
+/*
+ * JBoss DNA (
http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership. Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of
+ * individual contributors.
+ *
+ * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
+ * is licensed to you under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * JBoss DNA is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site:
http://www.fsf.org.
+ */
+package org.jboss.dna.graph.connector.federation;
+
+import static org.hamcrest.core.Is.is;
+import static org.junit.Assert.assertThat;
+import java.util.Set;
+import org.jboss.dna.graph.Graph;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * An integration test that verifies the behavior of a {@link FederatedRepositorySource}
configured with a federated workspace
+ * using a single mirror projection to an underlying source.
+ */
+public class FederatedRepositorySourceUsingMirrorProjectionTest extends
AbstractFederatedRepositorySourceIntegrationTest {
+
+ private String mirrorSourceName;
+ private String mirrorWorkspaceName;
+
+ @Override
+ @Before
+ public void beforeEach() throws Exception {
+ super.beforeEach();
+
+ // Set up the projection ...
+ mirrorSourceName = "Mirror Source";
+ mirrorWorkspaceName = "Mirror Workspace";
+ addProjection("fedSpace", "Mirror Projection",
mirrorSourceName, mirrorWorkspaceName, "/ => /");
+
+ // Add some data to the source ...
+ Graph source = graphFor(mirrorSourceName, mirrorWorkspaceName);
+
source.importXmlFrom(getClass().getClassLoader().getResource("cars.xml").toURI()).into("/");
+ }
+
+ /**
+ * Assert that the node in the federated repository given by the supplied path
represents the same node in the underlying
+ * mirror source given by the same path.
+ *
+ * @param pathToNode the path to the node in the federated repository and in the
underlying source
+ */
+ protected void assertSameNode( String pathToNode ) {
+ assertSameNode(pathToNode, pathToNode, mirrorSourceName, mirrorWorkspaceName);
+ }
+
+ /**
+ * Assert that the node in the source repository given by the supplied path does not
exist in the source or in the federated
+ * repository.
+ *
+ * @param pathToNode the path to the node in the federated repository and in the
underlying source
+ */
+ protected void assertNoNode( String pathToNode ) {
+ assertNoNode(pathToNode, pathToNode, mirrorSourceName, mirrorWorkspaceName);
+ }
+
+ @Test
+ public void shouldListAllFederatedWorkspaces() {
+ Set<String> workspaces = federated.getWorkspaces();
+ assertThat(workspaces.contains("fedSpace"), is(true));
+ assertThat(workspaces.size(), is(1));
+ }
+
+ @Test
+ public void shouldFederateNodesInMirrorSource() {
+ assertSameNode("/");
+ assertSameNode("/Cars");
+ assertSameNode("/Cars/Hybrid");
+ assertSameNode("/Cars/Hybrid/Toyota Prius");
+ assertSameNode("/Cars/Hybrid/Toyota Highlander");
+ assertSameNode("/Cars/Hybrid/Nissan Altima");
+ assertSameNode("/Cars/Sports/Aston Martin DB9");
+ assertSameNode("/Cars/Sports/Infiniti G37");
+ assertSameNode("/Cars/Luxury/Cadillac DTS");
+ assertSameNode("/Cars/Luxury/Bentley Continental");
+ assertSameNode("/Cars/Luxury/Lexus IS350");
+ assertSameNode("/Cars/Utility/Land Rover LR2");
+ assertSameNode("/Cars/Utility/Land Rover LR3");
+ assertSameNode("/Cars/Utility/Hummer H3");
+ assertSameNode("/Cars/Utility/Ford F-150");
+ }
+
+ @Test
+ public void shouldCreateNodeUnderRootInMirrorSource() {
+ federated.createAt("/Hovercraft").with("prop1",
"value1").and();
+ assertSameNode("/Hovercraft");
+ // And make sure the parent node is the same ...
+ assertSameNode("/");
+ }
+
+ @Test
+ public void shouldCreateNodeWellBelowRootInMirrorSource() {
+ federated.createAt("/Cars/Hybrid/MyNewHybrid").with("prop1",
"value1").and();
+ assertSameNode("/Cars/Hybrid/MyNewHybrid");
+ // And make sure the parent node is the same ...
+ assertSameNode("/Cars/Hybrid");
+ }
+
+ @Test
+ public void shouldDeleteNodeUnderRootFromMirrorSource() {
+ // Create a node that we can delete ...
+ federated.createAt("/Hovercraft").with("prop1",
"value1").and();
+ assertSameNode("/Hovercraft");
+ federated.delete("/Hovercraft");
+ assertNoNode("/Hovercraft");
+ // And make sure the parent node is the same ...
+ assertSameNode("/");
+ // Delete the cars node (which is everything) ...
+ federated.delete("/Cars");
+ // And make sure the parent node is the same ...
+ assertSameNode("/");
+ }
+
+ @Test
+ public void shouldDeleteNodeWellBelowRootFromMirrorSource() {
+ federated.delete("/Cars/Luxury/Cadillac DTS");
+ assertNoNode("/Cars/Luxury/Cadillac DTS");
+ // And make sure the parent node is the same ...
+ assertSameNode("/Cars/Luxury");
+ }
+}
Property changes on:
trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/federation/FederatedRepositorySourceUsingMirrorProjectionTest.java
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Added:
trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/federation/FederatedRepositorySourceUsingOffsetProjectionTest.java
===================================================================
---
trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/federation/FederatedRepositorySourceUsingOffsetProjectionTest.java
(rev 0)
+++
trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/federation/FederatedRepositorySourceUsingOffsetProjectionTest.java 2009-05-18
15:36:58 UTC (rev 911)
@@ -0,0 +1,177 @@
+/*
+ * JBoss DNA (
http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership. Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of
+ * individual contributors.
+ *
+ * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
+ * is licensed to you under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * JBoss DNA is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site:
http://www.fsf.org.
+ */
+package org.jboss.dna.graph.connector.federation;
+
+import static org.hamcrest.core.Is.is;
+import static org.junit.Assert.assertThat;
+import java.util.Set;
+import org.jboss.dna.graph.Graph;
+import org.jboss.dna.graph.request.UnsupportedRequestException;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * An integration test that verifies the behavior of a {@link FederatedRepositorySource}
configured with a federated workspace
+ * using a single mirror projection to an underlying source.
+ */
+public class FederatedRepositorySourceUsingOffsetProjectionTest extends
AbstractFederatedRepositorySourceIntegrationTest {
+
+ private String offsetSourceName;
+ private String offsetWorkspaceName;
+
+ @Override
+ @Before
+ public void beforeEach() throws Exception {
+ super.beforeEach();
+
+ // Set up the projection ...
+ offsetSourceName = "Offset Source";
+ offsetWorkspaceName = "Offset Workspace";
+ addProjection("fedSpace", "Offset Projection",
offsetSourceName, offsetWorkspaceName, "/a/b => /");
+
+ // Add some data to the source ...
+ Graph source = graphFor(offsetSourceName, offsetWorkspaceName);
+
source.importXmlFrom(getClass().getClassLoader().getResource("cars.xml").toURI()).into("/");
+ }
+
+ /**
+ * Assert that the node in the source repository given by the supplied path
represents the equivalent offset node in the
+ * federated repository given by a path offset from the supplied path.
+ *
+ * @param pathToSourceNode the path to the node in the source repository
+ */
+ protected void assertSameNode( String pathToSourceNode ) {
+ String pathToFedNode = "/a/b" + pathToSourceNode;
+ assertSameNode(pathToFedNode, pathToSourceNode, offsetSourceName,
offsetWorkspaceName);
+ }
+
+ /**
+ * Assert that the node in the source repository given by the supplied path does not
exist in the source and that the
+ * equivalent offset node in the federated repository also does not exist.
+ *
+ * @param pathToSourceNode the path to the node in the source repository
+ */
+ protected void assertNoNode( String pathToSourceNode ) {
+ String pathToFedNode = "/a/b" + pathToSourceNode;
+ assertNoNode(pathToFedNode, pathToSourceNode, offsetSourceName,
offsetWorkspaceName);
+ }
+
+ @Test
+ public void shouldListAllFederatedWorkspaces() {
+ Set<String> workspaces = federated.getWorkspaces();
+ assertThat(workspaces.contains("fedSpace"), is(true));
+ assertThat(workspaces.size(), is(1));
+ }
+
+ @Test
+ public void shouldFederateNodesInOffsetSource() {
+ assertSameNode("/");
+ assertSameNode("/Cars");
+ assertSameNode("/Cars/Hybrid");
+ assertSameNode("/Cars/Hybrid/Toyota Prius");
+ assertSameNode("/Cars/Hybrid/Toyota Highlander");
+ assertSameNode("/Cars/Hybrid/Nissan Altima");
+ assertSameNode("/Cars/Sports/Aston Martin DB9");
+ assertSameNode("/Cars/Sports/Infiniti G37");
+ assertSameNode("/Cars/Luxury/Cadillac DTS");
+ assertSameNode("/Cars/Luxury/Bentley Continental");
+ assertSameNode("/Cars/Luxury/Lexus IS350");
+ assertSameNode("/Cars/Utility/Land Rover LR2");
+ assertSameNode("/Cars/Utility/Land Rover LR3");
+ assertSameNode("/Cars/Utility/Hummer H3");
+ assertSameNode("/Cars/Utility/Ford F-150");
+ }
+
+ @Test( expected = UnsupportedRequestException.class )
+ public void shouldNodeAllowCreatingNodeWithinOffset() {
+ // This is not below the offset '/a/b' and should therefore fail ...
+ federated.createAt("/a/Hovercraft").with("prop1",
"value1").and();
+ }
+
+ @Test
+ public void shouldCreateNodeUnderRootInOffsetSource() {
+ // Create the node below the offset ...
+ federated.createAt("/a/b/Hovercraft").with("prop1",
"value1").and();
+ // Verify it is the same from the federation and source ...
+ assertSameNode("/Hovercraft");
+ // And make sure the parent node is the same ...
+ assertSameNode("/");
+ }
+
+ @Test
+ public void shouldCreateNodeWellBelowRootInOffsetSource() {
+ // Create the node below the offset ...
+
federated.createAt("/a/b/Cars/Hybrid/MyNewHybrid").with("prop1",
"value1").and();
+ // Verify it is the same from the federation and source ...
+ assertSameNode("/Cars/Hybrid/MyNewHybrid");
+ // And make sure the parent node is the same ...
+ assertSameNode("/Cars/Hybrid");
+ }
+
+ @Test
+ public void shouldAllowDeletingNodeAtBottomOfOffset() {
+ // This is not below the offset '/a/b' and should therefore fail ...
+ federated.delete("/a/b");
+ // This deletes everything, and recreates /a/b (since it is a placeholder) ...
+ assertSameNode("/");
+ assertThat(federated.getChildren().of("/a/b").size(), is(0));
+ }
+
+ @Test
+ public void shouldNodeAllowDeletingNodeWithinOffset() {
+ federated.delete("/a");
+ assertSameNode("/");
+ // All nodes should be removed, but the placeholders should remain ...
+ assertThat(federated.getChildren().of("/a").size(), is(1));
+ assertThat(federated.getChildren().of("/a/b").size(), is(0));
+ }
+
+ @Test
+ public void shouldDeleteNodeUnderRootInOffsetSource() {
+ // Create the node below the offset ...
+ federated.createAt("/a/b/Hovercraft").with("prop1",
"value1").and();
+ // Verify it is the same from the federation and source ...
+ assertSameNode("/Hovercraft");
+ // And make sure the parent node is the same ...
+ assertSameNode("/");
+ // Delete the node below the offset ...
+ federated.delete("/a/b/Hovercraft");
+ // Verify it is the same from the federation and source ...
+ assertNoNode("/Hovercraft");
+ assertSameNode("/");
+ // Delete the node below the offset ...
+ federated.delete("/a/b/Cars");
+ // Verify it is the same from the federation and source ...
+ assertSameNode("/");
+ }
+
+ @Test
+ public void shouldDeleteNodeWellBelowRootInOffsetSource() {
+ // Delete the node below the offset ...
+ federated.delete("/a/b/Cars/Luxury/Cadillac DTS");
+ assertNoNode("/Cars/Luxury/Cadillac DTS");
+ // And make sure the parent node is the same ...
+ assertSameNode("/Cars/Luxury");
+ }
+}
Property changes on:
trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/federation/FederatedRepositorySourceUsingOffsetProjectionTest.java
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Added:
trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/federation/FederatedRepositoryTest.java
===================================================================
---
trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/federation/FederatedRepositoryTest.java
(rev 0)
+++
trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/federation/FederatedRepositoryTest.java 2009-05-18
15:36:58 UTC (rev 911)
@@ -0,0 +1,91 @@
+/*
+ * JBoss DNA (
http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership. Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of
+ * individual contributors.
+ *
+ * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
+ * is licensed to you under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * JBoss DNA is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site:
http://www.fsf.org.
+ */
+package org.jboss.dna.graph.connector.federation;
+
+import static org.hamcrest.core.Is.is;
+import static org.hamcrest.core.IsSame.sameInstance;
+import static org.junit.Assert.assertThat;
+import static org.mockito.Mockito.stub;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import org.jboss.dna.graph.cache.CachePolicy;
+import org.jboss.dna.graph.connector.RepositoryConnectionFactory;
+import org.jboss.dna.graph.request.InvalidWorkspaceException;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.MockitoAnnotations;
+import org.mockito.MockitoAnnotations.Mock;
+
+/**
+ * @author Randall Hauch
+ */
+public class FederatedRepositoryTest {
+
+ private String name;
+ private FederatedRepository repository;
+ private CachePolicy defaultCachePolicy;
+ private ExecutorService executor;
+ @Mock
+ private FederatedWorkspace workspace1;
+ @Mock
+ private FederatedWorkspace workspace2;
+ @Mock
+ private RepositoryConnectionFactory connectionFactory;
+
+ @Before
+ public void beforeEach() {
+ MockitoAnnotations.initMocks(this);
+ name = "Repository";
+ executor = Executors.newSingleThreadExecutor();
+ stub(workspace1.getName()).toReturn("workspace1");
+ stub(workspace2.getName()).toReturn("workspace2");
+ Collection<FederatedWorkspace> configs = new
ArrayList<FederatedWorkspace>();
+ configs.add(workspace1);
+ configs.add(workspace2);
+ repository = new FederatedRepository(name, connectionFactory, configs,
defaultCachePolicy, executor);
+ }
+
+ @Test
+ public void shouldHaveNameUponCreation() {
+ assertThat(repository.getSourceName(), is(name));
+ }
+
+ @Test
+ public void shouldFindExistingWorkspaces() {
+ assertThat(repository.getWorkspace("workspace1"),
is(sameInstance(workspace1)));
+ assertThat(repository.getWorkspace("workspace2"),
is(sameInstance(workspace2)));
+ }
+
+ @Test
+ public void shouldFindDefaultWorkspaceIfThereIsOne() {
+ assertThat(repository.getWorkspace(null), is(sameInstance(workspace1)));
+ }
+
+ @Test( expected = InvalidWorkspaceException.class )
+ public void shouldNotFindWorkspaceThatDoesNotExist() {
+ repository.getWorkspace("non-existant");
+ }
+}
Property changes on:
trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/federation/FederatedRepositoryTest.java
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Added:
trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/federation/FederatedRequestTest.java
===================================================================
---
trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/federation/FederatedRequestTest.java
(rev 0)
+++
trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/federation/FederatedRequestTest.java 2009-05-18
15:36:58 UTC (rev 911)
@@ -0,0 +1,201 @@
+/*
+ * JBoss DNA (
http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership. Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of
+ * individual contributors.
+ *
+ * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
+ * is licensed to you under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * JBoss DNA is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site:
http://www.fsf.org.
+ */
+package org.jboss.dna.graph.connector.federation;
+
+import static org.hamcrest.core.Is.is;
+import static org.hamcrest.core.IsNull.notNullValue;
+import static org.hamcrest.core.IsNull.nullValue;
+import static org.hamcrest.core.IsSame.sameInstance;
+import static org.junit.Assert.assertThat;
+import static org.mockito.Mockito.mock;
+import java.util.concurrent.CountDownLatch;
+import org.jboss.dna.common.statistic.Stopwatch;
+import org.jboss.dna.graph.connector.federation.FederatedRequest.ProjectedRequest;
+import org.jboss.dna.graph.request.Request;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.MockitoAnnotations;
+import org.mockito.MockitoAnnotations.Mock;
+
+/**
+ *
+ */
+public class FederatedRequestTest {
+
+ private FederatedRequest request;
+ @Mock
+ private Request original;
+ private Projection[] projection;
+ private Request[] projectedRequest;
+
+ @Before
+ public void beforeEach() {
+ MockitoAnnotations.initMocks(this);
+ request = new FederatedRequest(original);
+ projection = new Projection[] {mock(Projection.class), mock(Projection.class),
mock(Projection.class)};
+ projectedRequest = new Request[] {mock(Request.class), mock(Request.class),
mock(Request.class)};
+ }
+
+ @Test
+ public void shouldHaveOriginalRequest() {
+ assertThat(request.original(), is(sameInstance(original)));
+ }
+
+ @Test
+ public void shouldAddFirstProjectedRequest() {
+ request.add(projectedRequest[0], false, false, projection[0]);
+ assertThat(request.hasIncompleteRequests(), is(true));
+ assertThat(request.getFirstProjectedRequest().getProjection(),
is(sameInstance(projection[0])));
+ assertThat(request.getFirstProjectedRequest().getRequest(),
is(sameInstance(projectedRequest[0])));
+ assertThat(request.getFirstProjectedRequest().hasNext(), is(false));
+ assertThat(request.getFirstProjectedRequest().next(), is(nullValue()));
+ }
+
+ @Test
+ public void shouldAddMultipleProjectedRequests() {
+ request.add(projectedRequest[0], false, false, projection[0]);
+ request.add(projectedRequest[1], false, true, projection[1]);
+ request.add(projectedRequest[2], false, false, projection[2]);
+ assertThat(request.hasIncompleteRequests(), is(true));
+
+ ProjectedRequest first = request.getFirstProjectedRequest();
+ assertThat(first.getProjection(), is(sameInstance(projection[0])));
+ assertThat(first.getRequest(), is(sameInstance(projectedRequest[0])));
+ assertThat(first.isComplete(), is(false));
+ assertThat(first.hasNext(), is(true));
+ assertThat(first.next(), is(notNullValue()));
+
+ ProjectedRequest second = first.next();
+ assertThat(second.getProjection(), is(sameInstance(projection[1])));
+ assertThat(second.getRequest(), is(sameInstance(projectedRequest[1])));
+ assertThat(second.isComplete(), is(true));
+ assertThat(second.hasNext(), is(true));
+ assertThat(second.next(), is(notNullValue()));
+
+ ProjectedRequest third = second.next();
+ assertThat(third.getProjection(), is(sameInstance(projection[2])));
+ assertThat(third.getRequest(), is(sameInstance(projectedRequest[2])));
+ assertThat(third.isComplete(), is(false));
+ assertThat(third.hasNext(), is(false));
+ assertThat(third.next(), is(nullValue()));
+ }
+
+ @Test
+ public void shouldAddMultipleProjectedRequestsAddedInMethodChain() {
+ request.add(projectedRequest[0], false, false,
projection[0]).add(projectedRequest[1], false, true,
projection[1]).add(projectedRequest[2],
+
false,
+
false,
+
projection[2]);
+ assertThat(request.hasIncompleteRequests(), is(true));
+
+ ProjectedRequest first = request.getFirstProjectedRequest();
+ assertThat(first.getProjection(), is(sameInstance(projection[0])));
+ assertThat(first.getRequest(), is(sameInstance(projectedRequest[0])));
+ assertThat(first.isComplete(), is(false));
+ assertThat(first.hasNext(), is(true));
+ assertThat(first.next(), is(notNullValue()));
+
+ ProjectedRequest second = first.next();
+ assertThat(second.getProjection(), is(sameInstance(projection[1])));
+ assertThat(second.getRequest(), is(sameInstance(projectedRequest[1])));
+ assertThat(second.isComplete(), is(true));
+ assertThat(second.hasNext(), is(true));
+ assertThat(second.next(), is(notNullValue()));
+
+ ProjectedRequest third = second.next();
+ assertThat(third.getProjection(), is(sameInstance(projection[2])));
+ assertThat(third.getRequest(), is(sameInstance(projectedRequest[2])));
+ assertThat(third.isComplete(), is(false));
+ assertThat(third.hasNext(), is(false));
+ assertThat(third.next(), is(nullValue()));
+ }
+
+ @Test
+ public void
shouldCreateCountDownLatchUponFreezeIfNumberOfIncompleteProjectedRequestsIsNonZero() {
+ request.add(projectedRequest[0], false, false, projection[0]);
+ assertThat(request.getLatch(), is(nullValue()));
+ request.freeze();
+ assertThat(request.getLatch(), is(notNullValue()));
+ assertThat(request.getLatch().getCount(), is(1L));
+ assertThat(request.hasIncompleteRequests(), is(true));
+ }
+
+ @Test
+ public void
shouldNotCreateCountDownLatchUponFreezeIfNumberOfIncompleteProjectedRequestsIsZero() {
+ request.add(projectedRequest[0], false, true, projection[0]);
+ assertThat(request.getLatch(), is(nullValue()));
+ request.freeze();
+ assertThat(request.getLatch(), is(nullValue()));
+ assertThat(request.hasIncompleteRequests(), is(false));
+ }
+
+ @Test
+ public void shouldNotBlockWhenAwaitingOnRequestWithNoIncompleteRequests() throws
InterruptedException {
+ request.add(projectedRequest[0], false, true, projection[0]);
+ request.freeze();
+ assertThat(request.hasIncompleteRequests(), is(false));
+ request.await();
+ }
+
+ @Test
+ public void shouldBlockWhenAwaitingOnRequestWithIncompleteRequests() throws
InterruptedException {
+ request.add(projectedRequest[0], false, false, projection[0]);
+ request.add(projectedRequest[1], false, false, projection[1]);
+ request.add(projectedRequest[2], false, false, projection[2]);
+ request.freeze();
+ assertThat(request.hasIncompleteRequests(), is(true));
+ CountDownLatch latch = request.getLatch();
+ assertThat(latch.getCount(), is(3L));
+ Stopwatch sw = new Stopwatch();
+ sw.start();
+ new Thread(new CountDownRunnable(latch, 100L)).start();
+ request.await(); // this blocks until the latch reaches 0
+ assertThat(latch.getCount(), is(0L));
+ sw.stop();
+ assertThat(sw.getTotalDuration().getDurationInMilliseconds().intValue() >=
250, is(true));
+ }
+
+ protected static class CountDownRunnable implements Runnable {
+ private final CountDownLatch latch;
+ private final long sleepTimeInMillis;
+
+ protected CountDownRunnable( CountDownLatch latch,
+ long sleepTimeInMillis ) {
+ assert latch != null;
+ this.latch = latch;
+ this.sleepTimeInMillis = sleepTimeInMillis;
+ }
+
+ public void run() {
+ while (latch.getCount() != 0) {
+ try {
+ Thread.sleep(sleepTimeInMillis);
+ } catch (InterruptedException err) {
+ System.err.println("Error: interrupted while sleeping before
counting down in " + this.getClass().getName());
+ }
+ latch.countDown();
+ }
+ }
+ }
+}
Property changes on:
trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/federation/FederatedRequestTest.java
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Added:
trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/federation/FederatedWorkspaceTest.java
===================================================================
---
trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/federation/FederatedWorkspaceTest.java
(rev 0)
+++
trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/federation/FederatedWorkspaceTest.java 2009-05-18
15:36:58 UTC (rev 911)
@@ -0,0 +1,181 @@
+/*
+ * JBoss DNA (
http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership. Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of
+ * individual contributors.
+ *
+ * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
+ * is licensed to you under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * JBoss DNA is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site:
http://www.fsf.org.
+ */
+package org.jboss.dna.graph.connector.federation;
+
+import static org.hamcrest.core.Is.is;
+import static org.hamcrest.core.IsNull.notNullValue;
+import static org.hamcrest.core.IsSame.sameInstance;
+import static org.junit.Assert.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.stub;
+import java.util.ArrayList;
+import java.util.List;
+import org.jboss.dna.graph.ExecutionContext;
+import org.jboss.dna.graph.cache.CachePolicy;
+import org.jboss.dna.graph.connector.RepositoryContext;
+import org.jboss.dna.graph.connector.federation.Projection.Rule;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.MockitoAnnotations;
+import org.mockito.MockitoAnnotations.Mock;
+
+/**
+ *
+ */
+public class FederatedWorkspaceTest {
+
+ private FederatedWorkspace workspace;
+ private ExecutionContext context;
+ private CachePolicy cachePolicy;
+ private String sourceName;
+ private String workspaceName;
+ private List<Projection> projections;
+ @Mock
+ private RepositoryContext repositoryContext;
+
+ @Before
+ public void beforeEach() {
+ MockitoAnnotations.initMocks(this);
+ sourceName = "federated";
+ workspaceName = "my workspace";
+ context = new ExecutionContext();
+ stub(repositoryContext.getExecutionContext()).toReturn(context);
+ projections = new ArrayList<Projection>();
+ projections.add(new Projection("source1", "workspace1",
false, rule("/a => /a1")));
+ projections.add(new Projection("source2", "workspace2",
false, rule("/a => /a2", "/b => /b")));
+ projections.add(new Projection("source2", "workspace4",
false, rule("/x => /y")));
+ projections.add(new Projection("source3", "workspace3",
false, rule("/c/d/e => /c1")));
+ workspace = new FederatedWorkspace(repositoryContext, sourceName, workspaceName,
projections, cachePolicy);
+ }
+
+ protected Rule[] rule( String... rule ) {
+ Rule[] rules = new Rule[rule.length];
+ for (int i = 0; i != rule.length; ++i) {
+ rules[i] = Projection.fromString(rule[i], context);
+ }
+ return rules;
+ }
+
+ @Test
+ public void shouldHaveSameRepositoryContextPassedIntoConstructor() {
+ assertThat(workspace.getRepositoryContext(),
is(sameInstance(repositoryContext)));
+ }
+
+ @Test
+ public void shouldHaveSameSourceNamePassedIntoConstructor() {
+ assertThat(workspace.getSourceName(), is(sameInstance(sourceName)));
+ }
+
+ @Test
+ public void shouldHaveSameWorkspaceNamePassedIntoConstructor() {
+ assertThat(workspace.getName(), is(sameInstance(workspaceName)));
+ }
+
+ @Test
+ public void shouldHaveSameCachePolicyPassedIntoContructor() {
+ assertThat(workspace.getCachePolicy(), is(sameInstance(cachePolicy)));
+ }
+
+ @Test
+ public void shouldHaveSameProjectionsPassedIntoConstructor() {
+ assertThat(workspace.getProjections(), is(projections));
+ }
+
+ @Test
+ public void shouldHaveCorrectMappingOfProjectionsBySource() {
+ assertThat(workspace.getProjectionsBySourceName().get("source1"),
is(projections.subList(0, 1)));
+ assertThat(workspace.getProjectionsBySourceName().get("source2"),
is(projections.subList(1, 3)));
+ assertThat(workspace.getProjectionsBySourceName().get("source3"),
is(projections.subList(3, 4)));
+ }
+
+ @Test
+ public void shouldHaveProblemsContainer() {
+ assertThat(workspace.getProblems(), is(notNullValue()));
+ }
+
+ @Test
+ public void shouldCorrectlyDetermineContainmentBySourceAndWorkspaceName() {
+ assertThat(workspace.contains("source1", "workspace1"),
is(true));
+ assertThat(workspace.contains("source2", "workspace2"),
is(true));
+ assertThat(workspace.contains("source2", "workspace4"),
is(true));
+ assertThat(workspace.contains("source3", "workspace3"),
is(true));
+ }
+
+ @Test
+ public void
shouldCorrectlyDetermineNonContainmentOfCaseSensitiveSourceNotInProjections() {
+ assertThat(workspace.contains("source x", "workspace3"),
is(false));
+ assertThat(workspace.contains("Source1", "workspace3"),
is(false));
+ assertThat(workspace.contains("", "workspace3"), is(false));
+ }
+
+ @Test
+ public void
shouldCorrectlyDetermineNonContainmentOfCaseSensitiveWorkspaceNotInProjections() {
+ assertThat(workspace.contains("source1", "Workspace1"),
is(false));
+ assertThat(workspace.contains("source1", "workspace 1"),
is(false));
+ assertThat(workspace.contains("source1", "no workspace"),
is(false));
+ }
+
+ @Test
+ public void
shouldConsiderWorkspacesEqualIfSameSourceNameAndSameWorkspaceNameAndSameProjections() {
+ RepositoryContext otherReposContext = mock(RepositoryContext.class);
+ stub(otherReposContext.getExecutionContext()).toReturn(context);
+ CachePolicy otherPolicy = mock(CachePolicy.class);
+ FederatedWorkspace other = new FederatedWorkspace(otherReposContext, sourceName,
workspaceName, projections, otherPolicy);
+ assertThat(workspace.equals(other), is(true));
+ }
+
+ @Test
+ public void shouldConsiderWorkspacesNotEqualIfDifferentSourceName() {
+ RepositoryContext otherReposContext = mock(RepositoryContext.class);
+ stub(otherReposContext.getExecutionContext()).toReturn(context);
+ CachePolicy otherPolicy = mock(CachePolicy.class);
+ FederatedWorkspace other = new FederatedWorkspace(otherReposContext,
"diff", workspaceName, projections, otherPolicy);
+ assertThat(workspace.equals(other), is(false));
+ }
+
+ @Test
+ public void shouldConsiderWorkspacesNotEqualIfDifferentWorkspaceName() {
+ RepositoryContext otherReposContext = mock(RepositoryContext.class);
+ stub(otherReposContext.getExecutionContext()).toReturn(context);
+ CachePolicy otherPolicy = mock(CachePolicy.class);
+ FederatedWorkspace other = new FederatedWorkspace(otherReposContext, sourceName,
"diff", projections, otherPolicy);
+ assertThat(workspace.equals(other), is(false));
+ }
+
+ @Test
+ public void shouldConsiderWorkspacesNotEqualIfDifferentProjections() {
+ RepositoryContext otherReposContext = mock(RepositoryContext.class);
+ stub(otherReposContext.getExecutionContext()).toReturn(context);
+ CachePolicy otherPolicy = mock(CachePolicy.class);
+ FederatedWorkspace other = new FederatedWorkspace(otherReposContext, sourceName,
workspaceName,
+ projections.subList(0, 3),
otherPolicy);
+ assertThat(workspace.equals(other), is(false));
+ }
+
+ @Test
+ public void shouldDetermineProjection() {
+
+ }
+
+}
Property changes on:
trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/federation/FederatedWorkspaceTest.java
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Added:
trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/federation/ForkRequestProcessorChannelTest.java
===================================================================
---
trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/federation/ForkRequestProcessorChannelTest.java
(rev 0)
+++
trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/federation/ForkRequestProcessorChannelTest.java 2009-05-18
15:36:58 UTC (rev 911)
@@ -0,0 +1,182 @@
+/*
+ * JBoss DNA (
http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership. Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of
+ * individual contributors.
+ *
+ * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
+ * is licensed to you under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * JBoss DNA is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site:
http://www.fsf.org.
+ */
+package org.jboss.dna.graph.connector.federation;
+
+import static org.hamcrest.core.Is.is;
+import static org.hamcrest.core.IsSame.sameInstance;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.fail;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.stub;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import org.jboss.dna.graph.ExecutionContext;
+import org.jboss.dna.graph.connector.MockRepositoryConnection;
+import org.jboss.dna.graph.connector.RepositoryConnection;
+import org.jboss.dna.graph.connector.RepositoryConnectionFactory;
+import org.jboss.dna.graph.connector.federation.ForkRequestProcessor.Channel;
+import org.jboss.dna.graph.request.Request;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.MockitoAnnotations;
+import org.mockito.MockitoAnnotations.Mock;
+
+/**
+ *
+ */
+public class ForkRequestProcessorChannelTest {
+
+ private ExecutionContext context;
+ private String sourceName;
+ private Channel channel;
+ private List<Request> requests;
+ private ExecutorService executor;
+ private LinkedList<Request> executedRequests;
+ private RepositoryConnection connection;
+ @Mock
+ private RepositoryConnectionFactory connectionFactory;
+
+ @Before
+ public void beforeEach() {
+ MockitoAnnotations.initMocks(this);
+ context = new ExecutionContext();
+ sourceName = "SourceA";
+ channel = new ForkRequestProcessor.Channel(sourceName);
+ requests = new ArrayList<Request>();
+ requests.add(mock(Request.class));
+ requests.add(mock(Request.class));
+ requests.add(mock(Request.class));
+ requests.add(mock(Request.class));
+
+ // Create the mock connection ...
+ executedRequests = new LinkedList<Request>(); // this is where requests
submitted to the connection will go
+ connection = new MockRepositoryConnection(sourceName, executedRequests);
+
+ // Stub the connection factory ...
+ stub(connectionFactory.createConnection(sourceName)).toReturn(connection);
+
+ // Create the executor ...
+ executor = Executors.newSingleThreadExecutor();
+ }
+
+ @Test
+ public void shouldCreateEmptyIteratorIfDoneCalledBeforeObtainingIterator() {
+ channel.done();
+ Iterator<Request> iter = channel.createIterator();
+ assertThat(iter.hasNext(), is(false));
+ }
+
+ @Test
+ public void shouldCreateEmptyIteratorIfDoneCalledAfterObtainingIterator() {
+ Iterator<Request> iter = channel.createIterator();
+ channel.done();
+ assertThat(iter.hasNext(), is(false));
+ }
+
+ @Test
+ public void shouldCreateIteratorOverRequestsAddedToChannelAfterObtainingIterator() {
+ Iterator<Request> iter = channel.createIterator();
+ // Add the requests ...
+ for (Request request : requests) {
+ channel.add(request);
+ }
+ // Call done ...
+ channel.done();
+ // Start iterating ...
+ for (Request expected : requests) {
+ assertThat(iter.hasNext(), is(true));
+ assertThat(iter.next(), is(sameInstance(expected)));
+ }
+ assertThat(iter.hasNext(), is(false));
+ }
+
+ @Test
+ public void shouldCreateIteratorOverRequestsAddedToChannelAfterBeginningIteration()
{
+ Iterator<Request> iter = channel.createIterator();
+ // Add the requests in a separate thread ...
+ new Thread(new AddRequestsRunnable(channel, requests, 100)).start();
+ // Start iterating ...
+ for (Request expected : requests) {
+ assertThat(iter.hasNext(), is(true)); // blocks
+ assertThat(iter.next(), is(sameInstance(expected)));
+ }
+ assertThat(iter.hasNext(), is(false));
+ }
+
+ @Test
+ public void shouldSubmitRequestsToConnection() throws Exception {
+ // Start the channel ...
+ channel.start(executor, context, connectionFactory);
+
+ // Submit the requests to the channel ...
+ for (Request request : requests) {
+ channel.add(request);
+ }
+
+ // Mark the channel as done ...
+ channel.done();
+
+ // Wait until the channel has completed ...
+ channel.await();
+
+ // Verify that all the requests to the channel were processed ...
+ Iterator<Request> iter = executedRequests.iterator();
+ for (Request expected : requests) {
+ assertThat(iter.hasNext(), is(true));
+ assertThat(iter.next(), is(sameInstance(expected)));
+ }
+ assertThat(iter.hasNext(), is(false));
+ }
+
+ protected static class AddRequestsRunnable implements Runnable {
+ private final Channel channel;
+ private final Iterator<Request> requests;
+ private final int intervalInMillis;
+
+ protected AddRequestsRunnable( Channel channel,
+ List<Request> requests,
+ int intervalInMillis ) {
+ this.requests = requests.iterator();
+ this.intervalInMillis = intervalInMillis;
+ this.channel = channel;
+ }
+
+ public void run() {
+ while (requests.hasNext()) {
+ try {
+ Thread.sleep(intervalInMillis);
+ } catch (InterruptedException err) {
+ fail(err.getMessage());
+ }
+ channel.add(requests.next());
+ }
+ // Call done ...
+ channel.done();
+ }
+ }
+}
Property changes on:
trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/federation/ForkRequestProcessorChannelTest.java
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Added:
trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/federation/ForkRequestProcessorTest.java
===================================================================
---
trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/federation/ForkRequestProcessorTest.java
(rev 0)
+++
trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/federation/ForkRequestProcessorTest.java 2009-05-18
15:36:58 UTC (rev 911)
@@ -0,0 +1,365 @@
+/*
+ * JBoss DNA (
http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership. Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of
+ * individual contributors.
+ *
+ * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
+ * is licensed to you under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * JBoss DNA is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site:
http://www.fsf.org.
+ */
+package org.jboss.dna.graph.connector.federation;
+
+import static org.hamcrest.core.Is.is;
+import static org.hamcrest.core.IsInstanceOf.instanceOf;
+import static org.hamcrest.core.IsNull.nullValue;
+import static org.hamcrest.core.IsSame.sameInstance;
+import static org.junit.Assert.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.stub;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import org.jboss.dna.graph.ExecutionContext;
+import org.jboss.dna.graph.Location;
+import org.jboss.dna.graph.connector.MockRepositoryConnection;
+import org.jboss.dna.graph.connector.RepositoryConnectionFactory;
+import org.jboss.dna.graph.connector.federation.Projection.Rule;
+import org.jboss.dna.graph.property.DateTime;
+import org.jboss.dna.graph.property.Name;
+import org.jboss.dna.graph.property.Path;
+import org.jboss.dna.graph.property.PathNotFoundException;
+import org.jboss.dna.graph.property.Property;
+import org.jboss.dna.graph.property.Path.Segment;
+import org.jboss.dna.graph.request.InvalidWorkspaceException;
+import org.jboss.dna.graph.request.ReadNodeRequest;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.MockitoAnnotations;
+import org.mockito.MockitoAnnotations.Mock;
+
+/**
+ *
+ */
+public class ForkRequestProcessorTest {
+
+ private ForkRequestProcessor processor;
+ private ExecutionContext context;
+ private DateTime now;
+ private String sourceName;
+ private String workspaceName;
+ private String sourceNameA;
+ private String sourceNameB;
+ private String sourceNameC;
+ private String workspaceNameA;
+ private String workspaceNameB;
+ private String workspaceNameC;
+ private String nonExistantWorkspaceName;
+ private LinkedList<FederatedRequest> federatedRequests;
+ private ExecutorService executor;
+ @Mock
+ private FederatedWorkspace workspace;
+ @Mock
+ private FederatedRepository repository;
+ @Mock
+ private RepositoryConnectionFactory connectionFactory;
+ private Projection projectionA;
+ private Projection projectionB;
+ // private Projection projectionC;
+ private MockRepositoryConnection connectionForSourceA;
+ private MockRepositoryConnection connectionForSourceB;
+ private MockRepositoryConnection connectionForSourceC;
+ private Map<Name, Property> properties;
+ private List<Location> children;
+
+ @Before
+ public void beforeEach() {
+ MockitoAnnotations.initMocks(this);
+ executor = Executors.newSingleThreadExecutor();
+ sourceName = "MySource";
+ workspaceName = "MyWorkspace";
+ sourceNameA = "SourceA";
+ sourceNameB = "SourceB";
+ sourceNameC = "SourceC";
+ workspaceNameA = "WorkspaceA";
+ workspaceNameB = "WorkspaceB";
+ workspaceNameC = "WorkspaceC";
+ nonExistantWorkspaceName = "Non-Existant Workspace";
+ context = new ExecutionContext();
+ now = context.getValueFactories().getDateFactory().create();
+ federatedRequests = new LinkedList<FederatedRequest>();
+ children = new ArrayList<Location>();
+ properties = new HashMap<Name, Property>();
+
+ // Set up the connection factory and connection ...
+ connectionForSourceA = new MockRepositoryConnection(sourceNameA);
+ connectionForSourceB = new MockRepositoryConnection(sourceNameB);
+ connectionForSourceC = new MockRepositoryConnection(sourceNameC);
+
stub(connectionFactory.createConnection(sourceNameA)).toReturn(connectionForSourceA);
+
stub(connectionFactory.createConnection(sourceNameB)).toReturn(connectionForSourceB);
+
stub(connectionFactory.createConnection(sourceNameC)).toReturn(connectionForSourceC);
+
+ // Stub the FederatedRepository ...
+ stub(repository.getSourceName()).toReturn(sourceName);
+ stub(repository.getWorkspace(workspaceName)).toReturn(workspace);
+ stub(repository.getExecutor()).toReturn(executor);
+ stub(repository.getConnectionFactory()).toReturn(connectionFactory);
+ stub(repository.getWorkspace(nonExistantWorkspaceName)).toThrow(new
InvalidWorkspaceException());
+
+ // Stub the FederatedWorkspace ...
+ stub(workspace.getName()).toReturn(workspaceName);
+ // workspace.project(context,location) needs to be stubbed ...
+
+ // Set up the projections ...
+ projectionA = new Projection(sourceNameA, workspaceNameA, false, rules("/a
=> /"));
+ projectionB = new Projection(sourceNameB, workspaceNameB, false, rules("/b
=> /"));
+ // projectionC = new Projection(sourceNameC, workspaceNameC, rules("/c =>
/"));
+
+ // Now set up the processor ...
+ processor = new ForkRequestProcessor(repository, context, now,
federatedRequests);
+ }
+
+ protected Rule[] rules( String... rule ) {
+ Rule[] rules = new Rule[rule.length];
+ for (int i = 0; i != rule.length; ++i) {
+ rules[i] = Projection.fromString(rule[i], context);
+ }
+ return rules;
+ }
+
+ public Name name( String name ) {
+ return context.getValueFactories().getNameFactory().create(name);
+ }
+
+ public Path path( String path ) {
+ return context.getValueFactories().getPathFactory().create(path);
+ }
+
+ public Segment segment( String segment ) {
+ return context.getValueFactories().getPathFactory().createSegment(segment);
+ }
+
+ public Location location( String path ) {
+ return Location.create(path(path));
+ }
+
+ public void addProperty( String name,
+ Object... values ) {
+ Property property = context.getPropertyFactory().create(name(name), values);
+ properties.put(property.getName(), property);
+ }
+
+ public void addChild( Location parent,
+ String childName ) {
+ Path path = context.getValueFactories().getPathFactory().create(parent.getPath(),
segment(childName));
+ children.add(Location.create(path));
+ }
+
+ @Test
+ public void shouldReturnImmediatelyFromAwaitIfNoChannelsAreMade() throws Exception {
+ processor.await();
+ }
+
+ @Test
+ public void shouldReturnFromAwaitAfterChannelsAreCompleted() throws Exception {
+ ReadNodeRequest requestA = new ReadNodeRequest(location("/a/some"),
workspaceNameA);
+ ReadNodeRequest requestB = new ReadNodeRequest(location("/b/some"),
workspaceNameB);
+ ReadNodeRequest requestC = new ReadNodeRequest(location("/c/some"),
workspaceNameC);
+ processor.submit(requestA, sourceNameA);
+ processor.submit(requestB, sourceNameB);
+ processor.submit(requestC, sourceNameC);
+ processor.close();
+ processor.await();
+ }
+
+ @Test
+ public void shouldReturnReadableLocation() {
+ Location location = location("/dna:something/jcr:else");
+ String result =
processor.readable(location("/dna:something/jcr:else"));
+ assertThat(result, is(location.getString(context.getNamespaceRegistry())));
+ }
+
+ @Test
+ public void shouldFindFederatedWorkspaceByName() {
+ ReadNodeRequest request = new ReadNodeRequest(location("/some"),
this.workspace.getName());
+ FederatedWorkspace workspace = processor.getWorkspace(request,
request.inWorkspace());
+ assertThat(workspace, is(sameInstance(this.workspace)));
+ }
+
+ @Test
+ public void shouldRecordErrorOnRequestIfFederatedWorkspaceCouldNotBeFoundByName() {
+ ReadNodeRequest request = new ReadNodeRequest(location("/some"),
nonExistantWorkspaceName);
+ FederatedWorkspace workspace = processor.getWorkspace(request,
request.inWorkspace());
+ assertThat(workspace, is(nullValue()));
+ assertThat(request.hasError(), is(true));
+ assertThat(request.getError(), is(instanceOf(InvalidWorkspaceException.class)));
+ }
+
+ @Test
+ public void
shouldSubmitFederatedRequestToQueueIfFederatedRequestHasNoIncompleteRequests() {
+ FederatedRequest request = mock(FederatedRequest.class);
+ stub(request.hasIncompleteRequests()).toReturn(false);
+ processor.submit(request);
+ assertThat(federatedRequests.size(), is(1));
+ assertThat(federatedRequests.get(0), is(sameInstance(request)));
+ }
+
+ @Test
+ public void
shouldSubmitToSourcesTheSingleRequestInFederatedRequestAndAddFederatedRequestToQueue()
throws Exception {
+ ReadNodeRequest original = new ReadNodeRequest(location("/a/some"),
this.workspace.getName());
+ FederatedRequest request = new FederatedRequest(original);
+ // Create the projection and the source request ...
+ ReadNodeRequest sourceRequest = new ReadNodeRequest(location("/some"),
workspaceNameA);
+ request.add(sourceRequest, false, false, projectionA);
+ assertThat(request.getFirstProjectedRequest().getProjection(),
is(sameInstance(projectionA)));
+ assertThat(request.getFirstProjectedRequest().hasNext(), is(false));
+
+ // Submit the federated request ...
+ processor.submit(request);
+ assertThat(federatedRequests.size(), is(1));
+ assertThat(federatedRequests.get(0), is(sameInstance(request)));
+ // Wait for the processor to complete all source channels ...
+ processor.close();
+ processor.await();
+ // The source should have received something like the original
+ assertThat(connectionForSourceA.getProcessedRequests().contains(sourceRequest),
is(true));
+ }
+
+ @Test
+ public void
shouldSubmitToSourcesTheMultipleRequestsInFederatedRequestAndAddFederatedRequestToQueue()
throws Exception {
+ ReadNodeRequest original = new ReadNodeRequest(location("/some"),
this.workspace.getName());
+ FederatedRequest request = new FederatedRequest(original);
+
+ // Create the first projection and the source request ...
+ ReadNodeRequest sourceRequestA = new
ReadNodeRequest(location("/a/some/other"), workspaceNameA);
+ request.add(sourceRequestA, false, false, projectionA);
+ assertThat(request.getFirstProjectedRequest().getProjection(),
is(sameInstance(projectionA)));
+
+ // Create the second projection and the source request ...
+ ReadNodeRequest sourceRequestB = new
ReadNodeRequest(location("/b/some/other"), workspaceNameB);
+ request.add(sourceRequestB, false, false, projectionB);
+ assertThat(request.getFirstProjectedRequest().next().getProjection(),
is(sameInstance(projectionB)));
+
+ assertThat(request.getFirstProjectedRequest().next().hasNext(), is(false));
+
+ // Submit the federated request ...
+ processor.submit(request);
+ assertThat(federatedRequests.size(), is(1));
+ assertThat(federatedRequests.get(0), is(sameInstance(request)));
+ // Wait for the processor to complete all source channels ...
+ processor.close();
+ processor.await();
+ // The source should have received something like the original
+ assertThat(connectionForSourceA.getProcessedRequests().contains(sourceRequestA),
is(true));
+ assertThat(connectionForSourceB.getProcessedRequests().contains(sourceRequestB),
is(true));
+ assertThat(connectionForSourceC.getProcessedRequests().isEmpty(), is(true));
+ }
+
+ //
----------------------------------------------------------------------------------------------------------------
+ // process(ReadNodeRequest)
+ //
----------------------------------------------------------------------------------------------------------------
+
+ @Test
+ public void shouldNotForkReadNodeRequestIfWorkspaceNameIsInvalid() {
+ // Stub the workspace to have no projection ...
+ Location locationInFed = location("/a/b");
+ stub(workspace.project(context, locationInFed, false)).toReturn(null);
+
+ ReadNodeRequest request = new ReadNodeRequest(locationInFed,
nonExistantWorkspaceName);
+ processor.process(request);
+ assertThat(request.hasError(), is(true));
+ assertThat(request.getError(), is(instanceOf(InvalidWorkspaceException.class)));
+ }
+
+ @Test
+ public void shouldNotForkReadNodeRequestIfThereIsNoProjection() {
+ // Stub the workspace to have no projection ...
+ Location locationInFed = location("/a/b");
+ stub(workspace.project(context, locationInFed, false)).toReturn(null);
+
+ ReadNodeRequest request = new ReadNodeRequest(locationInFed, workspaceName);
+ processor.process(request);
+ assertThat(request.hasError(), is(true));
+ assertThat(request.getError(), is(instanceOf(PathNotFoundException.class)));
+ }
+
+ @Test
+ public void shouldSubmitSingleSourceRequestWhenProcessingSingleReadNodeRequest()
throws Exception {
+ // Stub the workspace to have a projection ...
+ Location locationInFed = location("/a/x/y");
+ Location locationInSource = location("/x/y");
+ ProjectedNode projectedNode = new ProxyNode(projectionA, locationInSource,
locationInFed, false);
+ stub(workspace.project(context, locationInFed, false)).toReturn(projectedNode);
+
+ ReadNodeRequest request = new ReadNodeRequest(locationInFed, workspaceName);
+ processor.process(request);
+ assertThat(request.hasError(), is(false));
+
+ // Check that the federated request has the right information ...
+ FederatedRequest fedRequest = federatedRequests.poll();
+ ReadNodeRequest projectedRequest =
(ReadNodeRequest)fedRequest.getFirstProjectedRequest().getRequest();
+ assertThat(projectedRequest.at(), is(locationInSource));
+ assertThat(fedRequest.getFirstProjectedRequest().hasNext(), is(false));
+
+ // Close the processor ...
+ processor.close();
+ processor.await();
+
+ // Verify the source saw the expected read ...
+ ReadNodeRequest sourceRequest =
(ReadNodeRequest)connectionForSourceA.getProcessedRequests().poll();
+ assertThat(sourceRequest.at().getPath(), is(locationInSource.getPath()));
+ assertThat(connectionForSourceB.getProcessedRequests().isEmpty(), is(true));
+ assertThat(connectionForSourceC.getProcessedRequests().isEmpty(), is(true));
+ }
+
+ @Test
+ public void shouldNotSubmitPlaceholderNodesWhenProcessingReadNodeRequest() throws
Exception {
+ // Stub the workspace to have a projection ...
+ Location locationInFed = location("/a/x/y");
+ Location locationInSource = location("/a/x/y");
+ addProperty("propA", "valueA");
+ addProperty("propB", "valueB");
+ addChild(locationInSource, "child1");
+ addChild(locationInSource, "child2");
+ PlaceholderNode projectedNode = new PlaceholderNode(locationInSource, properties,
children);
+ stub(workspace.project(context, locationInFed, false)).toReturn(projectedNode);
+
+ ReadNodeRequest request = new ReadNodeRequest(locationInFed, workspaceName);
+ processor.process(request);
+ assertThat(request.hasError(), is(false));
+
+ // Check that the federated request has the right information ...
+ FederatedRequest fedRequest = federatedRequests.poll();
+ ReadNodeRequest projectedRequest =
(ReadNodeRequest)fedRequest.getFirstProjectedRequest().getRequest();
+ assertThat(projectedRequest.at(), is(locationInFed));
+ assertThat(projectedRequest.getChildren(), is(children));
+ assertThat(projectedRequest.getPropertiesByName(), is(properties));
+ assertThat(fedRequest.getFirstProjectedRequest().hasNext(), is(false));
+
+ // Close the processor ...
+ processor.close();
+ processor.await();
+
+ // Verify that no sources saw a request ...
+ assertThat(connectionForSourceA.getProcessedRequests().isEmpty(), is(true));
+ assertThat(connectionForSourceB.getProcessedRequests().isEmpty(), is(true));
+ assertThat(connectionForSourceC.getProcessedRequests().isEmpty(), is(true));
+ }
+}
Property changes on:
trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/federation/ForkRequestProcessorTest.java
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Copied:
trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/federation/GeneralProjectorTest.java
(from rev 910, trunk/dna-graph/src/main/java/org/jboss/dna/graph/DnaLexicon.java)
===================================================================
---
trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/federation/GeneralProjectorTest.java
(rev 0)
+++
trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/federation/GeneralProjectorTest.java 2009-05-18
15:36:58 UTC (rev 911)
@@ -0,0 +1,62 @@
+/*
+ * JBoss DNA (
http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership. Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of
+ * individual contributors.
+ *
+ * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
+ * is licensed to you under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * JBoss DNA is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site:
http://www.fsf.org.
+ */
+package org.jboss.dna.graph.connector.federation;
+
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ *
+ */
+public class GeneralProjectorTest extends AbstractProjectorTest<GeneralProjector>
{
+
+ private String sourceNameA;
+ private String sourceNameB;
+ private String sourceNameC;
+ private String workspaceNameA;
+ private String workspaceNameB;
+ private String workspaceNameC;
+
+ @Before
+ @Override
+ public void beforeEach() {
+ super.beforeEach();
+ sourceNameA = "Source A";
+ sourceNameB = "Source B";
+ sourceNameC = "Source C";
+ workspaceNameA = "Workspace A";
+ workspaceNameB = "Workspace B";
+ workspaceNameC = "Workspace C";
+ addProjection(sourceNameA, workspaceNameA, "/a => /");
+ addProjection(sourceNameB, workspaceNameB, "/b1/b2 => /");
+ addProjection(sourceNameC, workspaceNameC, "/c1/c2/c3 => /x/y");
+ this.projector = GeneralProjector.with(context, projections);
+ }
+
+ @Test
+ public void should() {
+
+ }
+
+}
Added:
trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/federation/JoinRequestProcessorTest.java
===================================================================
---
trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/federation/JoinRequestProcessorTest.java
(rev 0)
+++
trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/federation/JoinRequestProcessorTest.java 2009-05-18
15:36:58 UTC (rev 911)
@@ -0,0 +1,350 @@
+/*
+ * JBoss DNA (
http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership. Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of
+ * individual contributors.
+ *
+ * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
+ * is licensed to you under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * JBoss DNA is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site:
http://www.fsf.org.
+ */
+package org.jboss.dna.graph.connector.federation;
+
+import static org.hamcrest.core.Is.is;
+import static org.hamcrest.core.IsSame.sameInstance;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.fail;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.stub;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.UUID;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.LinkedBlockingQueue;
+import org.jboss.dna.graph.DnaLexicon;
+import org.jboss.dna.graph.ExecutionContext;
+import org.jboss.dna.graph.Location;
+import org.jboss.dna.graph.cache.CachePolicy;
+import org.jboss.dna.graph.connector.federation.Projection.Rule;
+import org.jboss.dna.graph.property.DateTime;
+import org.jboss.dna.graph.property.Name;
+import org.jboss.dna.graph.property.Path;
+import org.jboss.dna.graph.property.Property;
+import org.jboss.dna.graph.property.Path.Segment;
+import org.jboss.dna.graph.request.ReadNodeRequest;
+import org.jboss.dna.graph.request.Request;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.MockitoAnnotations;
+import org.mockito.MockitoAnnotations.Mock;
+
+/**
+ *
+ */
+public class JoinRequestProcessorTest {
+
+ private JoinRequestProcessor joinProcessor;
+ private ExecutionContext context;
+ private DateTime now;
+ private String sourceName;
+ protected List<Request> unknownRequests;
+ protected BlockingQueue<FederatedRequest> joinQueue;
+ private Projection mirrorProjection;
+ private Projection projectionA;
+ @Mock
+ private FederatedRepository repository;
+ @Mock
+ private CachePolicy cachePolicy;
+
+ @Before
+ public void beforeEach() {
+ MockitoAnnotations.initMocks(this);
+ sourceName = "MySource";
+ stub(repository.getDefaultCachePolicy()).toReturn(cachePolicy);
+ stub(repository.getSourceName()).toReturn(sourceName);
+
+ unknownRequests = new ArrayList<Request>();
+ context = new ExecutionContext();
+ now = context.getValueFactories().getDateFactory().create();
+ joinProcessor = new JoinRequestProcessorWithUnknownHandler(repository, context,
now);
+ joinQueue = new LinkedBlockingQueue<FederatedRequest>();
+
+ // Set up the projections ...
+ mirrorProjection = new Projection("sourceA", "workspaceM",
false, rules("/ => /"));
+ projectionA = new Projection("sourceA", "workspaceA", false,
rules("/a => /"));
+ }
+
+ protected Rule[] rules( String... rule ) {
+ Rule[] rules = new Rule[rule.length];
+ for (int i = 0; i != rule.length; ++i) {
+ rules[i] = Projection.fromString(rule[i], context);
+ }
+ return rules;
+ }
+
+ public Name name( String name ) {
+ return context.getValueFactories().getNameFactory().create(name);
+ }
+
+ public Path path( String path ) {
+ return context.getValueFactories().getPathFactory().create(path);
+ }
+
+ public Segment segment( String segment ) {
+ return context.getValueFactories().getPathFactory().createSegment(segment);
+ }
+
+ public Location location( String path ) {
+ return Location.create(path(path));
+ }
+
+ public Property property( String name,
+ Object... values ) {
+ return context.getPropertyFactory().create(name(name), values);
+ }
+
+ public Location child( Location parent,
+ String childName ) {
+ Path path = context.getValueFactories().getPathFactory().create(parent.getPath(),
segment(childName));
+ return Location.create(path);
+ }
+
+ public static void pause( long millis ) {
+ try {
+ Thread.sleep(100L);
+ } catch (InterruptedException e) {
+ try {
+ fail("Interrupted while sleeping");
+ } finally {
+ Thread.interrupted();
+ }
+ }
+ }
+
+ @Test
+ public void shouldProcessFederatedRequestsUsingIteratable() {
+ List<FederatedRequest> requests = new ArrayList<FederatedRequest>();
+ Request original = mock(Request.class);
+ FederatedRequest request = mock(FederatedRequest.class);
+ stub(request.original()).toReturn(original);
+ requests.add(request);
+ joinProcessor.process(requests);
+ assertThat(joinProcessor.federatedRequest, is(sameInstance(request)));
+ assertThat(unknownRequests.size(), is(1));
+ assertThat(unknownRequests.get(0), is(sameInstance(original)));
+ }
+
+ @Test
+ public void shouldProcessFederatedRequestsUsingBlockingQueue() {
+ Request original = mock(Request.class);
+ // Create the original request, the projection, and the federated request ...
+ FederatedRequest request = new FederatedRequest(original);
+ request.add(original, false, false, projectionA);
+ // Freeze the request ...
+ request.freeze();
+ // And mark it as done by decrementing the latch ...
+ request.getLatch().countDown();
+ assertThat(request.getLatch().getCount(), is(0L));
+ // Create the queue and add the request ...
+ BlockingQueue<FederatedRequest> queue = new
LinkedBlockingQueue<FederatedRequest>();
+ queue.add(request);
+ // Add a terminating request ...
+ queue.add(new NoMoreFederatedRequests());
+ joinProcessor.process(queue);
+ assertThat(joinProcessor.federatedRequest, is(sameInstance(request)));
+ assertThat(unknownRequests.size(), is(1));
+ assertThat(unknownRequests.get(0), is(sameInstance(original)));
+ }
+
+ @Test
+ public void
shouldProcessFederatedRequestsUsingBlockingQueueThatIsTerminatedAfterProcessingBegins() {
+ final Request original = mock(Request.class);
+ final FederatedRequest request = new FederatedRequest(original);
+ Thread thread = new Thread() {
+ @Override
+ public void run() {
+ // Create the original request, the projection, and the federated request
...
+ Projection projection = mock(Projection.class);
+ request.add(original, false, false, projection);
+ // Freeze the request ...
+ request.freeze();
+ // Add the request ...
+ joinQueue.add(request);
+ // Pause ...
+ pause(100L);
+ // And mark it as done by decrementing the latch ...
+ request.getLatch().countDown();
+ assertThat(request.getLatch().getCount(), is(0L));
+ // Pause ...
+ pause(100L);
+ // Add a terminating request ...
+ joinQueue.add(new NoMoreFederatedRequests());
+ }
+ };
+ thread.start();
+ joinProcessor.process(joinQueue);
+ assertThat(joinProcessor.federatedRequest, is(sameInstance(request)));
+ assertThat(unknownRequests.size(), is(1));
+ assertThat(unknownRequests.get(0), is(sameInstance(original)));
+ }
+
+ @Test
+ public void shouldJoinSingleMirroredReadNodeRequest() {
+ // Create the original read node request ...
+ final ReadNodeRequest original = new
ReadNodeRequest(location("/a/b/c"), "workspaceM");
+ // Create a single federated request...
+ final FederatedRequest request = new FederatedRequest(original);
+ // And "fork" the original request ...
+ final ReadNodeRequest projected = new
ReadNodeRequest(location("/a/b/c"), "workspaceM");
+ projected.setActualLocationOfNode(Location.create(projected.at().getPath(),
UUID.randomUUID()));
+ projected.addProperty(property("propA", "valueA"));
+ projected.addProperty(property("propB", "valueB"));
+ projected.addChild(child(projected.getActualLocationOfNode(),
"child1"));
+ projected.addChild(child(projected.getActualLocationOfNode(),
"child2"));
+ request.add(projected, true, false, mirrorProjection);
+ request.freeze();
+ request.getLatch().countDown();
+ joinQueue.add(request);
+ // Add a terminating request and join the request...
+ joinQueue.add(new NoMoreFederatedRequests());
+ joinProcessor.process(joinQueue);
+ // Check the results of the original has the same results of the projected...
+ assertThat(original.getChildren(), is(projected.getChildren()));
+ assertThat(original.getPropertiesByName(), is(projected.getPropertiesByName()));
+ assertThat(original.getActualLocationOfNode(),
is(projected.getActualLocationOfNode()));
+ }
+
+ @Test
+ public void shouldJoinSingleOffsetReadNodeRequest() {
+ // Create the original read node request ...
+ final ReadNodeRequest original = new
ReadNodeRequest(location("/a/b/c"), "workspaceM");
+ // Create a single federated request using the mirror projection...
+ final FederatedRequest request = new FederatedRequest(original);
+ // And "fork" the original request by creating a mirror
+ final ReadNodeRequest projected = new ReadNodeRequest(location("/b/c"),
"workspaceM");
+ projected.setActualLocationOfNode(Location.create(projected.at().getPath(),
UUID.randomUUID()));
+ projected.addProperty(property("propA", "valueA"));
+ projected.addProperty(property("propB", "valueB"));
+ projected.addChild(child(projected.getActualLocationOfNode(),
"child1"));
+ projected.addChild(child(projected.getActualLocationOfNode(),
"child2"));
+ request.add(projected, false, false, projectionA);
+ request.freeze();
+ request.getLatch().countDown();
+ joinQueue.add(request);
+ // Add a terminating request and join the request...
+ joinQueue.add(new NoMoreFederatedRequests());
+ joinProcessor.process(joinQueue);
+ // Check the results of the original has the same results of the projected...
+ assertThat(original.getPropertiesByName(), is(projected.getPropertiesByName()));
+ assertThat(original.getActualLocationOfNode().getPath(),
is(path("/a/b/c")));
+ assertThat(original.getChildren().get(0).getPath(),
is(path("/a/b/c/child1")));
+ assertThat(original.getChildren().get(1).getPath(),
is(path("/a/b/c/child2")));
+ }
+
+ @Test
+ public void shouldJoinMultipleReadNodeRequest() {
+ // Create the original read node request ...
+ final ReadNodeRequest original = new
ReadNodeRequest(location("/a/b/c"), "workspaceM");
+ // Create a single federated request using the mirror projection...
+ final FederatedRequest request = new FederatedRequest(original);
+ // And "fork" the original request into the first source request ...
+ final ReadNodeRequest projected1 = new
ReadNodeRequest(location("/b/c"), "workspaceM");
+ projected1.setActualLocationOfNode(Location.create(projected1.at().getPath(),
UUID.randomUUID()));
+ projected1.addProperty(property("propA", "valueA"));
+ projected1.addProperty(property("propB", "valueB"));
+ projected1.addChild(child(projected1.getActualLocationOfNode(),
"child1"));
+ projected1.addChild(child(projected1.getActualLocationOfNode(),
"child2"));
+ request.add(projected1, false, false, projectionA);
+ // And a second source request ...
+ final ReadNodeRequest projected2 = new
ReadNodeRequest(location("/a/b/c"), "workspaceM");
+ projected2.setActualLocationOfNode(Location.create(projected2.at().getPath(),
UUID.randomUUID()));
+ projected2.addProperty(property("propC", "valueC"));
+ projected2.addProperty(property("propD", "valueD"));
+ projected2.addChild(child(projected2.getActualLocationOfNode(),
"child2"));
+ projected2.addChild(child(projected2.getActualLocationOfNode(),
"child3"));
+ request.add(projected2, true, false, mirrorProjection);
+ request.freeze();
+ request.getLatch().countDown();
+ request.getLatch().countDown();
+ joinQueue.add(request);
+ // Add a terminating request and join the request...
+ joinQueue.add(new NoMoreFederatedRequests());
+ joinProcessor.process(joinQueue);
+ // Check the results of the original has the same results of the projected...
+ assertThat(original.getProperties().containsAll(projected1.getProperties()),
is(true));
+ assertThat(original.getProperties().containsAll(projected2.getProperties()),
is(true));
+ assertThat(original.getActualLocationOfNode().getPath(),
is(path("/a/b/c")));
+ assertThat(original.getActualLocationOfNode().getUuid(),
is(projected1.getActualLocationOfNode().getUuid()));
+
assertThat(original.getActualLocationOfNode().getIdProperty(DnaLexicon.UUID).isMultiple(),
is(true));
+
assertThat(original.getActualLocationOfNode().getIdProperty(DnaLexicon.UUID).getValuesAsArray()[0],
+ is((Object)projected1.getActualLocationOfNode().getUuid()));
+
assertThat(original.getActualLocationOfNode().getIdProperty(DnaLexicon.UUID).getValuesAsArray()[1],
+ is((Object)projected2.getActualLocationOfNode().getUuid()));
+ assertThat(original.getChildren().get(0).getPath(),
is(path("/a/b/c/child1")));
+ assertThat(original.getChildren().get(1).getPath(),
is(path("/a/b/c/child2")));
+ assertThat(original.getChildren().get(2).getPath(),
is(path("/a/b/c/child2[2]")));
+ assertThat(original.getChildren().get(3).getPath(),
is(path("/a/b/c/child3")));
+ }
+
+ @Test
+ public void
shouldCancelFederatedRequestIfOneOfSeveralMultipleReadNodeRequestIsCancelled() {
+ // Create the original read node request ...
+ final ReadNodeRequest original = new
ReadNodeRequest(location("/a/b/c"), "workspaceM");
+ // Create a single federated request using the mirror projection...
+ final FederatedRequest request = new FederatedRequest(original);
+ // And "fork" the original request into the first source request ...
+ final ReadNodeRequest projected1 = new
ReadNodeRequest(location("/b/c"), "workspaceM");
+ projected1.setActualLocationOfNode(Location.create(projected1.at().getPath(),
UUID.randomUUID()));
+ projected1.addProperty(property("propA", "valueA"));
+ projected1.addProperty(property("propB", "valueB"));
+ projected1.addChild(child(projected1.getActualLocationOfNode(),
"child1"));
+ projected1.addChild(child(projected1.getActualLocationOfNode(),
"child2"));
+ request.add(projected1, false, false, projectionA);
+ // And a second source request (that was cancelled)...
+ final ReadNodeRequest projected2 = new
ReadNodeRequest(location("/a/b/c"), "workspaceM");
+ projected2.cancel();
+ request.add(projected2, true, false, mirrorProjection);
+ request.freeze();
+ request.getLatch().countDown();
+ request.getLatch().countDown();
+ joinQueue.add(request);
+ // Add a terminating request and join the request...
+ joinQueue.add(new NoMoreFederatedRequests());
+ joinProcessor.process(joinQueue);
+ // Check the results of the original has the same results of the projected...
+ assertThat(original.getProperties().isEmpty(), is(true));
+ assertThat(original.getChildren().isEmpty(), is(true));
+ assertThat(original.isCancelled(), is(true));
+ }
+
+ /**
+ * A specialization of {@link JoinRequestProcessor} that simply records unknown
request types into
+ * {@link JoinRequestProcessorTest#unknownRequests}.
+ */
+ protected class JoinRequestProcessorWithUnknownHandler extends JoinRequestProcessor
{
+ protected JoinRequestProcessorWithUnknownHandler( FederatedRepository
repository,
+ ExecutionContext context,
+ DateTime now ) {
+ super(repository, context, now);
+ }
+
+ @Override
+ protected void processUnknownRequest( Request request ) {
+ unknownRequests.add(request);
+ }
+ }
+
+}
Property changes on:
trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/federation/JoinRequestProcessorTest.java
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Copied:
trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/federation/MirrorProjectorTest.java
(from rev 910, trunk/dna-graph/src/main/java/org/jboss/dna/graph/DnaLexicon.java)
===================================================================
---
trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/federation/MirrorProjectorTest.java
(rev 0)
+++
trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/federation/MirrorProjectorTest.java 2009-05-18
15:36:58 UTC (rev 911)
@@ -0,0 +1,58 @@
+/*
+ * JBoss DNA (
http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership. Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of
+ * individual contributors.
+ *
+ * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
+ * is licensed to you under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * JBoss DNA is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site:
http://www.fsf.org.
+ */
+package org.jboss.dna.graph.connector.federation;
+
+import static org.hamcrest.core.Is.is;
+import static org.junit.Assert.assertThat;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ *
+ */
+public class MirrorProjectorTest extends AbstractProjectorTest<MirrorProjector> {
+
+ private String sourceName;
+ private String workspaceName;
+
+ @Before
+ @Override
+ public void beforeEach() {
+ super.beforeEach();
+ this.sourceName = "source1";
+ this.workspaceName = "workspace1";
+ addProjection(sourceName, workspaceName, "/ => /");
+ this.projector = MirrorProjector.with(context, projections);
+ }
+
+ @Test
+ public void shouldAlwaysReturnProxyNodeForSuppliedLocation() {
+ ProjectedNode node = projector.project(context, locationA, false);
+ assertThat(node.isProxy(), is(true));
+ ProxyNode proxy = node.asProxy();
+ assertThat(proxy.location(), is(locationA));
+ assertThat(proxy.source(), is(sourceName));
+ assertThat(proxy.workspaceName(), is(workspaceName));
+ }
+}
Added:
trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/federation/OffsetMirrorProjectorTest.java
===================================================================
---
trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/federation/OffsetMirrorProjectorTest.java
(rev 0)
+++
trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/federation/OffsetMirrorProjectorTest.java 2009-05-18
15:36:58 UTC (rev 911)
@@ -0,0 +1,84 @@
+/*
+ * JBoss DNA (
http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership. Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of
+ * individual contributors.
+ *
+ * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
+ * is licensed to you under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * JBoss DNA is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site:
http://www.fsf.org.
+ */
+package org.jboss.dna.graph.connector.federation;
+
+import static org.hamcrest.core.Is.is;
+import static org.junit.Assert.assertThat;
+import org.jboss.dna.graph.Location;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ *
+ */
+public class OffsetMirrorProjectorTest extends
AbstractProjectorTest<OffsetMirrorProjector> {
+
+ private String mirrorSourceName;
+ private String mirrorWorkspaceName;
+
+ @Before
+ @Override
+ public void beforeEach() {
+ super.beforeEach();
+ this.mirrorSourceName = "source1";
+ this.mirrorWorkspaceName = "workspace1";
+ addProjection(mirrorSourceName, mirrorWorkspaceName, "/a/b/c =>
/");
+ this.projector = OffsetMirrorProjector.with(context, projections);
+ }
+
+ protected void assertProjectedIntoMirror( String federatedPath,
+ String pathInSource ) {
+ Location location = location(federatedPath);
+ ProjectedNode node = projector.project(context, location, false);
+ assertThat(node.isProxy(), is(true));
+ ProxyNode proxy = node.asProxy();
+ assertThat(proxy.location().getPath(), is(path(pathInSource)));
+ assertThat(proxy.source(), is(mirrorSourceName));
+ assertThat(proxy.workspaceName(), is(mirrorWorkspaceName));
+ assertThat(proxy.hasNext(), is(false));
+ }
+
+ @Test
+ public void shouldAlwaysReturnProxyNodeForLocationAboveMirrorSource() {
+ assertPlacholderHasChildren("/", "a");
+ assertPlacholderHasChildren("/a", "b");
+ assertPlacholderHasChildren("/a/b", "c");
+ assertProjectedIntoMirror("/a/b/c", "/");
+ }
+
+ @Test
+ public void shouldAlwaysReturnProxyNodeForLocationWithinMirror() {
+ assertProjectedIntoMirror("/a/b/c", "/");
+ assertProjectedIntoMirror("/a/b/c/d", "/d");
+ assertProjectedIntoMirror("/a/b/c/d/e", "/d/e");
+ }
+
+ @Test
+ public void shouldReturnNoProjectedNodeForLocationOffPathToSourceBranch() {
+ assertNoProjectedNodeAt("/a[2]");
+ assertNoProjectedNodeAt("/a/b[2]");
+ assertNoProjectedNodeAt("/a/b/c[2]");
+ }
+
+}
Property changes on:
trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/federation/OffsetMirrorProjectorTest.java
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Copied:
trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/federation/ProjectionParserTest.java
(from rev 910, trunk/dna-graph/src/main/java/org/jboss/dna/graph/DnaLexicon.java)
===================================================================
---
trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/federation/ProjectionParserTest.java
(rev 0)
+++
trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/federation/ProjectionParserTest.java 2009-05-18
15:36:58 UTC (rev 911)
@@ -0,0 +1,43 @@
+/*
+ * JBoss DNA (
http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership. Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of
+ * individual contributors.
+ *
+ * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
+ * is licensed to you under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * JBoss DNA is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site:
http://www.fsf.org.
+ */
+package org.jboss.dna.graph.connector.federation;
+
+import static org.hamcrest.core.Is.is;
+import static org.junit.Assert.assertThat;
+import org.junit.Test;
+
+/**
+ * @author Randall Hauch
+ */
+public class ProjectionParserTest {
+
+ private ProjectionParser parser;
+
+ @Test
+ public void shouldInitializeSingletonWithParserMethods() {
+ parser = ProjectionParser.getInstance();
+ assertThat(parser.getParserMethods().size(), is(1));
+ }
+
+}
Added:
trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/federation/ProjectionPathRuleTest.java
===================================================================
---
trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/federation/ProjectionPathRuleTest.java
(rev 0)
+++
trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/federation/ProjectionPathRuleTest.java 2009-05-18
15:36:58 UTC (rev 911)
@@ -0,0 +1,250 @@
+/*
+ * JBoss DNA (
http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership. Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of
+ * individual contributors.
+ *
+ * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
+ * is licensed to you under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * JBoss DNA is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site:
http://www.fsf.org.
+ */
+package org.jboss.dna.graph.connector.federation;
+
+import static org.hamcrest.core.Is.is;
+import static org.hamcrest.core.IsNull.nullValue;
+import static org.hamcrest.core.IsSame.sameInstance;
+import static org.junit.Assert.assertThat;
+import static org.junit.matchers.JUnitMatchers.hasItems;
+import org.jboss.dna.common.text.TextEncoder;
+import org.jboss.dna.common.text.UrlEncoder;
+import org.jboss.dna.graph.ExecutionContext;
+import org.jboss.dna.graph.property.NamespaceRegistry;
+import org.jboss.dna.graph.property.Path;
+import org.jboss.dna.graph.property.PathFactory;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * @author Randall Hauch
+ */
+public class ProjectionPathRuleTest {
+
+ private ExecutionContext context;
+ private Projection.PathRule rule;
+ private PathFactory pathFactory;
+ private Path repositoryPath;
+ private Path sourcePath;
+ private Path[] validExceptions;
+ private NamespaceRegistry registry;
+ private TextEncoder encoder;
+
+ @Before
+ public void beforeEach() {
+ context = new ExecutionContext();
+ pathFactory = context.getValueFactories().getPathFactory();
+ registry = context.getNamespaceRegistry();
+ encoder = new UrlEncoder();
+ repositoryPath = pathFactory.create("/a/b/c");
+ sourcePath = pathFactory.create("/x/y");
+ validExceptions = new Path[] {pathFactory.create("e/f"),
pathFactory.create("e/g")};
+ rule = new Projection.PathRule(repositoryPath, sourcePath, validExceptions);
+ }
+
+ @Test
+ public void
shouldCreateInstanceWithValidRepositoryPathAndValidSourcePathAndNoExceptions() {
+ rule = new Projection.PathRule(repositoryPath, sourcePath);
+ assertThat(rule.getPathInRepository(), is(sameInstance(repositoryPath)));
+ assertThat(rule.getPathInSource(), is(sameInstance(sourcePath)));
+ assertThat(rule.hasExceptionsToRule(), is(false));
+ }
+
+ @Test
+ public void
shouldCreateInstanceWithValidRepositoryPathAndValidSourcePathAndValidExceptions() {
+ rule = new Projection.PathRule(repositoryPath, sourcePath, validExceptions);
+ assertThat(rule.getPathInRepository(), is(sameInstance(repositoryPath)));
+ assertThat(rule.getPathInSource(), is(sameInstance(sourcePath)));
+ assertThat(rule.hasExceptionsToRule(), is(true));
+ assertThat(rule.getExceptionsToRule(), hasItems(validExceptions));
+ }
+
+ @Test( expected = AssertionError.class )
+ public void
shouldFailToCreateInstanceWithNullRepositoryPathAndValidSourcePathAndNoExceptions() {
+ repositoryPath = null;
+ new Projection.PathRule(repositoryPath, sourcePath);
+ }
+
+ @Test( expected = AssertionError.class )
+ public void
shouldFailToCreateInstanceWithValidRepositoryPathAndNullSourcePathAndNoExceptions() {
+ sourcePath = null;
+ new Projection.PathRule(repositoryPath, sourcePath);
+ }
+
+ @Test( expected = AssertionError.class )
+ public void
shouldFailToCreateInstanceWithValidRepositoryPathAndValidSourcePathAndAbsoluteExceptions()
{
+ Path relativePath = validExceptions[0];
+ Path absolutePath = pathFactory.create("/j/k/l/m");
+ new Projection.PathRule(repositoryPath, sourcePath, relativePath, absolutePath);
+ }
+
+ @Test
+ public void shouldIncludeRepositoryPathsAtPathInRepository() {
+ assertThat(rule.includes(sourcePath), is(true));
+ }
+
+ @Test
+ public void shouldIncludeRepositoryPathsBelowPathInRepositoryThatAreNotExcluded() {
+ assertThat(rule.includes(pathFactory.create(sourcePath, "m")),
is(true));
+ assertThat(rule.includes(pathFactory.create(sourcePath, "m/n")),
is(true));
+ assertThat(rule.includes(pathFactory.create(sourcePath, "o/p")),
is(true));
+ assertThat(rule.includes(pathFactory.create(sourcePath, "e/e")),
is(true));
+ assertThat(rule.includes(pathFactory.create(sourcePath, "e")),
is(true));
+ }
+
+ @Test
+ public void shouldNotIncludeRepositoryPathsBelowPathInRepositoryThatAreExcluded() {
+ assertThat(rule.includes(pathFactory.create(sourcePath, "e/f")),
is(false));
+ assertThat(rule.includes(pathFactory.create(sourcePath, "e/g")),
is(false));
+ assertThat(rule.includes(pathFactory.create(sourcePath, "e/f/g")),
is(false));
+ assertThat(rule.includes(pathFactory.create(sourcePath, "e/g/h")),
is(false));
+ }
+
+ @Test
+ public void shouldNotIncludeRepositoryPathsNotBelowPathInRepository() {
+ assertThat(rule.includes(pathFactory.create("/m/n")), is(false));
+ assertThat(rule.includes(pathFactory.create("/x/y[3]")), is(false));
+ }
+
+ @Test
+ public void shouldProjectRepositoryPathIntoSourcePath() {
+ assertThat(rule.projectPathInRepositoryToPathInSource(repositoryPath,
pathFactory), is(sourcePath));
+ }
+
+ @Test
+ public void shouldProjectPathBelowRepositoryPathIntoPathBelowSourcePath() {
+ Path pathInRepository = pathFactory.create(repositoryPath, "m/n");
+ Path pathInSource = pathFactory.create(sourcePath, "m/n");
+ assertThat(rule.projectPathInRepositoryToPathInSource(pathInRepository,
pathFactory), is(pathInSource));
+
+ pathInRepository = pathFactory.create(repositoryPath, "m");
+ pathInSource = pathFactory.create(sourcePath, "m");
+ assertThat(rule.projectPathInRepositoryToPathInSource(pathInRepository,
pathFactory), is(pathInSource));
+
+ pathInRepository = pathFactory.create(repositoryPath, "m/n[3]");
+ pathInSource = pathFactory.create(sourcePath, "m/n[3]");
+ assertThat(rule.projectPathInRepositoryToPathInSource(pathInRepository,
pathFactory), is(pathInSource));
+ }
+
+ @Test
+ public void shouldProjectSourcePathIntoRepositoryPath() {
+ assertThat(rule.projectPathInSourceToPathInRepository(sourcePath, pathFactory),
is(repositoryPath));
+ }
+
+ @Test
+ public void shouldProjectPathBelowSourcePathIntoPathBelowRepositoryPath() {
+ Path pathInRepository = pathFactory.create(repositoryPath, "m/n");
+ Path pathInSource = pathFactory.create(sourcePath, "m/n");
+ assertThat(rule.projectPathInSourceToPathInRepository(pathInSource, pathFactory),
is(pathInRepository));
+
+ pathInRepository = pathFactory.create(repositoryPath, "m");
+ pathInSource = pathFactory.create(sourcePath, "m");
+ assertThat(rule.projectPathInSourceToPathInRepository(pathInSource, pathFactory),
is(pathInRepository));
+
+ pathInRepository = pathFactory.create(repositoryPath, "m/n[3]");
+ pathInSource = pathFactory.create(sourcePath, "m/n[3]");
+ assertThat(rule.projectPathInSourceToPathInRepository(pathInSource, pathFactory),
is(pathInRepository));
+ }
+
+ @Test
+ public void
shouldGetPathsInRepositoryGivenPathsInSourceAtOrBelowSourcePathIfNotExcluded() {
+ assertThat(rule.getPathInRepository(sourcePath, pathFactory),
is(repositoryPath));
+ assertThatGetPathInRepositoryReturnsCorrectPathInSource("");
+ assertThatGetPathInRepositoryReturnsCorrectPathInSource("m/n");
+ assertThatGetPathInRepositoryReturnsCorrectPathInSource("m[1]");
+ assertThatGetPathInRepositoryReturnsCorrectPathInSource("m[1]/n/o/p");
+ }
+
+ protected void assertThatGetPathInRepositoryReturnsCorrectPathInSource( String
subpath ) {
+ assertThat(rule.getPathInRepository(pathFactory.create(sourcePath, subpath),
pathFactory),
+ is(pathFactory.create(repositoryPath, subpath)));
+ }
+
+ @Test
+ public void
shouldGetNullPathInRepositoryGivenPathsInSourceAtOrBelowSourcePathIfExcluded() {
+ assertThat(rule.getPathInRepository(pathFactory.create(sourcePath,
"e/f"), pathFactory), is(nullValue()));
+ assertThat(rule.getPathInRepository(pathFactory.create(sourcePath,
"e/g"), pathFactory), is(nullValue()));
+ assertThat(rule.getPathInRepository(pathFactory.create(sourcePath,
"e/f/h"), pathFactory), is(nullValue()));
+ assertThat(rule.getPathInRepository(pathFactory.create(sourcePath,
"e/g/h"), pathFactory), is(nullValue()));
+ }
+
+ @Test
+ public void
shouldGetNullPathInRepositoryGivenPathsInRepositoryNotAtOrBelowSourcePath() {
+ assertThat(rule.getPathInRepository(pathFactory.create("/m/n"),
pathFactory), is(nullValue()));
+ }
+
+ @Test
+ public void
shouldGetPathsInSourceGivenPathsInRepositoryAtOrBelowRepositoryPathIfNotExcluded() {
+ assertThat(rule.getPathInSource(repositoryPath, pathFactory), is(sourcePath));
+ assertThatGetPathInSourceReturnsCorrectPathInRepository("");
+ assertThatGetPathInSourceReturnsCorrectPathInRepository("m/n");
+ assertThatGetPathInSourceReturnsCorrectPathInRepository("m[1]");
+ assertThatGetPathInSourceReturnsCorrectPathInRepository("m[1]/n/o/p");
+
+ }
+
+ protected void assertThatGetPathInSourceReturnsCorrectPathInRepository( String
subpath ) {
+ assertThat(rule.getPathInSource(pathFactory.create(repositoryPath, subpath),
pathFactory),
+ is(pathFactory.create(sourcePath, subpath)));
+ }
+
+ @Test
+ public void
shouldGetNullPathInSourceGivenPathsInRepositoryAtOrBelowRepositoryPathIfExcluded() {
+ assertThat(rule.getPathInSource(pathFactory.create(repositoryPath,
"e/f"), pathFactory), is(nullValue()));
+ assertThat(rule.getPathInSource(pathFactory.create(repositoryPath,
"e/g"), pathFactory), is(nullValue()));
+ assertThat(rule.getPathInSource(pathFactory.create(repositoryPath,
"e/f/h"), pathFactory), is(nullValue()));
+ assertThat(rule.getPathInSource(pathFactory.create(repositoryPath,
"e/g/h"), pathFactory), is(nullValue()));
+ }
+
+ @Test
+ public void
shouldGetNullPathInSourceGivenPathsInRepositoryNotAtOrBelowRepositoryPath() {
+ assertThat(rule.getPathInSource(pathFactory.create("/m/n"),
pathFactory), is(nullValue()));
+ }
+
+ @Test
+ public void shouldConvertToString() {
+ assertThat(rule.getString(registry, encoder), is("/a/b/c => /x/y $ e/f $
e/g"));
+
+ repositoryPath = pathFactory.create("/a/b/c");
+ sourcePath = pathFactory.create("/");
+ rule = new Projection.PathRule(repositoryPath, sourcePath, validExceptions);
+ assertThat(rule.getString(registry, encoder), is("/a/b/c => / $ e/f $
e/g"));
+
+ repositoryPath = pathFactory.create("/");
+ sourcePath = pathFactory.create("/");
+ rule = new Projection.PathRule(repositoryPath, sourcePath, validExceptions);
+ assertThat(rule.getString(registry, encoder), is("/ => / $ e/f $
e/g"));
+ }
+
+ @Test
+ public void shouldHaveToString() {
+ assertThat(rule.toString(), is("/{}a/{}b/{}c => /{}x/{}y $ {}e/{}f $
{}e/{}g"));
+
+ repositoryPath = pathFactory.create("/a/b/c");
+ sourcePath = pathFactory.create("/");
+ rule = new Projection.PathRule(repositoryPath, sourcePath, validExceptions);
+ assertThat(rule.toString(), is("/{}a/{}b/{}c => / $ {}e/{}f $
{}e/{}g"));
+ }
+
+}
Property changes on:
trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/federation/ProjectionPathRuleTest.java
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Added:
trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/federation/ProjectionTest.java
===================================================================
---
trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/federation/ProjectionTest.java
(rev 0)
+++
trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/federation/ProjectionTest.java 2009-05-18
15:36:58 UTC (rev 911)
@@ -0,0 +1,290 @@
+/*
+ * JBoss DNA (
http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership. Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of
+ * individual contributors.
+ *
+ * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
+ * is licensed to you under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * JBoss DNA is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site:
http://www.fsf.org.
+ */
+package org.jboss.dna.graph.connector.federation;
+
+import static org.hamcrest.core.Is.is;
+import static org.hamcrest.core.IsInstanceOf.instanceOf;
+import static org.hamcrest.core.IsNull.nullValue;
+import static org.junit.Assert.assertThat;
+import static org.junit.matchers.JUnitMatchers.hasItems;
+import static org.mockito.Mockito.stub;
+import java.util.Set;
+import org.jboss.dna.graph.ExecutionContext;
+import org.jboss.dna.graph.property.Path;
+import org.jboss.dna.graph.property.PathFactory;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.MockitoAnnotations;
+import org.mockito.MockitoAnnotations.Mock;
+
+/**
+ * @author Randall Hauch
+ */
+public class ProjectionTest {
+
+ private ExecutionContext context;
+ private String sourceName;
+ private String workspaceName;
+ private Projection.Rule[] rules;
+ private Projection projection;
+ private PathFactory pathFactory;
+ private boolean readOnly;
+ @Mock
+ private Projection.Rule mockRule1;
+ @Mock
+ private Projection.Rule mockRule2;
+ @Mock
+ private Projection.Rule mockRule3;
+
+ @Before
+ public void beforeEach() {
+ MockitoAnnotations.initMocks(this);
+ context = new ExecutionContext();
+ pathFactory = context.getValueFactories().getPathFactory();
+ sourceName = "Valid name";
+ workspaceName = "Valid workspace";
+ readOnly = false;
+ rules = new Projection.Rule[] {mockRule1, mockRule2, mockRule3};
+ projection = new Projection(sourceName, workspaceName, readOnly, rules);
+ }
+
+ @Test
+ public void shouldCreateInstanceWithValidNameAndValidRules() {
+ projection = new Projection(sourceName, workspaceName, readOnly, rules);
+ assertThat(projection.getSourceName(), is(sourceName));
+ assertThat(projection.getWorkspaceName(), is(workspaceName));
+ assertThat(projection.getRules().size(), is(rules.length));
+ assertThat(projection.getRules(), hasItems(mockRule1, mockRule2, mockRule3));
+ }
+
+ @Test( expected = IllegalArgumentException.class )
+ public void shouldFailToCreateInstanceWithNullNameAndValidRules() {
+ sourceName = null;
+ projection = new Projection(sourceName, workspaceName, readOnly, rules);
+ }
+
+ @Test( expected = IllegalArgumentException.class )
+ public void
shouldFailToCreateInstanceWithNullWorkspaceNameAndValidSourceNameAndRules() {
+ sourceName = null;
+ projection = new Projection(sourceName, workspaceName, readOnly, rules);
+ }
+
+ @Test( expected = IllegalArgumentException.class )
+ public void shouldFailToCreateInstanceWithEmptyNameAndValidRules() {
+ sourceName = "";
+ projection = new Projection(sourceName, workspaceName, readOnly, rules);
+ }
+
+ @Test( expected = IllegalArgumentException.class )
+ public void shouldFailToCreateInstanceWithBlankNameAndValidRules() {
+ sourceName = " \t ";
+ projection = new Projection(sourceName, workspaceName, readOnly, rules);
+ }
+
+ @Test( expected = IllegalArgumentException.class )
+ public void shouldFailToCreateInstanceWithValidNameAndNullRules() {
+ rules = null;
+ projection = new Projection(sourceName, workspaceName, readOnly, rules);
+ }
+
+ @Test( expected = IllegalArgumentException.class )
+ public void shouldFailToCreateInstanceWithValidNameAndEmptyRules() {
+ rules = new Projection.Rule[] {};
+ projection = new Projection(sourceName, workspaceName, readOnly, rules);
+ }
+
+ @Test( expected = IllegalArgumentException.class )
+ public void shouldFailToCreateInstanceWithValidNameAndRulesArrayContainingAllNulls()
{
+ projection = new Projection(sourceName, null, readOnly, null, null);
+ }
+
+ @Test
+ public void
shouldCreateInstanceWithValidNameAndRulesAndShouldPruneNullRuleReferences() {
+ projection = new Projection(sourceName, workspaceName, readOnly, mockRule1, null,
mockRule3);
+ assertThat(projection.getRules().size(), is(2));
+ assertThat(projection.getRules(), hasItems(mockRule1, mockRule3));
+ }
+
+ @Test( expected = IllegalArgumentException.class )
+ public void shouldFailToGetPathsInSourceGivenValidPathAndNullPathFactory() {
+ Path pathInRepository = pathFactory.create("/a/b/c");
+ projection.getPathsInSource(pathInRepository, null);
+ }
+
+ @Test
+ public void shouldGetNoPathsInSourceGivenNullPathInRepository() {
+ Set<Path> pathsInSource = projection.getPathsInSource(null, pathFactory);
+ assertThat(pathsInSource.isEmpty(), is(true));
+ }
+
+ @Test
+ public void shouldGetNoPathsInSourceGivenPathInRepositoryAndNoApplicableRules() {
+ Path pathInRepository = pathFactory.create("/a/b/c");
+ stub(mockRule1.getPathInSource(pathInRepository, pathFactory)).toReturn(null);
+ stub(mockRule2.getPathInSource(pathInRepository, pathFactory)).toReturn(null);
+ stub(mockRule3.getPathInSource(pathInRepository, pathFactory)).toReturn(null);
+ Set<Path> pathsInSource = projection.getPathsInSource(pathInRepository,
pathFactory);
+ assertThat(pathsInSource.isEmpty(), is(true));
+ }
+
+ @Test
+ public void shouldGetPathInSourceGivenPathInRepositoryAndOneApplicableRules() {
+ Path pathInRepository = pathFactory.create("/a/b/c");
+ Path pathInSource = pathFactory.create("/d/e/f");
+ stub(mockRule1.getPathInSource(pathInRepository,
pathFactory)).toReturn(pathInSource);
+ stub(mockRule2.getPathInSource(pathInRepository, pathFactory)).toReturn(null);
+ stub(mockRule3.getPathInSource(pathInRepository, pathFactory)).toReturn(null);
+ Set<Path> pathsInSource = projection.getPathsInSource(pathInRepository,
pathFactory);
+ assertThat(pathsInSource, hasItems(pathInSource));
+ }
+
+ @Test
+ public void shouldGetPathsInSourceGivenPathInRepositoryAndMultipleApplicableRules()
{
+ Path pathInRepository = pathFactory.create("/a/b/c");
+ Path pathInSource1 = pathFactory.create("/d/e/f");
+ Path pathInSource2 = pathFactory.create("/d/e/g");
+ Path pathInSource3 = pathFactory.create("/d/e/h");
+ stub(mockRule1.getPathInSource(pathInRepository,
pathFactory)).toReturn(pathInSource1);
+ stub(mockRule2.getPathInSource(pathInRepository,
pathFactory)).toReturn(pathInSource2);
+ stub(mockRule3.getPathInSource(pathInRepository,
pathFactory)).toReturn(pathInSource3);
+ Set<Path> pathsInSource = projection.getPathsInSource(pathInRepository,
pathFactory);
+ assertThat(pathsInSource, hasItems(pathInSource1, pathInSource2,
pathInSource3));
+ }
+
+ @Test
+ public void
shouldGetPathsInSourceGivenPathInRepositoryAndMultipleApplicableRulesReturningDuplicatePathsInSource()
{
+ Path pathInRepository = pathFactory.create("/a/b/c");
+ Path pathInSource1 = pathFactory.create("/d/e/f");
+ Path pathInSource23 = pathFactory.create("/d/e/g");
+ stub(mockRule1.getPathInSource(pathInRepository,
pathFactory)).toReturn(pathInSource1);
+ stub(mockRule2.getPathInSource(pathInRepository,
pathFactory)).toReturn(pathInSource23);
+ stub(mockRule3.getPathInSource(pathInRepository,
pathFactory)).toReturn(pathInSource23);
+ Set<Path> pathsInSource = projection.getPathsInSource(pathInRepository,
pathFactory);
+ assertThat(pathsInSource, hasItems(pathInSource1, pathInSource23));
+ }
+
+ @Test( expected = IllegalArgumentException.class )
+ public void shouldFailToGetPathsInRepositoryGivenValidPathAndNullPathFactory() {
+ Path pathInSource = pathFactory.create("/a/b/c");
+ projection.getPathsInRepository(pathInSource, null);
+ }
+
+ @Test
+ public void shouldGetNoPathsInRepositoryGivenNullPathInSource() {
+ Set<Path> pathsInRepository = projection.getPathsInRepository(null,
pathFactory);
+ assertThat(pathsInRepository.isEmpty(), is(true));
+ }
+
+ @Test
+ public void shouldGetNoPathsInRepositoryGivenPathInSourceAndNoApplicableRules() {
+ Path pathInSource = pathFactory.create("/d/e/f");
+ stub(mockRule1.getPathInRepository(pathInSource, pathFactory)).toReturn(null);
+ stub(mockRule2.getPathInRepository(pathInSource, pathFactory)).toReturn(null);
+ stub(mockRule3.getPathInRepository(pathInSource, pathFactory)).toReturn(null);
+ Set<Path> pathsInRepository = projection.getPathsInRepository(pathInSource,
pathFactory);
+ assertThat(pathsInRepository.isEmpty(), is(true));
+ }
+
+ @Test
+ public void shouldGetPathInRepositoryGivenPathInSourceAndOneApplicableRules() {
+ Path pathInRepository = pathFactory.create("/a/b/c");
+ Path pathInSource = pathFactory.create("/d/e/f");
+ stub(mockRule1.getPathInRepository(pathInSource,
pathFactory)).toReturn(pathInRepository);
+ stub(mockRule2.getPathInRepository(pathInSource, pathFactory)).toReturn(null);
+ stub(mockRule3.getPathInRepository(pathInSource, pathFactory)).toReturn(null);
+ Set<Path> pathsInRepository = projection.getPathsInRepository(pathInSource,
pathFactory);
+ assertThat(pathsInRepository, hasItems(pathInRepository));
+ }
+
+ @Test
+ public void shouldGetPathsInRepositoryGivenPathInSourceAndMultipleApplicableRules()
{
+ Path pathInSource = pathFactory.create("/a/b/c");
+ Path pathInRepository1 = pathFactory.create("/d/e/f");
+ Path pathInRepository2 = pathFactory.create("/d/e/g");
+ Path pathInRepository3 = pathFactory.create("/d/e/h");
+ stub(mockRule1.getPathInRepository(pathInSource,
pathFactory)).toReturn(pathInRepository1);
+ stub(mockRule2.getPathInRepository(pathInSource,
pathFactory)).toReturn(pathInRepository2);
+ stub(mockRule3.getPathInRepository(pathInSource,
pathFactory)).toReturn(pathInRepository3);
+ Set<Path> pathsInRepository = projection.getPathsInRepository(pathInSource,
pathFactory);
+ assertThat(pathsInRepository, hasItems(pathInRepository1, pathInRepository2,
pathInRepository3));
+ }
+
+ @Test
+ public void
shouldGetPathsInRepositoryGivenPathInSourceAndMultipleApplicableRulesReturningDuplicatePathsInRepository()
{
+ Path pathInSource = pathFactory.create("/a/b/c");
+ Path pathInRepository1 = pathFactory.create("/d/e/f");
+ Path pathInRepository23 = pathFactory.create("/d/e/g");
+ stub(mockRule1.getPathInRepository(pathInSource,
pathFactory)).toReturn(pathInRepository1);
+ stub(mockRule2.getPathInRepository(pathInSource,
pathFactory)).toReturn(pathInRepository23);
+ stub(mockRule3.getPathInRepository(pathInSource,
pathFactory)).toReturn(pathInRepository23);
+ Set<Path> pathsInRepository = projection.getPathsInRepository(pathInSource,
pathFactory);
+ assertThat(pathsInRepository, hasItems(pathInRepository1, pathInRepository23));
+ }
+
+ @Test
+ public void
shouldParsePathRuleFromDefinitionWithNonRootRepositoryPathAndNonRootSourcePath() {
+ Projection.Rule rule = Projection.parsePathRule("/a => /b",
context);
+ assertThat(rule, is(instanceOf(Projection.PathRule.class)));
+ Projection.PathRule pathRule = (Projection.PathRule)rule;
+ assertThat(pathRule.getPathInRepository(),
is(pathFactory.create("/a")));
+ assertThat(pathRule.getPathInSource(), is(pathFactory.create("/b")));
+ }
+
+ @Test
+ public void
shouldParsePathRuleFromDefinitionWithRootRepositoryPathAndNonRootSourcePath() {
+ Projection.Rule rule = Projection.parsePathRule("/ => /b",
context);
+ assertThat(rule, is(instanceOf(Projection.PathRule.class)));
+ Projection.PathRule pathRule = (Projection.PathRule)rule;
+ assertThat(pathRule.getPathInRepository(), is(pathFactory.createRootPath()));
+ assertThat(pathRule.getPathInSource(), is(pathFactory.create("/b")));
+ }
+
+ @Test
+ public void
shouldParsePathRuleFromDefinitionWithNonRootRepositoryPathAndRootSourcePath() {
+ Projection.Rule rule = Projection.parsePathRule("/a => /",
context);
+ assertThat(rule, is(instanceOf(Projection.PathRule.class)));
+ Projection.PathRule pathRule = (Projection.PathRule)rule;
+ assertThat(pathRule.getPathInRepository(),
is(pathFactory.create("/a")));
+ assertThat(pathRule.getPathInSource(), is(pathFactory.createRootPath()));
+ }
+
+ @Test
+ public void
shouldParsePathRuleFromDefinitionWithRootRepositoryPathAndRootSourcePath() {
+ Projection.Rule rule = Projection.parsePathRule("/ => /", context);
+ assertThat(rule, is(instanceOf(Projection.PathRule.class)));
+ Projection.PathRule pathRule = (Projection.PathRule)rule;
+ assertThat(pathRule.getPathInRepository(), is(pathFactory.createRootPath()));
+ assertThat(pathRule.getPathInSource(), is(pathFactory.createRootPath()));
+ }
+
+ @Test
+ public void
shouldNotParsePathRuleFromDefinitionWithRootRepositoryPathAndNoSourcePath() {
+ assertThat(Projection.parsePathRule("/", context), is(nullValue()));
+ }
+
+ @Test
+ public void
shouldNotParsePathRuleFromDefinitionWithNonRootRepositoryPathAndNoSourcePath() {
+ assertThat(Projection.parsePathRule("a/", context), is(nullValue()));
+ }
+}
Property changes on:
trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/federation/ProjectionTest.java
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Added:
trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/federation/ProjectorWithPlaceholdersTest.java
===================================================================
---
trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/federation/ProjectorWithPlaceholdersTest.java
(rev 0)
+++
trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/federation/ProjectorWithPlaceholdersTest.java 2009-05-18
15:36:58 UTC (rev 911)
@@ -0,0 +1,182 @@
+/*
+ * JBoss DNA (
http://www.jboss.org/dna)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership. Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of
+ * individual contributors.
+ *
+ * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
+ * is licensed to you under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * JBoss DNA is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site:
http://www.fsf.org.
+ */
+package org.jboss.dna.graph.connector.federation;
+
+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.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import org.jboss.dna.graph.ExecutionContext;
+import org.jboss.dna.graph.Location;
+import org.jboss.dna.graph.connector.federation.Projection.Rule;
+import org.jboss.dna.graph.property.Path;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ *
+ */
+public class ProjectorWithPlaceholdersTest {
+
+ protected ExecutionContext context;
+ protected List<Projection> projections;
+ protected Collection<PlaceholderNode> placeholders;
+
+ @Before
+ public void beforeEach() {
+ this.context = new ExecutionContext();
+ this.projections = new ArrayList<Projection>();
+ this.placeholders = new ArrayList<PlaceholderNode>();
+ }
+
+ protected Rule[] rule( String... rule ) {
+ Rule[] rules = new Rule[rule.length];
+ for (int i = 0; i != rule.length; ++i) {
+ rules[i] = Projection.fromString(rule[i], context);
+ }
+ return rules;
+ }
+
+ protected Path path( String path ) {
+ return context.getValueFactories().getPathFactory().create(path);
+ }
+
+ protected Path path( Path parent,
+ String relativePath ) {
+ return context.getValueFactories().getPathFactory().create(parent,
relativePath);
+ }
+
+ protected void addProjection( String sourceName,
+ String workspaceName,
+ String... rules ) {
+ projections.add(new Projection(sourceName, workspaceName, false, rule(rules)));
+ }
+
+ protected PlaceholderNode getPlaceholder( Path path ) {
+ for (PlaceholderNode placeholder : placeholders) {
+ if (placeholder.location().getPath().equals(path)) return placeholder;
+ }
+ return null;
+ }
+
+ protected void assertPlacholderHasChildren( String parent,
+ String... childSegments ) {
+ Path parentPath = path(parent);
+ ProjectedNode node = getPlaceholder(parentPath);
+ assertThat(node, is(notNullValue()));
+ assertThat(node.isPlaceholder(), is(true));
+ PlaceholderNode placeholder = node.asPlaceholder();
+ List<Location> locations = new ArrayList<Location>();
+ for (String childSegment : childSegments) {
+ Path childPath = path(parentPath, childSegment);
+ locations.add(Location.create(childPath));
+ }
+ assertThat(placeholder.children(), is(locations));
+ }
+
+ protected void assertNoPlacholder( String parent ) {
+ assertThat(getPlaceholder(path(parent)), is(nullValue()));
+ }
+
+ @Test
+ public void shouldLoadPlaceholderNodesForWorkspaceWithOneProjectionNotAtRoot() {
+ addProjection("source1", "workspace1", "/a/b =>
/c/d");
+ ProjectorWithPlaceholders.loadPlaceholderNodes(context, projections,
placeholders);
+ assertPlacholderHasChildren("/", "a");
+ assertPlacholderHasChildren("/a", "b");
+ assertThat(placeholders.size(), is(2));
+ }
+
+ @Test
+ public void shouldLoadPlaceholderNodesForWorkspaceWithOneProjectionAtRoot() {
+ addProjection("source1", "workspace1", "/ =>
/");
+ ProjectorWithPlaceholders.loadPlaceholderNodes(context, projections,
placeholders);
+ assertNoPlacholder("/");
+ assertThat(placeholders.size(), is(0));
+ }
+
+ @Test
+ public void shouldLoadPlaceholderNodesForWorkspaceWithMultipleProjectionsNotAtRoot()
{
+ addProjection("source1", "workspace1", "/a/b =>
/a1/b1");
+ addProjection("source2", "workspace2", "/c/d =>
/c1/d1");
+ ProjectorWithPlaceholders.loadPlaceholderNodes(context, projections,
placeholders);
+ assertPlacholderHasChildren("/", "a", "c");
+ assertPlacholderHasChildren("/a", "b");
+ assertPlacholderHasChildren("/c", "d");
+ assertThat(placeholders.size(), is(3));
+ }
+
+ @Test
+ public void shouldLoadPlaceholderNodesForWorkspaceWithMultipleProjectionsAtRoot() {
+ addProjection("source1", "workspace1", "/ =>
/");
+ addProjection("source2", "workspace2", "/ =>
/");
+ ProjectorWithPlaceholders.loadPlaceholderNodes(context, projections,
placeholders);
+ assertNoPlacholder("/");
+ assertThat(placeholders.size(), is(0));
+ }
+
+ @Test
+ public void shouldLoadPlaceholderNodesForWorkspaceWithMirrorAndBranchProjections() {
+ addProjection("source1", "workspace1", "/ =>
/");
+ addProjection("source2", "workspace2", "/a =>
/a");
+ ProjectorWithPlaceholders.loadPlaceholderNodes(context, projections,
placeholders);
+ assertPlacholderHasChildren("/", "a"); // placeholder for
root contains only branch root
+ assertThat(placeholders.size(), is(1));
+ assertNoPlacholder("/a");
+ assertNoPlacholder("/b");
+ assertNoPlacholder("/c/d");
+ assertNoPlacholder("/a/e");
+ }
+
+ @Test
+ public void
shouldLoadPlaceholderNodesForWorkspaceWithMirrorAndOffsetBranchProjections() {
+ addProjection("source1", "workspace1", "/ =>
/");
+ addProjection("source2", "workspace2", "/a/b/c =>
/a/b");
+ ProjectorWithPlaceholders.loadPlaceholderNodes(context, projections,
placeholders);
+ assertPlacholderHasChildren("/", "a"); // placeholder for
root contains only branch root
+ assertPlacholderHasChildren("/a", "b");
+ assertPlacholderHasChildren("/a/b", "c");
+ assertThat(placeholders.size(), is(3));
+ assertNoPlacholder("/x");
+ assertNoPlacholder("/y/z");
+ assertNoPlacholder("/a/b/c");
+ }
+
+ @Test
+ public void shouldLoadPlaceholderNodesForWorkspaceWithOffsetMirrorProjection() {
+ addProjection("source1", "workspace1", "/a/b/c =>
/");
+ ProjectorWithPlaceholders.loadPlaceholderNodes(context, projections,
placeholders);
+ assertPlacholderHasChildren("/", "a"); // placeholder for
root contains only branch root
+ assertPlacholderHasChildren("/a", "b");
+ assertPlacholderHasChildren("/a/b", "c");
+ assertThat(placeholders.size(), is(3));
+ assertNoPlacholder("/a[2]");
+ assertNoPlacholder("/a/b[2]");
+ assertNoPlacholder("/a/b/c[2]");
+ }
+
+}
Property changes on:
trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/federation/ProjectorWithPlaceholdersTest.java
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Modified:
trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/inmemory/InMemoryConnectorReadableTest.java
===================================================================
---
trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/inmemory/InMemoryConnectorReadableTest.java 2009-05-15
19:28:48 UTC (rev 910)
+++
trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/inmemory/InMemoryConnectorReadableTest.java 2009-05-18
15:36:58 UTC (rev 911)
@@ -79,7 +79,7 @@
Location root = graph.getCurrentWorkspace().getRoot();
Subgraph subgraph1 = graph.getSubgraphOfDepth(10).at(root);
Subgraph subgraph2 = graph.getSubgraphOfDepth(10).at(root);
- assertEquivalentSubgraphs(subgraph1, subgraph2, true);
+ assertEquivalentSubgraphs(subgraph1, subgraph2, true, true);
}
}
Modified:
trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/test/AbstractConnectorTest.java
===================================================================
---
trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/test/AbstractConnectorTest.java 2009-05-15
19:28:48 UTC (rev 910)
+++
trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/test/AbstractConnectorTest.java 2009-05-18
15:36:58 UTC (rev 911)
@@ -613,16 +613,22 @@
* @param subgraph2 the second subgraph; may not be null
* @param idPropertiesShouldMatch true if the identification properties of each
corresponding node should match, or false if
* the identification properties should be ignored
+ * @param pathsShouldMatch true if the absolute paths of the subgraphs should match,
or false if only the relative paths
+ * within the subgraphs should match
*/
- public void assertEquivalentSubgraphs( Subgraph subgraph1,
- Subgraph subgraph2,
- boolean idPropertiesShouldMatch ) {
+ public static void assertEquivalentSubgraphs( Subgraph subgraph1,
+ Subgraph subgraph2,
+ boolean idPropertiesShouldMatch,
+ boolean pathsShouldMatch ) {
assertThat(subgraph1, is(notNullValue()));
assertThat(subgraph2, is(notNullValue()));
// Shortcut ...
if (subgraph1.getLocation().equals(subgraph2.getLocation())) return;
+ Path rootPath1 = subgraph1.getRoot().getLocation().getPath();
+ Path rootPath2 = subgraph2.getRoot().getLocation().getPath();
+
// Iterate over each subgraph. Note that because each location should have a
path, the path can be used
// to ensure the structure matches.
Iterator<SubgraphNode> iter1 = subgraph1.iterator();
@@ -631,10 +637,19 @@
Node node1 = iter1.next();
Node node2 = iter2.next();
+ assertThat(node1, is(notNullValue()));
+ assertThat(node2, is(notNullValue()));
+
// Each node should have equivalent paths ..
assertThat(node1.getLocation().hasPath(), is(true));
assertThat(node2.getLocation().hasPath(), is(true));
- assertThat(node1.getLocation().getPath(),
is(node2.getLocation().getPath()));
+ if (pathsShouldMatch) {
+ assertThat(node1.getLocation().getPath(),
is(node2.getLocation().getPath()));
+ } else {
+ Path relativeNode1 =
node1.getLocation().getPath().relativeTo(rootPath1);
+ Path relativeNode2 =
node2.getLocation().getPath().relativeTo(rootPath2);
+ assertThat(relativeNode1, is(relativeNode2));
+ }
// Each node should have the same Identification properties ...
if (idPropertiesShouldMatch) {
@@ -654,7 +669,7 @@
properties2.remove(idProperty.getName());
}
}
- assertThat(properties1.values(), is(properties2.values()));
+ assertThat(properties1, is(properties2));
// Each node should have the same children. We can check this, tho this will
be enforced when comparing paths ...
assertThat(node1.getChildrenSegments(), is(node2.getChildrenSegments()));
@@ -672,8 +687,8 @@
* @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 ) {
+ public static void assertSameNode( Node node1,
+ Node node2 ) {
assertThat(node1, is(notNullValue()));
assertThat(node2, is(notNullValue()));
@@ -701,8 +716,8 @@
* @param node the node; may not be null
* @param properties the expected properties
*/
- public void assertSameProperties( Node node,
- Map<Name, Property> properties ) {
+ public static void assertSameProperties( Node node,
+ Map<Name, Property> properties ) {
assertThat(node, is(notNullValue()));
assertThat(properties, is(notNullValue()));
Set<Name> names = new HashSet<Name>(properties.keySet());
@@ -720,8 +735,8 @@
* @param node the node; may not be null
* @param properties the expected properties
*/
- public void assertSameProperties( Node node,
- Iterable<Property> properties ) {
+ public static void assertSameProperties( Node node,
+ Iterable<Property> properties ) {
assertThat(node, is(notNullValue()));
assertThat(properties, is(notNullValue()));
Set<Name> names = new
HashSet<Name>(node.getPropertiesByName().keySet());
Modified:
trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/test/ReadableConnectorTest.java
===================================================================
---
trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/test/ReadableConnectorTest.java 2009-05-15
19:28:48 UTC (rev 910)
+++
trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/test/ReadableConnectorTest.java 2009-05-18
15:36:58 UTC (rev 911)
@@ -305,7 +305,7 @@
Subgraph subgraph1 = graph.getSubgraphOfDepth(10).at(root);
for (int i = 0; i != 4; ++i) {
Subgraph subgraph2 = graph.getSubgraphOfDepth(10).at(root);
- assertEquivalentSubgraphs(subgraph1, subgraph2, true);
+ assertEquivalentSubgraphs(subgraph1, subgraph2, true, true);
}
}
}
Modified:
trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/test/WorkspaceConnectorTest.java
===================================================================
---
trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/test/WorkspaceConnectorTest.java 2009-05-15
19:28:48 UTC (rev 910)
+++
trunk/dna-graph/src/test/java/org/jboss/dna/graph/connector/test/WorkspaceConnectorTest.java 2009-05-18
15:36:58 UTC (rev 911)
@@ -283,7 +283,7 @@
// Verify that the content of 'workspace1' matches that of
'workspace2'
Subgraph subgraph1 =
graph.getSubgraphOfDepth(100000).at(workspace1.getRoot());
Subgraph subgraph2 =
graph.getSubgraphOfDepth(100000).at(workspace2.getRoot());
- assertEquivalentSubgraphs(subgraph1, subgraph2, true);
+ assertEquivalentSubgraphs(subgraph1, subgraph2, true, true);
} catch (InvalidRequestException error) {
// Updates may not be supported, but if they are then this is a failure
...
if (source.getCapabilities().supportsUpdates()) throw error;
Modified: trunk/pom.xml
===================================================================
--- trunk/pom.xml 2009-05-15 19:28:48 UTC (rev 910)
+++ trunk/pom.xml 2009-05-18 15:36:58 UTC (rev 911)
@@ -144,7 +144,7 @@
<module>extensions/dna-common-jdbc</module>
<module>extensions/dna-connector-jdbc-metadata</module>
<module>dna-integration-tests</module>
- <module>docs/examples/gettingstarted</module>
+ <!--module>docs/examples/gettingstarted</module-->
</modules>
<profiles>