Author: bcarothers
Date: 2009-12-28 23:25:02 -0500 (Mon, 28 Dec 2009)
New Revision: 1484
Added:
trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/path/AbstractWritablePathWorkspace.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/path/PathRepositoryTransaction.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/path/WritablePathRepository.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/path/WritablePathWorkspace.java
trunk/extensions/dna-connector-filesystem/src/main/java/org/jboss/dna/connector/filesystem/FileSystemRepository.java
Removed:
trunk/extensions/dna-connector-filesystem/src/main/java/org/jboss/dna/connector/filesystem/FileSystemConnection.java
trunk/extensions/dna-connector-filesystem/src/main/java/org/jboss/dna/connector/filesystem/FileSystemRequestProcessor.java
Modified:
trunk/dna-graph/src/main/java/org/jboss/dna/graph/GraphI18n.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/path/DefaultPathNode.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/path/PathNode.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/path/PathRepository.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/path/PathRepositoryConnection.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/path/PathRepositorySource.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/path/PathRequestProcessor.java
trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/path/PathWorkspace.java
trunk/dna-graph/src/main/resources/org/jboss/dna/graph/GraphI18n.properties
trunk/extensions/dna-connector-filesystem/src/main/java/org/jboss/dna/connector/filesystem/FileSystemI18n.java
trunk/extensions/dna-connector-filesystem/src/main/java/org/jboss/dna/connector/filesystem/FileSystemSource.java
trunk/extensions/dna-connector-filesystem/src/main/java/org/jboss/dna/connector/filesystem/FileSystemTransaction.java
trunk/extensions/dna-connector-filesystem/src/main/java/org/jboss/dna/connector/filesystem/package-info.java
trunk/extensions/dna-connector-filesystem/src/main/resources/org/jboss/dna/connector/filesystem/FileSystemI18n.properties
trunk/extensions/dna-connector-filesystem/src/test/java/org/jboss/dna/connector/filesystem/FileSystemSourceTest.java
trunk/extensions/dna-connector-jdbc-metadata/src/main/java/org/jboss/dna/connector/meta/jdbc/JdbcMetadataI18n.java
trunk/extensions/dna-connector-jdbc-metadata/src/main/java/org/jboss/dna/connector/meta/jdbc/JdbcMetadataRepository.java
trunk/extensions/dna-connector-jdbc-metadata/src/main/java/org/jboss/dna/connector/meta/jdbc/JdbcMetadataSource.java
trunk/extensions/dna-connector-jdbc-metadata/src/main/resources/org/jboss/dna/connector/meta/jdbc/JdbcMetadataI18n.properties
Log:
DNA-603 File System Repository Should Be Ported To Use Path Repository
Applied patch that augments the path repository framework to support writes and moves the
file system repository over onto the framework. All tests pass.
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-12-27 20:45:58
UTC (rev 1483)
+++ trunk/dna-graph/src/main/java/org/jboss/dna/graph/GraphI18n.java 2009-12-29 04:25:02
UTC (rev 1484)
@@ -104,6 +104,7 @@
public static I18n workspaceDoesNotExistInRepository;
public static I18n workspaceAlreadyExistsInRepository;
public static I18n sourceIsReadOnly;
+ public static I18n workspaceIsReadOnly;
/* Federation Connection */
public static I18n namePropertyIsRequiredForFederatedRepositorySource;
Added:
trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/path/AbstractWritablePathWorkspace.java
===================================================================
---
trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/path/AbstractWritablePathWorkspace.java
(rev 0)
+++
trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/path/AbstractWritablePathWorkspace.java 2009-12-29
04:25:02 UTC (rev 1484)
@@ -0,0 +1,137 @@
+/*
+ * 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.path;
+
+import java.util.Map;
+import java.util.UUID;
+import org.jboss.dna.graph.ExecutionContext;
+import org.jboss.dna.graph.NodeConflictBehavior;
+import org.jboss.dna.graph.connector.LockFailedException;
+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 org.jboss.dna.graph.property.Path.Segment;
+import org.jboss.dna.graph.query.QueryResults;
+import org.jboss.dna.graph.request.AccessQueryRequest;
+import org.jboss.dna.graph.request.LockBranchRequest.LockScope;
+
+/**
+ * Implementation of some methods from {@link WritablePathWorkspace} to assist in the
development of path-based connectors.
+ * Subclasses of this class should be made thread-safe.
+ */
+public abstract class AbstractWritablePathWorkspace implements WritablePathWorkspace {
+
+ private final String name;
+ protected final UUID rootNodeUuid;
+
+ public AbstractWritablePathWorkspace( String name,
+ UUID rootNodeUuid ) {
+ super();
+ this.name = name;
+ this.rootNodeUuid = rootNodeUuid;
+ }
+
+ /**
+ * This should copy the subgraph rooted at the original node and place the new copy
under the supplied new parent. Note that
+ * internal references between nodes within the original subgraph must be reflected
as internal nodes within the new subgraph.
+ *
+ * @param context the context; may not be null
+ * @param original the node to be copied; may not be null
+ * @param originalWorkspace the workspace containing the original node; may not be
null
+ * @param newParent the parent where the copy is to be placed; may not be null
+ * @param desiredName the desired name for the node; if null, the name will be
obtained from the original node
+ * @param recursive true if the copy should be recursive
+ * @return the new node, which is the top of the new subgraph
+ */
+ public PathNode copyNode( ExecutionContext context,
+ PathNode original,
+ PathWorkspace originalWorkspace,
+ PathNode newParent,
+ Name desiredName,
+ boolean recursive ) {
+ PathFactory pathFactory = context.getValueFactories().getPathFactory();
+ PathNode copy = createNode(context, newParent, desiredName,
original.getProperties(), NodeConflictBehavior.REPLACE);
+
+ if (recursive) {
+ Path originalPath = original.getPath();
+
+ for (Segment childSegment : original.getChildSegments()) {
+ Path childPath = pathFactory.create(originalPath, childSegment);
+ PathNode childNode = originalWorkspace.getNode(childPath);
+ copyNode(context, childNode, originalWorkspace, copy,
childSegment.getName(), true);
+ }
+ }
+ return copy;
+ }
+
+ public PathNode createNode( ExecutionContext context,
+ String pathToNewNode,
+ Map<Name, Property> properties,
+ NodeConflictBehavior conflictBehavior ) {
+ PathFactory pathFactory = context.getValueFactories().getPathFactory();
+ Path newPath = pathFactory.create(pathToNewNode);
+
+ return createNode(context, getNode(newPath.getParent()),
newPath.getLastSegment().getName(), properties, conflictBehavior);
+ }
+
+ public PathNode moveNode( ExecutionContext context,
+ PathNode node,
+ Name desiredNewName,
+ WritablePathWorkspace originalWorkspace,
+ PathNode newParent,
+ PathNode beforeNode ) {
+ if (desiredNewName == null) {
+ assert !node.getPath().isRoot();
+ desiredNewName = node.getPath().getLastSegment().getName();
+ }
+
+ PathNode newCopy = copyNode(context, node, originalWorkspace, newParent,
desiredNewName, true);
+ originalWorkspace.removeNode(context, node.getPath());
+ return newCopy;
+ }
+
+ public QueryResults query( ExecutionContext context,
+ AccessQueryRequest accessQuery ) {
+ return null;
+ }
+
+ public QueryResults search( ExecutionContext context,
+ String fullTextSearchExpression ) {
+ return null;
+ }
+
+ public String getName() {
+ return this.name;
+ }
+
+ public void lockNode( PathNode node,
+ LockScope lockScope,
+ long lockTimeoutInMillis ) throws LockFailedException {
+ }
+
+ public void unlockNode( PathNode node ) {
+ }
+
+}
Property changes on:
trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/path/AbstractWritablePathWorkspace.java
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Modified:
trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/path/DefaultPathNode.java
===================================================================
---
trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/path/DefaultPathNode.java 2009-12-27
20:45:58 UTC (rev 1483)
+++
trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/path/DefaultPathNode.java 2009-12-29
04:25:02 UTC (rev 1484)
@@ -5,6 +5,7 @@
import java.util.List;
import java.util.Map;
import java.util.Set;
+import java.util.UUID;
import net.jcip.annotations.Immutable;
import org.jboss.dna.graph.ExecutionContext;
import org.jboss.dna.graph.property.Name;
@@ -22,13 +23,16 @@
private final Path path;
private final Map<Name, Property> properties;
private final List<Segment> childSegments;
+ private final UUID uuid;
private Set<Name> uniqueChildNames;
public DefaultPathNode( Path path,
+ UUID uuid,
Map<Name, Property> properties,
List<Segment> childSegments ) {
super();
this.path = path;
+ this.uuid = uuid;
this.properties = properties;
this.childSegments = childSegments;
}
@@ -41,6 +45,10 @@
return this.path;
}
+ public UUID getUuid() {
+ return this.uuid;
+ }
+
public Map<Name, Property> getProperties() {
return Collections.unmodifiableMap(this.properties);
}
@@ -70,4 +78,5 @@
return null;
}
+
}
Modified: trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/path/PathNode.java
===================================================================
---
trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/path/PathNode.java 2009-12-27
20:45:58 UTC (rev 1483)
+++
trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/path/PathNode.java 2009-12-29
04:25:02 UTC (rev 1484)
@@ -3,6 +3,7 @@
import java.util.List;
import java.util.Map;
import java.util.Set;
+import java.util.UUID;
import org.jboss.dna.graph.ExecutionContext;
import org.jboss.dna.graph.property.Name;
import org.jboss.dna.graph.property.NameFactory;
@@ -22,6 +23,14 @@
public Path getPath();
/**
+ * Returns the UUID for this node. Only the root node in a {@link PathWorkspace}
should have a UUID. All other nodes should
+ * return null from this method.
+ *
+ * @return the UUID for this node; may be null
+ */
+ public UUID getUuid();
+
+ /**
* Returns the set of child names for this node
*
* @return the set of child names for this node
@@ -57,5 +66,4 @@
* @return a map of property names to the property for the given name
*/
public Map<Name, Property> getProperties();
-
}
Modified:
trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/path/PathRepository.java
===================================================================
---
trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/path/PathRepository.java 2009-12-27
20:45:58 UTC (rev 1483)
+++
trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/path/PathRepository.java 2009-12-29
04:25:02 UTC (rev 1484)
@@ -1,18 +1,21 @@
package org.jboss.dna.graph.connector.path;
-import java.util.HashMap;
-import java.util.Map;
import java.util.Set;
import java.util.UUID;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import net.jcip.annotations.ThreadSafe;
import org.jboss.dna.common.util.CheckArg;
+@ThreadSafe
public abstract class PathRepository {
protected final UUID rootNodeUuid;
private final String sourceName;
private final String defaultWorkspaceName;
- protected final Map<String, PathWorkspace> workspaces = new HashMap<String,
PathWorkspace>();
+ protected final ConcurrentMap<String, PathWorkspace> workspaces = new
ConcurrentHashMap<String, PathWorkspace>();
+
/**
* Creates a {@code PathRepository} with the given repository source name, root node
UUID, and a default workspace named
* {@code ""} (the empty string).
@@ -103,4 +106,31 @@
*/
protected abstract void initialize();
+ /**
+ * Begin a transaction, hinting whether the transaction will be used only to read the
content. If this is called, then the
+ * transaction must be either {@link PathRepositoryTransaction#commit() committed}
or
+ * {@link PathRepositoryTransaction#rollback() rolled back}.
+ *
+ * @param readonly true if the transaction will not modify any content, or false if
changes are to be made
+ * @return the transaction; never null
+ * @see PathRepositoryTransaction#commit()
+ * @see PathRepositoryTransaction#rollback()
+ */
+ public PathRepositoryTransaction startTransaction( boolean readonly ) {
+
+ // Read-only repositories can return a default NOP implementation
+ return new PathRepositoryTransaction() {
+
+ public void commit() {
+ }
+
+ public void rollback() {
+ }
+
+ };
+ }
+
+ public boolean isWritable() {
+ return false;
+ }
}
Modified:
trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/path/PathRepositoryConnection.java
===================================================================
---
trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/path/PathRepositoryConnection.java 2009-12-27
20:45:58 UTC (rev 1483)
+++
trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/path/PathRepositoryConnection.java 2009-12-29
04:25:02 UTC (rev 1484)
@@ -76,21 +76,49 @@
sw = new Stopwatch();
sw.start();
}
+
// Do any commands update/write?
+ PathRepositoryTransaction txn =
repository.startTransaction(request.isReadOnly());
RepositoryContext repositoryContext = this.source.getRepositoryContext();
Observer observer = repositoryContext != null ? repositoryContext.getObserver() :
null;
- RequestProcessor processor = new PathRequestProcessor(context, this.repository,
observer);
+ RequestProcessor processor = new PathRequestProcessor(context, this.repository,
observer, source.areUpdatesAllowed());
+ boolean commit = true;
try {
// Obtain the lock and execute the commands ...
processor.process(request);
+ if (request.hasError() && !request.isReadOnly()) {
+ // The changes failed, so we need to rollback so we have
'all-or-nothing' behavior
+ commit = false;
+ }
+ } catch (Throwable error) {
+ commit = false;
} finally {
try {
processor.close();
} finally {
- processor.notifyObserverOfChanges();
+ // Now commit or rollback ...
+ try {
+ if (commit) {
+ txn.commit();
+ } else {
+ // Need to rollback the changes made to the repository ...
+ txn.rollback();
+ }
+ } catch (Throwable commitOrRollbackError) {
+ if (commit && !request.hasError() &&
!request.isFrozen()) {
+ // Record the error on the request ...
+ request.setError(commitOrRollbackError);
+ }
+ commit = false; // couldn't do it
+ }
+ if (commit) {
+ // Now that we've closed our transaction, we can notify the
observer of the committed changes ...
+ processor.notifyObserverOfChanges();
+ }
}
}
+
if (logger.isTraceEnabled()) {
assert sw != null;
sw.stop();
Modified:
trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/path/PathRepositorySource.java
===================================================================
---
trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/path/PathRepositorySource.java 2009-12-27
20:45:58 UTC (rev 1483)
+++
trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/path/PathRepositorySource.java 2009-12-29
04:25:02 UTC (rev 1484)
@@ -11,6 +11,23 @@
public interface PathRepositorySource extends RepositorySource {
/**
+ * Get whether this source allows updates.
+ *
+ * @return true if this source allows updates by clients, or false if no updates are
allowed
+ * @see #setUpdatesAllowed(boolean)
+ */
+ boolean areUpdatesAllowed();
+
+ /**
+ * Set whether this source allows updates to data within workspaces
+ *
+ * @param updatesAllowed true if this source allows updates to data within workspaces
clients, or false if updates are not
+ * allowed.
+ * @see #areUpdatesAllowed()
+ */
+ void setUpdatesAllowed( boolean updatesAllowed );
+
+ /**
* Returns the {@link CachePolicy cache policy} for the repository source
*
* @return the {@link CachePolicy cache policy} for the repository source
Added:
trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/path/PathRepositoryTransaction.java
===================================================================
---
trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/path/PathRepositoryTransaction.java
(rev 0)
+++
trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/path/PathRepositoryTransaction.java 2009-12-29
04:25:02 UTC (rev 1484)
@@ -0,0 +1,53 @@
+/*
+ * 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.path;
+
+import org.jboss.dna.graph.connector.map.MapRepositoryTransaction;
+
+/**
+ * A transaction returned by the {@link PathRepository#startTransaction(boolean)}.
+ *
+ * @see MapRepositoryTransaction
+ */
+public interface PathRepositoryTransaction {
+
+ /**
+ * Commit any changes that have been made to the repository. This method may throw
runtime exceptions if there are failures
+ * committing the changes, but the transaction is still expected to be closed.
+ *
+ * @see #rollback()
+ * @see PathRepository#startTransaction(boolean)
+ */
+ void commit();
+
+ /**
+ * Rollback any changes that have been made to this repository. This method may throw
runtime exceptions if there are failures
+ * rolling back the changes, but the transaction is still expected to be closed.
+ *
+ * @see #commit()
+ * @see PathRepository#startTransaction(boolean)
+ */
+ void rollback();
+
+}
\ No newline at end of file
Property changes on:
trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/path/PathRepositoryTransaction.java
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Modified:
trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/path/PathRequestProcessor.java
===================================================================
---
trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/path/PathRequestProcessor.java 2009-12-27
20:45:58 UTC (rev 1483)
+++
trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/path/PathRequestProcessor.java 2009-12-29
04:25:02 UTC (rev 1484)
@@ -1,16 +1,26 @@
package org.jboss.dna.graph.connector.path;
+import java.util.Collections;
+import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
+import java.util.Map;
import java.util.Set;
import org.jboss.dna.common.i18n.I18n;
+import org.jboss.dna.common.util.CheckArg;
import org.jboss.dna.graph.ExecutionContext;
import org.jboss.dna.graph.GraphI18n;
import org.jboss.dna.graph.Location;
+import org.jboss.dna.graph.NodeConflictBehavior;
import org.jboss.dna.graph.observe.Observer;
+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.Path.Segment;
+import org.jboss.dna.graph.query.QueryResults;
+import org.jboss.dna.graph.request.AccessQueryRequest;
import org.jboss.dna.graph.request.CloneBranchRequest;
import org.jboss.dna.graph.request.CloneWorkspaceRequest;
import org.jboss.dna.graph.request.CopyBranchRequest;
@@ -18,14 +28,17 @@
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.FullTextSearchRequest;
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.LockBranchRequest;
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.ReadNodeRequest;
import org.jboss.dna.graph.request.Request;
+import org.jboss.dna.graph.request.UnlockBranchRequest;
import org.jboss.dna.graph.request.UpdatePropertiesRequest;
import org.jboss.dna.graph.request.VerifyWorkspaceRequest;
import org.jboss.dna.graph.request.processor.RequestProcessor;
@@ -37,17 +50,23 @@
private final PathFactory pathFactory;
private final PathRepository repository;
+ private final boolean updatesAllowed;
public PathRequestProcessor( ExecutionContext context,
PathRepository repository,
- Observer observer ) {
+ Observer observer,
+ boolean updatesAllowed ) {
super(repository.getSourceName(), context, observer);
this.repository = repository;
pathFactory = context.getValueFactories().getPathFactory();
+ this.updatesAllowed = updatesAllowed;
}
- private void updatesNotSupported( Request request ) {
- request.setError(new
InvalidRequestException(GraphI18n.unsupportedRequestType.text(request.getClass().getName(),
request)));
+ protected boolean updatesAllowed( Request request ) {
+ if (!updatesAllowed) {
+ request.setError(new
InvalidRequestException(GraphI18n.sourceIsReadOnly.text(getSourceName())));
+ }
+ return !request.hasError();
}
@Override
@@ -69,49 +88,321 @@
@Override
public void process( CreateWorkspaceRequest request ) {
- updatesNotSupported(request);
+ // There's a separate flag to allow creating workspaces (which may not
require modifying existing data)
+ // if (!updatesAllowed(request)) return;
+
+ if (!repository.isWritable()) {
+ String msg = GraphI18n.sourceIsReadOnly.text(repository.getSourceName());
+ request.setError(new InvalidRequestException(msg));
+ return;
+ }
+
+ WritablePathRepository writableRepo = (WritablePathRepository)repository;
+
+ PathWorkspace workspace = writableRepo.createWorkspace(getExecutionContext(),
+
request.desiredNameOfNewWorkspace(),
+
request.conflictBehavior());
+ if (workspace == null) {
+ String msg =
GraphI18n.workspaceAlreadyExistsInRepository.text(request.desiredNameOfNewWorkspace(),
+
repository.getSourceName());
+ request.setError(new InvalidWorkspaceException(msg));
+ } else {
+ request.setActualRootLocation(Location.create(pathFactory.createRootPath(),
repository.getRootNodeUuid()));
+ request.setActualWorkspaceName(workspace.getName());
+ recordChange(request);
+ }
}
@Override
public void process( CloneBranchRequest request ) {
- updatesNotSupported(request);
+ if (!updatesAllowed(request)) return;
+
+ PathWorkspace workspace = getWorkspace(request, request.fromWorkspace());
+ PathWorkspace intoWorkspace = getWorkspace(request, request.intoWorkspace());
+
+ if (workspace == null || intoWorkspace == null) return;
+ PathNode node = getTargetNode(workspace, request, request.from());
+ if (node == null) return;
+
+ if (!(intoWorkspace instanceof WritablePathWorkspace)) {
+ I18n msg = GraphI18n.workspaceIsReadOnly;
+ request.setError(new
InvalidRequestException(msg.text(repository.getSourceName(), intoWorkspace.getName())));
+ return;
+ }
+
+ WritablePathWorkspace newWorkspace = (WritablePathWorkspace)intoWorkspace;
+
+ // Look up the new parent, which must exist ...
+ Path newParentPath = request.into().getPath();
+ PathNode newParent = newWorkspace.getNode(newParentPath);
+ Set<Location> removedExistingNodes = new HashSet<Location>();
+ Name desiredName = request.desiredName();
+ PathNode newNode = newWorkspace.copyNode(getExecutionContext(), node, workspace,
newParent, desiredName, true);
+
+ Location oldLocation = Location.create(node.getPath(), node.getUuid());
+ Location newLocation = Location.create(newNode.getPath(), newNode.getUuid());
+ request.setActualLocations(oldLocation, newLocation);
+ request.setRemovedNodes(Collections.unmodifiableSet(removedExistingNodes));
+ recordChange(request);
}
@Override
public void process( CloneWorkspaceRequest request ) {
- updatesNotSupported(request);
+ if (!updatesAllowed(request)) return;
+
+ // Find the original workspace that we're cloning ...
+ final ExecutionContext context = getExecutionContext();
+ String targetWorkspaceName = request.desiredNameOfTargetWorkspace();
+ String nameOfWorkspaceToBeCloned = request.nameOfWorkspaceToBeCloned();
+ PathWorkspace original = repository.getWorkspace(nameOfWorkspaceToBeCloned);
+ PathWorkspace target = repository.getWorkspace(targetWorkspaceName);
+
+ if (!repository.isWritable()) {
+ String msg = GraphI18n.sourceIsReadOnly.text(repository.getSourceName());
+ request.setError(new InvalidRequestException(msg));
+ return;
+ }
+
+ WritablePathRepository writableRepo = (WritablePathRepository)repository;
+
+ if (target != null) {
+ String msg =
GraphI18n.workspaceAlreadyExistsInRepository.text(targetWorkspaceName,
repository.getSourceName());
+ request.setError(new InvalidWorkspaceException(msg));
+ return;
+ }
+
+ if (original == null) {
+ switch (request.cloneConflictBehavior()) {
+ case DO_NOT_CLONE:
+ String msg =
GraphI18n.workspaceDoesNotExistInRepository.text(nameOfWorkspaceToBeCloned,
+
repository.getSourceName());
+ request.setError(new InvalidWorkspaceException(msg));
+ return;
+ case SKIP_CLONE:
+ target = writableRepo.createWorkspace(context, targetWorkspaceName,
request.targetConflictBehavior());
+ assert target != null;
+
+
request.setActualRootLocation(Location.create(pathFactory.createRootPath(),
writableRepo.getRootNodeUuid()));
+ request.setActualWorkspaceName(target.getName());
+ return;
+ }
+ }
+ assert original != null;
+ target = writableRepo.createWorkspace(context,
+ targetWorkspaceName,
+ request.targetConflictBehavior(),
+ nameOfWorkspaceToBeCloned);
+ assert target != null;
+
+ request.setActualRootLocation(Location.create(pathFactory.createRootPath(),
writableRepo.getRootNodeUuid()));
+ request.setActualWorkspaceName(target.getName());
+ recordChange(request);
}
@Override
public void process( DestroyWorkspaceRequest request ) {
- updatesNotSupported(request);
+ if (!updatesAllowed(request)) return;
+
+ PathWorkspace workspace = repository.getWorkspace(request.workspaceName());
+ if (workspace != null) {
+ request.setActualRootLocation(Location.create(pathFactory.createRootPath(),
repository.getRootNodeUuid()));
+ recordChange(request);
+ } else {
+ String msg =
GraphI18n.workspaceDoesNotExistInRepository.text(request.workspaceName(),
repository.getSourceName());
+ request.setError(new InvalidWorkspaceException(msg));
+ }
}
@Override
public void process( CopyBranchRequest request ) {
- updatesNotSupported(request);
+ if (!updatesAllowed(request)) return;
+
+ PathWorkspace workspace = getWorkspace(request, request.fromWorkspace());
+ PathWorkspace intoWorkspace = getWorkspace(request, request.intoWorkspace());
+ if (workspace == null || intoWorkspace == null) return;
+ PathNode node = getTargetNode(workspace, request, request.from());
+ if (node == null) return;
+
+ if (!(intoWorkspace instanceof WritablePathWorkspace)) {
+ I18n msg = GraphI18n.workspaceIsReadOnly;
+ request.setError(new
InvalidRequestException(msg.text(repository.getSourceName(), intoWorkspace.getName())));
+ return;
+ }
+
+ WritablePathWorkspace newWorkspace = (WritablePathWorkspace)intoWorkspace;
+
+ // Look up the new parent, which must exist ...
+ Path newParentPath = request.into().getPath();
+ Name desiredName = request.desiredName();
+ PathNode newParent = newWorkspace.getNode(newParentPath);
+ PathNode newNode = newWorkspace.copyNode(getExecutionContext(), node, workspace,
newParent, desiredName, true);
+ Location oldLocation = Location.create(node.getPath(), node.getUuid());
+ Location newLocation = Location.create(newNode.getPath(), newNode.getUuid());
+ request.setActualLocations(oldLocation, newLocation);
+ recordChange(request);
}
@Override
public void process( CreateNodeRequest request ) {
- updatesNotSupported(request);
+ if (!updatesAllowed(request)) return;
+
+ PathWorkspace workspace = getWorkspace(request, request.inWorkspace());
+ if (workspace == null) return;
+ Path parent = request.under().getPath();
+ CheckArg.isNotNull(parent, "request.under().getPath()");
+ PathNode node = null;
+ // Look up the parent node, which must exist ...
+
+ PathNode parentNode = workspace.getNode(parent);
+ if (parentNode == null) {
+ Path lowestExisting = workspace.getLowestExistingPath(parent);
+ request.setError(new PathNotFoundException(request.under(), lowestExisting,
GraphI18n.nodeDoesNotExist.text(parent)));
+ return;
+ }
+
+ if (!(workspace instanceof WritablePathWorkspace)) {
+ I18n msg = GraphI18n.workspaceIsReadOnly;
+ request.setError(new
InvalidRequestException(msg.text(repository.getSourceName(), workspace.getName())));
+ return;
+ }
+
+ WritablePathWorkspace newWorkspace = (WritablePathWorkspace)workspace;
+
+ // Make a list of the properties that we will store: all props except dna:uuid
and jcr:uuid
+ Map<Name, Property> propsToStore = new HashMap<Name,
Property>(request.properties().size());
+ for (Property property : request.properties()) {
+ if (property.size() > 0) propsToStore.put(property.getName(), property);
+ }
+
+ NodeConflictBehavior conflictBehavior = request.conflictBehavior();
+ switch (conflictBehavior) {
+ case APPEND:
+ node = newWorkspace.createNode(getExecutionContext(), parentNode,
request.named(), propsToStore, conflictBehavior);
+ break;
+ case DO_NOT_REPLACE:
+ for (Segment childSegment : parentNode.getChildSegments()) {
+ if (request.named().equals(childSegment.getName())) {
+ Path childPath = pathFactory.create(parent, childSegment);
+ node = newWorkspace.getNode(childPath);
+ break;
+ }
+ }
+ if (node == null) {
+ node = newWorkspace.createNode(getExecutionContext(),
+ parentNode,
+ request.named(),
+ propsToStore,
+ conflictBehavior);
+ }
+ break;
+ case REPLACE:
+ // See if the node already exists (this doesn't record an error on
the request) ...
+ node = workspace.getNode(pathFactory.create(parent, request.named()));
+ if (node != null) {
+ newWorkspace.removeNode(getExecutionContext(), node.getPath());
+ }
+ node = newWorkspace.createNode(getExecutionContext(), parentNode,
request.named(), propsToStore, conflictBehavior);
+ break;
+ case UPDATE:
+ // See if the node already exists (this doesn't record an error on
the request) ...
+ node = newWorkspace.getNode(pathFactory.create(parent,
request.named()));
+ if (node == null) {
+ node = newWorkspace.createNode(getExecutionContext(),
+ parentNode,
+ request.named(),
+ propsToStore,
+ conflictBehavior);
+ } // otherwise, we found it and we're setting any properties below
+ break;
+ }
+ assert node != null;
+
+ Location actualLocation = Location.create(node.getPath(), node.getUuid());
+ request.setActualLocationOfNode(actualLocation);
+ recordChange(request);
+
}
@Override
public void process( DeleteBranchRequest request ) {
- updatesNotSupported(request);
+ if (!updatesAllowed(request)) return;
+
+ PathWorkspace workspace = getWorkspace(request, request.inWorkspace());
+ if (workspace == null) return;
+ PathNode node = getTargetNode(workspace, request, request.at());
+ if (node == null) return;
+
+ if (!(workspace instanceof WritablePathWorkspace)) {
+ I18n msg = GraphI18n.workspaceIsReadOnly;
+ request.setError(new
InvalidRequestException(msg.text(repository.getSourceName(), workspace.getName())));
+ return;
+ }
+
+ WritablePathWorkspace newWorkspace = (WritablePathWorkspace)workspace;
+ newWorkspace.removeNode(getExecutionContext(), node.getPath());
+
+ request.setActualLocationOfNode(Location.create(node.getPath(),
node.getUuid()));
+ recordChange(request);
}
@Override
public void process( MoveBranchRequest request ) {
- updatesNotSupported(request);
+ if (!updatesAllowed(request)) return;
+
+ PathWorkspace workspace = getWorkspace(request, request.inWorkspace());
+ if (workspace == null) return;
+
+ PathNode beforeNode = request.before() != null ? getTargetNode(workspace,
request, request.before()) : null;
+ PathNode node = getTargetNode(workspace, request, request.from());
+ if (node == null) return;
+ if (request.hasError()) return; // if beforeNode could not be found
+ // Look up the new parent, which must exist ...
+ Path newParentPath;
+
+ if (request.into() != null) {
+ newParentPath = request.into().getPath();
+ } else {
+ // into or before cannot both be null
+ assert beforeNode != null;
+ newParentPath = beforeNode.getPath().getParent();
+ }
+
+ PathNode newParent = workspace.getNode(newParentPath);
+ if (newParent == null) {
+ Path lowestExisting = workspace.getLowestExistingPath(newParentPath);
+ request.setError(new PathNotFoundException(request.into(), lowestExisting,
+
GraphI18n.nodeDoesNotExist.text(newParentPath)));
+ return;
+ }
+
+ if (!(workspace instanceof WritablePathWorkspace)) {
+ I18n msg = GraphI18n.workspaceIsReadOnly;
+ request.setError(new
InvalidRequestException(msg.text(repository.getSourceName(), workspace.getName())));
+ return;
+ }
+
+ WritablePathWorkspace newWorkspace = (WritablePathWorkspace)workspace;
+
+ node = newWorkspace.moveNode(getExecutionContext(), node, request.desiredName(),
newWorkspace, newParent, beforeNode);
+ assert node.getPath().getParent().equals(newParent.getPath());
+
+ Location oldLocation = Location.create(request.from().getPath());
+ Location newLocation = Location.create(node.getPath(), node.getUuid());
+ request.setActualLocations(oldLocation, newLocation);
+ recordChange(request);
+
}
@Override
public void process( ReadNodeRequest request ) {
PathWorkspace workspace = getWorkspace(request, request.inWorkspace());
+ if (workspace == null) return;
+
PathNode node = getTargetNode(workspace, request, request.at());
- if (node == null) return;
+ if (node == null) {
+ request.setError(new PathNotFoundException(request.at(),
workspace.getLowestExistingPath(request.at().getPath())));
+ return;
+ }
// Get the names of the children ...
for (Path.Segment childSegment : node.getChildSegments()) {
@@ -121,41 +412,119 @@
// Get the properties of the node ...
request.addProperties(node.getProperties().values());
- request.setActualLocationOfNode(Location.create(node.getPath()));
+ request.setActualLocationOfNode(Location.create(node.getPath(),
node.getUuid()));
setCacheableInfo(request);
}
@Override
public void process( ReadAllChildrenRequest request ) {
PathWorkspace workspace = getWorkspace(request, request.inWorkspace());
+ if (workspace == null) return;
+
PathNode node = getTargetNode(workspace, request, request.of());
- if (node == null) return;
+ if (node == null) {
+ request.setError(new PathNotFoundException(request.of(),
workspace.getLowestExistingPath(request.of().getPath())));
+ return;
+ }
List<Path.Segment> childSegments = node.getChildSegments();
for (Path.Segment childSegment : childSegments) {
request.addChild(Location.create(pathFactory.create(node.getPath(),
childSegment)));
}
- request.setActualLocationOfNode(Location.create(node.getPath()));
+ request.setActualLocationOfNode(Location.create(node.getPath(),
node.getUuid()));
setCacheableInfo(request);
}
@Override
public void process( ReadAllPropertiesRequest request ) {
PathWorkspace workspace = getWorkspace(request, request.inWorkspace());
+ if (workspace == null) return;
+
PathNode node = getTargetNode(workspace, request, request.at());
- if (node == null) return;
+ if (node == null) {
+ request.setError(new PathNotFoundException(request.at(),
workspace.getLowestExistingPath(request.at().getPath())));
+ return;
+ }
// Get the properties of the node ...
request.addProperties(node.getProperties().values());
- request.setActualLocationOfNode(Location.create(node.getPath()));
+ request.setActualLocationOfNode(Location.create(node.getPath(),
node.getUuid()));
setCacheableInfo(request);
}
@Override
+ public void process( AccessQueryRequest request ) {
+ PathWorkspace workspace = getWorkspace(request, request.workspace());
+ if (workspace == null) return;
+ final ExecutionContext context = getExecutionContext();
+ QueryResults results = workspace.query(context, request);
+ if (results != null) {
+ request.setResults(results.getTuples(), results.getStatistics());
+ } else {
+ super.processUnknownRequest(request);
+ }
+ }
+
+ @Override
+ public void process( FullTextSearchRequest request ) {
+ PathWorkspace workspace = getWorkspace(request, request.workspace());
+ if (workspace == null) return;
+ final ExecutionContext context = getExecutionContext();
+ QueryResults results = workspace.search(context, request.expression());
+ if (results != null) {
+ request.setResults(results.getColumns(), results.getTuples(),
results.getStatistics());
+ } else {
+ super.processUnknownRequest(request);
+ }
+ }
+
+ @Override
public void process( UpdatePropertiesRequest request ) {
- updatesNotSupported(request);
+ if (!updatesAllowed(request)) return;
+
+ PathWorkspace workspace = getWorkspace(request, request.inWorkspace());
+ PathNode node = getTargetNode(workspace, request, request.on());
+ if (node == null) return;
+
+ if (!(workspace instanceof WritablePathWorkspace)) {
+ I18n msg = GraphI18n.workspaceIsReadOnly;
+ request.setError(new
InvalidRequestException(msg.text(repository.getSourceName(), workspace.getName())));
+ return;
+ }
+
+ WritablePathWorkspace newWorkspace = (WritablePathWorkspace)workspace;
+
+ // Now set (or remove) the properties to the supplied node ...
+ newWorkspace.setProperties(getExecutionContext(), node.getPath(),
request.properties().values());
+
+ request.setActualLocationOfNode(Location.create(node.getPath(),
node.getUuid()));
+ recordChange(request);
}
+ @Override
+ public void process( LockBranchRequest request ) {
+ PathWorkspace workspace = getWorkspace(request, request.inWorkspace());
+ PathNode node = getTargetNode(workspace, request, request.at());
+ if (node == null) return;
+
+ workspace.lockNode(node, request.lockScope(), request.lockTimeoutInMillis());
+
+ request.setActualLocation(Location.create(node.getPath(), node.getUuid()));
+ recordChange(request);
+ }
+
+ @Override
+ public void process( UnlockBranchRequest request ) {
+ PathWorkspace workspace = getWorkspace(request, request.inWorkspace());
+ PathNode node = getTargetNode(workspace, request, request.at());
+ if (node == null) return;
+
+ workspace.unlockNode(node);
+
+ request.setActualLocation(Location.create(node.getPath(), node.getUuid()));
+ recordChange(request);
+ }
+
protected PathWorkspace getWorkspace( Request request,
String workspaceName ) {
// Get the workspace for this request ...
@@ -173,6 +542,13 @@
if (workspace == null) return null;
PathNode node = null;
+ if (location.getUuid() != null) {
+ if (repository.getRootNodeUuid().equals(location.getUuid())) {
+ PathFactory pathFactory = new
ExecutionContext().getValueFactories().getPathFactory();
+ return workspace.getNode(pathFactory.createRootPath());
+ }
+ }
+
if (!location.hasPath()) {
I18n msg = GraphI18n.pathConnectorRequestsMustHavePath;
request.setError(new IllegalArgumentException(msg.text()));
Modified:
trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/path/PathWorkspace.java
===================================================================
---
trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/path/PathWorkspace.java 2009-12-27
20:45:58 UTC (rev 1483)
+++
trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/path/PathWorkspace.java 2009-12-29
04:25:02 UTC (rev 1484)
@@ -16,14 +16,6 @@
String getName();
/**
- * Returns the root node in the workspace. This returns a {@link PathNode map node}
where {@code node.getParent() == null} and
- * {@code node.getPath().isRoot() == true}.
- *
- * @return the root node in the workspace
- */
- PathNode getRoot();
-
- /**
* Returns the node at the given path, if one exists of {@code null} if no {@PathNode
node} exists at the given
* path.
*
@@ -81,5 +73,4 @@
*/
QueryResults search( ExecutionContext context,
String fullTextSearchExpression );
-
}
Added:
trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/path/WritablePathRepository.java
===================================================================
---
trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/path/WritablePathRepository.java
(rev 0)
+++
trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/path/WritablePathRepository.java 2009-12-29
04:25:02 UTC (rev 1484)
@@ -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.
+ *
+ * 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.path;
+
+import java.util.UUID;
+import net.jcip.annotations.ThreadSafe;
+import org.jboss.dna.graph.ExecutionContext;
+import org.jboss.dna.graph.property.Path;
+import org.jboss.dna.graph.property.PathFactory;
+import org.jboss.dna.graph.property.Path.Segment;
+import org.jboss.dna.graph.request.CreateWorkspaceRequest.CreateConflictBehavior;
+
+/**
+ * Extension of {@link PathRepository} for repositories that support modification of
nodes as well as access to the nodes.
+ */
+@ThreadSafe
+public abstract class WritablePathRepository extends PathRepository {
+
+ public WritablePathRepository( String sourceName,
+ UUID rootNodeUuid,
+ String defaultWorkspaceName ) {
+ super(sourceName, rootNodeUuid, defaultWorkspaceName);
+ }
+
+ public WritablePathRepository( String sourceName,
+ UUID rootNodeUuid ) {
+ super(sourceName, rootNodeUuid);
+ }
+
+ /**
+ * Creates a new workspace with the given name containing only a root node.
+ * <p>
+ * <b>This method does NOT automatically add the newly created workspace to the
{@link #workspaces workspace map} or check to
+ * see if a workspace already exists in this repository with the same
name.</b>
+ * </p>
+ *
+ * @param context the context in which the workspace is to be created
+ * @param name the name of the workspace
+ * @return the newly created workspace; may not be null
+ */
+ protected abstract WritablePathWorkspace createWorkspace( ExecutionContext context,
+ String name );
+
+ /**
+ * Attempts to create a workspace with the given name with name-collision behavior
determined by the behavior parameter.
+ * <p>
+ * This method will first check to see if a workspace already exists with the given
name. If no such workspace exists, the
+ * method will create a new workspace with the given name, add it to the {@code
#workspaces workspaces map}, and return it. If
+ * a workspace with the requested name already exists and the {@code behavior} is
{@link CreateConflictBehavior#DO_NOT_CREATE}
+ * , this method will return {@code null} without modifying the state of the
repository. If a workspace with the requested
+ * name already exists and the {@code behavior} is {@link
CreateConflictBehavior#CREATE_WITH_ADJUSTED_NAME}, this method will
+ * generate a unique new name for the workspace, create a new workspace with the
given name, added it to the {@code
+ * #workspaces workspaces map}, and return it.
+ *
+ * @param context the context in which the workspace is to be created; may not be
null
+ * @param name the requested name of the workspace. The name of the workspace that is
returned from this method may not be the
+ * same as the requested name; may not be null
+ * @param behavior the behavior to use in case a workspace with the requested name
already exists in the repository
+ * @return the newly created workspace or {@code null} if a workspace with the
requested name already exists in the repository
+ * and {@code behavior == CreateConflictBehavior#DO_NOT_CREATE}.
+ */
+ public WritablePathWorkspace createWorkspace( ExecutionContext context,
+ String name,
+ CreateConflictBehavior behavior ) {
+ String newName = name;
+ boolean conflictingName = workspaces.containsKey(newName);
+ if (conflictingName) {
+ switch (behavior) {
+ case DO_NOT_CREATE:
+ return null;
+ case CREATE_WITH_ADJUSTED_NAME:
+ int counter = 0;
+ do {
+ newName = name + (++counter);
+ } while (workspaces.containsKey(newName));
+ break;
+ }
+ }
+ assert workspaces.containsKey(newName) == false;
+
+ WritablePathWorkspace workspace = createWorkspace(context, name);
+ workspaces.put(name, workspace);
+ return workspace;
+ }
+
+ /**
+ * Attempts to create a workspace with the requested name as in the
+ * {@link #createWorkspace(ExecutionContext, String, CreateConflictBehavior)} method
and then clones the content from the
+ * given source workspace into the new workspace if the creation was successful.
+ * <p>
+ * If no workspace with the name {@code nameOfWorkspaceToClone} exists, the method
will return an empty workspace.
+ * </p>
+ *
+ * @param context the context in which the workspace is to be created; may not be
null
+ * @param name the requested name of the workspace. The name of the workspace that is
returned from this method may not be the
+ * same as the requested name; may not be null
+ * @param existingWorkspaceBehavior the behavior to use in case a workspace with the
requested name already exists in the
+ * repository
+ * @param nameOfWorkspaceToClone the name of the workspace from which the content
should be cloned; may not be null
+ * @return the newly created workspace with an exact copy of the contents from the
workspace named {@code
+ * nameOfWorkspaceToClone} or {@code null} if a workspace with the requested
name already exists in the repository and
+ * {@code behavior == CreateConflictBehavior#DO_NOT_CREATE}.
+ */
+ public WritablePathWorkspace createWorkspace( ExecutionContext context,
+ String name,
+ CreateConflictBehavior
existingWorkspaceBehavior,
+ String nameOfWorkspaceToClone ) {
+ WritablePathWorkspace workspace = createWorkspace(context, name,
existingWorkspaceBehavior);
+ if (workspace == null) {
+ // Unable to create because of a duplicate name ...
+ return null;
+ }
+ PathWorkspace original = getWorkspace(nameOfWorkspaceToClone);
+
+ PathFactory pathFactory = context.getValueFactories().getPathFactory();
+ Path rootPath = pathFactory.createRootPath();
+
+ if (original != null) {
+ // Copy the properties of the root node ...
+ PathNode root = workspace.getNode(rootPath);
+ PathNode origRoot = original.getNode(rootPath);
+ workspace.removeProperties(context, rootPath,
root.getProperties().keySet());
+ workspace.setProperties(context, rootPath,
origRoot.getProperties().values());
+
+ // Loop over each child and call this method to copy the immediate children
(and below).
+ for (Segment childSegment : origRoot.getChildSegments()) {
+ Path childPath = pathFactory.create(origRoot.getPath(), childSegment);
+ PathNode originalNode = original.getNode(childPath);
+
+ workspace.copyNode(context, originalNode, original, root,
childSegment.getName(), true);
+ }
+ }
+
+ workspaces.put(name, workspace);
+ return workspace;
+ }
+
+ /**
+ * Removes the named workspace from the {@code #workspaces workspaces map}.
+ *
+ * @param name the name of the workspace to remove
+ * @return {@code true} if a workspace with that name previously existed in the map
+ */
+ public boolean destroyWorkspace( String name ) {
+ return workspaces.remove(name) != null;
+ }
+
+ @Override
+ public boolean isWritable() {
+ return true;
+ }
+
+}
Property changes on:
trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/path/WritablePathRepository.java
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Added:
trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/path/WritablePathWorkspace.java
===================================================================
---
trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/path/WritablePathWorkspace.java
(rev 0)
+++
trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/path/WritablePathWorkspace.java 2009-12-29
04:25:02 UTC (rev 1484)
@@ -0,0 +1,152 @@
+/*
+ * 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.path;
+
+import java.util.Map;
+import org.jboss.dna.graph.ExecutionContext;
+import org.jboss.dna.graph.NodeConflictBehavior;
+import org.jboss.dna.graph.property.Name;
+import org.jboss.dna.graph.property.Path;
+import org.jboss.dna.graph.property.Property;
+
+/**
+ * Extension of {@link PathWorkspace} for repositories that support modification of nodes
as well as access to the nodes.
+ */
+public interface WritablePathWorkspace extends PathWorkspace {
+
+ /**
+ * Create a node at the supplied path. The parent of the new node must already
exist.
+ *
+ * @param context the environment; may not be null
+ * @param pathToNewNode the path to the new node; may not be null
+ * @param properties the properties for the new node
+ * @param conflictBehavior the expected behavior if an equivalently-named child
already exists at the location
+ * @return the new node (or root if the path specified the root)
+ */
+ PathNode createNode( ExecutionContext context,
+ String pathToNewNode,
+ Map<Name, Property> properties,
+ NodeConflictBehavior conflictBehavior );
+
+ /**
+ * Create a new node with the supplied name, as a child of the supplied parent.
+ *
+ * @param context the execution context
+ * @param parentNode the parent node; may not be null
+ * @param name the name; may not be null
+ * @param properties the properties for the new node
+ * @param conflictBehavior the expected behavior if an equivalently-named child
already exists at the location
+ * @return the new node
+ */
+ PathNode createNode( ExecutionContext context,
+ PathNode parentNode,
+ Name name,
+ Map<Name, Property> properties,
+ NodeConflictBehavior conflictBehavior );
+
+ /**
+ * Move the supplied node to the new parent within this workspace. This method
automatically removes the node from its
+ * existing parent, and also correctly adjusts the {@link Path.Segment#getIndex()
index} to be correct in the new parent.
+ *
+ * @param context
+ * @param node the node to be moved; may not be the workspace root node
+ * @param desiredNewName the new name for the node, if it is to be changed; may be
null
+ * @param originalWorkspace the workspace containing the node to be moved
+ * @param newParent the new parent; may not be the workspace root node
+ * @param beforeNode the node before which this new node should be placed
+ * @return a new copy of {@code node} that reflects the new location
+ */
+ public PathNode moveNode( ExecutionContext context,
+ PathNode node,
+ Name desiredNewName,
+ WritablePathWorkspace originalWorkspace,
+ PathNode newParent,
+ PathNode beforeNode );
+
+ /**
+ * Copy the subgraph given by the original node and place the new copy under the
supplied new parent. Note that internal
+ * references between nodes within the original subgraph must be reflected as
internal nodes within the new subgraph.
+ *
+ * @param context the context; may not be null
+ * @param original the node to be copied; may not be null
+ * @param originalWorkspace the workspace containing the original parent node; may
not be null
+ * @param newParent the parent where the copy is to be placed; may not be null
+ * @param desiredName the desired name for the node; if null, the name will be
obtained from the original node
+ * @param recursive true if the copy should be recursive
+ * @return the new node, which is the top of the new subgraph
+ */
+ PathNode copyNode( ExecutionContext context,
+ PathNode original,
+ PathWorkspace originalWorkspace,
+ PathNode newParent,
+ Name desiredName,
+ boolean recursive );
+
+ /**
+ * Inserts the specified child at the specified position in the list of children.
Shifts the child currently at that position
+ * (if any) and any subsequent children to the right (adds one to their indices).
+ *
+ * @param index index at which the specified child is to be inserted
+ * @param child the child to be inserted
+ */
+ // public void addChild( int index,
+ // PathNode child );
+
+ /**
+ * Removes the node at the given path
+ *
+ * @param context the context; may not be null
+ * @param nodePath the path of the node to be removed
+ * @return true if the node existed (and was removed); false otherwise
+ */
+ public boolean removeNode( ExecutionContext context,
+ Path nodePath );
+
+ /**
+ * Sets the given properties in a single operation, overwriting any previous
properties for the same name. This bulk mutator
+ * should be used when multiple properties are being set in order to allow underlying
implementations to optimize their access
+ * to their respective persistent storage mechanism.
+ *
+ * @param context the context; may not be null
+ * @param nodePath the path to the node on which the properties should be set
+ * @param properties the properties to set
+ * @return this map node
+ */
+ public PathNode setProperties( ExecutionContext context,
+ Path nodePath,
+ Iterable<Property> properties );
+
+ /**
+ * Removes the properties with the given names
+ *
+ * @param context the context; may not be null
+ * @param nodePath the path to the node from which the properties should be removed
+ * @param propertyNames the name of the properties to remove
+ * @return this map node
+ */
+ public PathNode removeProperties( ExecutionContext context,
+ Path nodePath,
+ Iterable<Name> propertyNames );
+
+}
Property changes on:
trunk/dna-graph/src/main/java/org/jboss/dna/graph/connector/path/WritablePathWorkspace.java
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
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-12-27
20:45:58 UTC (rev 1483)
+++ trunk/dna-graph/src/main/resources/org/jboss/dna/graph/GraphI18n.properties 2009-12-29
04:25:02 UTC (rev 1484)
@@ -89,9 +89,10 @@
inMemoryConnectorRequestsMustHavePathOrUuid = In-Memory connector can only process
requests with a path and/or UUID
inMemoryConnectorMustAllowUpdates = In-Memory connector "{0}" must allow
updates.
pathConnectorRequestsMustHavePath = Path connectors can only process requests with a
path
-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
+workspaceDoesNotExistInRepository = The workspace "{0}" does not exist in the
"{1}" repository
+workspaceAlreadyExistsInRepository = The workspace "{0}" already exists in the
"{1}" repository
sourceIsReadOnly = The repository source "{0}" does not allow updates. Set the
"updatesAllowed" property to "true" on the repository source to allow
updates.
+workspaceIsReadOnly = The workspace "{1}" in repository source "{0}"
does not allow updates. Setting the "updatesAllowed" property to
"true" on the repository source may allow this workspace to support updates.
# Federation connector
namePropertyIsRequiredForFederatedRepositorySource = The "{0}" property is
required on each federated repository source
Deleted:
trunk/extensions/dna-connector-filesystem/src/main/java/org/jboss/dna/connector/filesystem/FileSystemConnection.java
===================================================================
---
trunk/extensions/dna-connector-filesystem/src/main/java/org/jboss/dna/connector/filesystem/FileSystemConnection.java 2009-12-27
20:45:58 UTC (rev 1483)
+++
trunk/extensions/dna-connector-filesystem/src/main/java/org/jboss/dna/connector/filesystem/FileSystemConnection.java 2009-12-29
04:25:02 UTC (rev 1484)
@@ -1,195 +0,0 @@
-/*
- * 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.connector.filesystem;
-
-import java.io.File;
-import java.io.FilenameFilter;
-import java.util.Map;
-import java.util.UUID;
-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.connector.RepositoryConnection;
-import org.jboss.dna.graph.connector.RepositorySourceException;
-import org.jboss.dna.graph.connector.map.MapRepositoryTransaction;
-import org.jboss.dna.graph.request.Request;
-import org.jboss.dna.graph.request.processor.RequestProcessor;
-
-/**
- * The {@link RepositoryConnection} implementation for the file system connector. The
bulk of the work is performed by the
- * {@link FileSystemRequestProcessor}.
- */
-public class FileSystemConnection implements RepositoryConnection {
-
- private final String sourceName;
- private final String defaultWorkspaceName;
- private final CachePolicy cachePolicy;
- private final Map<String, File> availableWorkspaces;
- private final boolean creatingWorkspacesAllowed;
- private final FilenameFilter filenameFilter;
- private final UUID rootNodeUuid;
- private final int maxPathLength;
- private final String workspaceRootPath;
- private final boolean updatesAllowed;
- private final CustomPropertiesFactory customPropertiesFactory;
-
- FileSystemConnection( String sourceName,
- String defaultWorkspaceName,
- Map<String, File> availableWorkspaces,
- boolean creatingWorkspacesAllowed,
- CachePolicy cachePolicy,
- UUID rootNodeUuid,
- String workspaceRootPath,
- int maxPathLength,
- FilenameFilter filenameFilter,
- boolean updatesAllowed,
- CustomPropertiesFactory customPropertiesFactory ) {
- assert sourceName != null;
- assert sourceName.trim().length() != 0;
- assert availableWorkspaces != null;
- assert rootNodeUuid != null;
- assert customPropertiesFactory != null;
- this.sourceName = sourceName;
- this.defaultWorkspaceName = defaultWorkspaceName;
- this.availableWorkspaces = availableWorkspaces;
- this.creatingWorkspacesAllowed = creatingWorkspacesAllowed;
- this.cachePolicy = cachePolicy;
- this.rootNodeUuid = rootNodeUuid;
- this.workspaceRootPath = workspaceRootPath;
- this.maxPathLength = maxPathLength;
- this.filenameFilter = filenameFilter;
- this.updatesAllowed = updatesAllowed;
- this.customPropertiesFactory = customPropertiesFactory;
- }
-
- /**
- * {@inheritDoc}
- *
- * @see org.jboss.dna.graph.connector.RepositoryConnection#getSourceName()
- */
- public String getSourceName() {
- return sourceName;
- }
-
- /**
- * {@inheritDoc}
- *
- * @see org.jboss.dna.graph.connector.RepositoryConnection#getDefaultCachePolicy()
- */
- public CachePolicy getDefaultCachePolicy() {
- return cachePolicy;
- }
-
- /**
- * {@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#execute(org.jboss.dna.graph.ExecutionContext,
- * org.jboss.dna.graph.request.Request)
- */
- public void execute( ExecutionContext context,
- Request request ) throws RepositorySourceException {
- FileSystemTransaction txn = startTransaction(request.isReadOnly());
- RequestProcessor processor = new FileSystemRequestProcessor(sourceName,
defaultWorkspaceName, availableWorkspaces,
-
creatingWorkspacesAllowed, rootNodeUuid, workspaceRootPath,
- maxPathLength,
context, filenameFilter, updatesAllowed,
-
customPropertiesFactory, txn);
- boolean commit = true;
- try {
- // Obtain the lock and execute the commands ...
- processor.process(request);
- if (request.hasError() && !request.isReadOnly()) {
- // The changes failed, so we need to rollback so we have
'all-or-nothing' behavior
- commit = false;
- }
- } catch (Throwable error) {
- commit = false;
- } finally {
- try {
- processor.close();
- } finally {
- // Now commit or rollback ...
- try {
- if (commit) {
- txn.commit();
- } else {
- // Need to rollback the changes made to the repository ...
- txn.rollback();
- }
- } catch (Throwable commitOrRollbackError) {
- if (commit && !request.hasError() &&
!request.isFrozen()) {
- // Record the error on the request ...
- request.setError(commitOrRollbackError);
- }
- commit = false; // couldn't do it
- }
- if (commit) {
- // Now that we've closed our transaction, we can notify the
observer of the committed changes ...
- processor.notifyObserverOfChanges();
- }
- }
- }
- }
-
- /**
- * Begin a transaction, hinting whether the transaction will be used only to read the
content. If this is called, then the
- * transaction must be either {@link MapRepositoryTransaction#commit() committed} or
- * {@link MapRepositoryTransaction#rollback() rolled back}.
- *
- * @param readonly true if the transaction will not modify any content, or false if
changes are to be made
- * @return the transaction; never null
- * @see MapRepositoryTransaction#commit()
- * @see MapRepositoryTransaction#rollback()
- */
- protected FileSystemTransaction startTransaction( boolean readonly ) {
- return new FileSystemTransaction();
- }
-
- /**
- * {@inheritDoc}
- *
- * @see org.jboss.dna.graph.connector.RepositoryConnection#close()
- */
- public void close() {
- }
-}
Modified:
trunk/extensions/dna-connector-filesystem/src/main/java/org/jboss/dna/connector/filesystem/FileSystemI18n.java
===================================================================
---
trunk/extensions/dna-connector-filesystem/src/main/java/org/jboss/dna/connector/filesystem/FileSystemI18n.java 2009-12-27
20:45:58 UTC (rev 1483)
+++
trunk/extensions/dna-connector-filesystem/src/main/java/org/jboss/dna/connector/filesystem/FileSystemI18n.java 2009-12-29
04:25:02 UTC (rev 1484)
@@ -61,6 +61,7 @@
public static I18n invalidNameForResource;
public static I18n invalidPathForResource;
public static I18n invalidPropertyNames;
+ public static I18n couldNotReadData;
public static I18n couldNotWriteData;
public static I18n couldNotUpdateData;
public static I18n missingRequiredProperty;
Added:
trunk/extensions/dna-connector-filesystem/src/main/java/org/jboss/dna/connector/filesystem/FileSystemRepository.java
===================================================================
---
trunk/extensions/dna-connector-filesystem/src/main/java/org/jboss/dna/connector/filesystem/FileSystemRepository.java
(rev 0)
+++
trunk/extensions/dna-connector-filesystem/src/main/java/org/jboss/dna/connector/filesystem/FileSystemRepository.java 2009-12-29
04:25:02 UTC (rev 1484)
@@ -0,0 +1,728 @@
+/*
+ * 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.connector.filesystem;
+
+import java.io.BufferedInputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import org.jboss.dna.common.i18n.I18n;
+import org.jboss.dna.common.util.FileUtil;
+import org.jboss.dna.common.util.IoUtil;
+import org.jboss.dna.graph.DnaLexicon;
+import org.jboss.dna.graph.ExecutionContext;
+import org.jboss.dna.graph.JcrLexicon;
+import org.jboss.dna.graph.JcrNtLexicon;
+import org.jboss.dna.graph.Location;
+import org.jboss.dna.graph.NodeConflictBehavior;
+import org.jboss.dna.graph.connector.RepositorySourceException;
+import org.jboss.dna.graph.connector.path.AbstractWritablePathWorkspace;
+import org.jboss.dna.graph.connector.path.DefaultPathNode;
+import org.jboss.dna.graph.connector.path.PathNode;
+import org.jboss.dna.graph.connector.path.PathRepositoryTransaction;
+import org.jboss.dna.graph.connector.path.WritablePathRepository;
+import org.jboss.dna.graph.connector.path.WritablePathWorkspace;
+import org.jboss.dna.graph.mimetype.MimeTypeDetector;
+import org.jboss.dna.graph.property.Binary;
+import org.jboss.dna.graph.property.BinaryFactory;
+import org.jboss.dna.graph.property.DateTimeFactory;
+import org.jboss.dna.graph.property.Name;
+import org.jboss.dna.graph.property.NameFactory;
+import org.jboss.dna.graph.property.NamespaceRegistry;
+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.Path.Segment;
+import org.jboss.dna.graph.request.InvalidRequestException;
+import org.jboss.dna.graph.request.Request;
+
+/**
+ * Implementation of {@code WritablePathRepository} that provides access to an underlying
file system. This repository only
+ * natively supports nodes of primary types {@link JcrNtLexicon#FOLDER nt:folder}, {@link
JcrNtLexicon#FILE nt:file}, and
+ * {@link DnaLexicon#RESOURCE dna:resource}, although the {@link CustomPropertiesFactory}
allows for the addition of mixin types
+ * to any and all primary types.
+ */
+public class FileSystemRepository extends WritablePathRepository {
+ private static final String DEFAULT_MIME_TYPE = "application/octet";
+
+ private final FileSystemSource source;
+ private File repositoryRoot;
+
+ public FileSystemRepository( FileSystemSource source ) {
+ super(source.getName(), source.getRootNodeUuid(),
source.getDefaultWorkspaceName());
+
+ this.source = source;
+ initialize();
+ }
+
+ /**
+ * Creates any predefined workspaces, including the default workspace.
+ */
+ @Override
+ protected void initialize() {
+ String repositoryRootPath = source.getWorkspaceRootPath();
+ String sourceName = this.getSourceName();
+
+ if (repositoryRootPath != null) {
+ this.repositoryRoot = new File(repositoryRootPath);
+ if (!this.repositoryRoot.exists()) {
+ throw new
IllegalStateException(FileSystemI18n.pathForWorkspaceRootDoesNotExist.text(repositoryRootPath,
+
sourceName));
+ }
+ if (!this.repositoryRoot.isDirectory()) {
+ throw new
IllegalStateException(FileSystemI18n.pathForWorkspaceRootIsNotDirectory.text(repositoryRootPath,
+
sourceName));
+ }
+ if (!this.repositoryRoot.canRead()) {
+ throw new
IllegalStateException(FileSystemI18n.pathForWorkspaceRootCannotBeRead.text(repositoryRootPath,
+
sourceName));
+ }
+ }
+
+ if (!this.workspaces.isEmpty()) return;
+
+ String defaultWorkspaceName = getDefaultWorkspaceName();
+ ExecutionContext context = source.getRepositoryContext().getExecutionContext();
+
+ for (String workspaceName : source.getPredefinedWorkspaceNames()) {
+ doCreateWorkspace(context, workspaceName);
+
+ }
+
+ if (!workspaces.containsKey(defaultWorkspaceName)) {
+ doCreateWorkspace(context, defaultWorkspaceName);
+ }
+ }
+
+ /**
+ * Internal method that creates a workspace and adds it to the map of active
workspaces without checking to see if
+ * {@link FileSystemSource#isCreatingWorkspacesAllowed() the source allows creating
workspaces}. This is useful when setting
+ * up predefined workspaces.
+ *
+ * @param context the current execution context; may not be null
+ * @param name the name of the workspace to create; may not be null
+ * @return the newly created workspace; never null
+ */
+ private WritablePathWorkspace doCreateWorkspace( ExecutionContext context,
+ String name ) {
+ // This doesn't create the directory representing the workspace (it must
already exist), but it will add
+ // the workspace name to the available names ...
+ File directory = getWorkspaceDirectory(name);
+ FileSystemWorkspace workspace = new FileSystemWorkspace(name, context,
directory);
+
+ workspaces.putIfAbsent(name, workspace);
+ return (WritablePathWorkspace)workspaces.get(name);
+
+ }
+
+ @Override
+ protected WritablePathWorkspace createWorkspace( ExecutionContext context,
+ String name ) {
+ if (!source.isCreatingWorkspacesAllowed()) {
+ String msg = FileSystemI18n.unableToCreateWorkspaces.text(getSourceName(),
name);
+ throw new InvalidRequestException(msg);
+ }
+
+ return doCreateWorkspace(context, name);
+ }
+
+ /**
+ * @param workspaceName the name of the workspace for which the root directory should
be returned
+ * @return the directory that maps to the root node in the named workspace; may be
null if the directory does not exist, is a
+ * file, or cannot be read.
+ */
+ protected File getWorkspaceDirectory( String workspaceName ) {
+ if (workspaceName == null) workspaceName = source.getDefaultWorkspaceName();
+
+ File directory = this.repositoryRoot == null ? new File(workspaceName) : new
File(repositoryRoot, workspaceName);
+ if (directory.exists() && directory.isDirectory() &&
directory.canRead()) return directory;
+ return null;
+ }
+
+ @Override
+ public PathRepositoryTransaction startTransaction( boolean readonly ) {
+ return new FileSystemTransaction();
+ }
+
+ /**
+ * Writable workspace implementation for file system-backed workspaces
+ */
+ public class FileSystemWorkspace extends AbstractWritablePathWorkspace {
+
+ private final ExecutionContext context;
+ private final File workspaceRoot;
+
+ public FileSystemWorkspace( String name,
+ ExecutionContext context,
+ File workspaceRoot ) {
+ super(name, source.getRootNodeUuid());
+ this.workspaceRoot = workspaceRoot;
+ this.context = context;
+ }
+
+ @Override
+ public PathNode createNode( ExecutionContext context,
+ PathNode parentNode,
+ Name name,
+ Map<Name, Property> properties,
+ NodeConflictBehavior conflictBehavior ) {
+ NameFactory nameFactory = context.getValueFactories().getNameFactory();
+ PathFactory pathFactory = context.getValueFactories().getPathFactory();
+ NamespaceRegistry registry = context.getNamespaceRegistry();
+ /*
+ * Get references to java.io.Files
+ */
+
+ Path parentPath = parentNode.getPath();
+ File parentFile = fileFor(parentPath);
+
+ Path newPath = pathFactory.create(parentPath, name);
+ String newName = name.getString(registry);
+ File newFile = new File(parentFile, newName);
+
+ /*
+ * Determine the node primary type
+ */
+ Property primaryTypeProp = properties.get(JcrLexicon.PRIMARY_TYPE);
+
+ // Default primary type to nt:folder
+ Name primaryType = primaryTypeProp == null ? JcrNtLexicon.FOLDER :
nameFactory.create(primaryTypeProp.getFirstValue());
+ CustomPropertiesFactory customPropertiesFactory =
source.customPropertiesFactory();
+
+ if (JcrNtLexicon.FILE.equals(primaryType)) {
+
+ // The FILE node is represented by the existence of the file
+ if (!parentFile.canWrite()) {
+ I18n msg = FileSystemI18n.parentIsReadOnly;
+ throw new RepositorySourceException(getSourceName(),
msg.text(parentPath, this.getName(), getSourceName()));
+ }
+
+ try {
+ ensureValidPathLength(newFile);
+ boolean skipWrite = false;
+
+ if (newFile.exists()) {
+ if (conflictBehavior.equals(NodeConflictBehavior.APPEND)) {
+ I18n msg = FileSystemI18n.sameNameSiblingsAreNotAllowed;
+ throw new InvalidRequestException(msg.text(getSourceName(),
newName));
+ } else if
(conflictBehavior.equals(NodeConflictBehavior.DO_NOT_REPLACE)) {
+ skipWrite = true;
+ }
+ }
+
+ // Don't try to write if the node conflict behavior is
DO_NOT_REPLACE
+ if (!skipWrite) {
+ if (!newFile.createNewFile()) {
+ I18n msg = FileSystemI18n.fileAlreadyExists;
+ throw new RepositorySourceException(getSourceName(),
msg.text(parentPath, getName(), getSourceName()));
+ }
+ }
+ } catch (IOException ioe) {
+ I18n msg = FileSystemI18n.couldNotCreateFile;
+ throw new RepositorySourceException(getSourceName(),
msg.text(parentPath,
+
getName(),
+
getSourceName(),
+
ioe.getMessage()), ioe);
+ }
+
+ customPropertiesFactory.recordFileProperties(context,
+ getSourceName(),
+ Location.create(newPath),
+ newFile,
+ properties);
+ } else if (JcrNtLexicon.RESOURCE.equals(primaryType) ||
DnaLexicon.RESOURCE.equals(primaryType)) {
+ if (!JcrLexicon.CONTENT.equals(name)) {
+ I18n msg = FileSystemI18n.invalidNameForResource;
+ String nodeName = name.getString();
+ throw new RepositorySourceException(getSourceName(),
msg.text(parentPath,
+
getName(),
+
getSourceName(),
+
nodeName));
+ }
+
+ if (!parentFile.isFile()) {
+ I18n msg = FileSystemI18n.invalidPathForResource;
+ throw new RepositorySourceException(getSourceName(),
msg.text(parentPath, getName(), getSourceName()));
+ }
+
+ if (!parentFile.canWrite()) {
+ I18n msg = FileSystemI18n.parentIsReadOnly;
+ throw new RepositorySourceException(getSourceName(),
msg.text(parentPath, getName(), getSourceName()));
+ }
+
+ boolean skipWrite = false;
+
+ if (parentFile.exists()) {
+ if (conflictBehavior.equals(NodeConflictBehavior.APPEND)) {
+ I18n msg = FileSystemI18n.sameNameSiblingsAreNotAllowed;
+ throw new InvalidRequestException(msg.text(getSourceName(),
newName));
+ } else if
(conflictBehavior.equals(NodeConflictBehavior.DO_NOT_REPLACE)) {
+ // The content node logically maps to the file contents. If there
are file contents, don't replace them.
+ FileInputStream checkForContents = null;
+ try {
+ checkForContents = new FileInputStream(parentFile);
+ if (-1 != checkForContents.read()) skipWrite = true;
+
+ } catch (IOException ignore) {
+
+ } finally {
+ try {
+ if (checkForContents != null) checkForContents.close();
+ } catch (Exception ignore) {
+ }
+ }
+ skipWrite = true;
+ }
+ }
+
+ if (!skipWrite) {
+ // Copy over data into a temp file, then move it to the correct
location
+ FileOutputStream fos = null;
+ try {
+ File temp = File.createTempFile("dna", null);
+ fos = new FileOutputStream(temp);
+
+ Property dataProp = properties.get(JcrLexicon.DATA);
+ if (dataProp == null) {
+ I18n msg = FileSystemI18n.missingRequiredProperty;
+ String dataPropName = JcrLexicon.DATA.getString();
+ throw new RepositorySourceException(getSourceName(),
msg.text(parentPath,
+
getName(),
+
getSourceName(),
+
dataPropName));
+ }
+
+ BinaryFactory binaryFactory =
context.getValueFactories().getBinaryFactory();
+ Binary binary =
binaryFactory.create(properties.get(JcrLexicon.DATA).getFirstValue());
+
+ IoUtil.write(binary.getStream(), fos);
+
+ if (!FileUtil.delete(parentFile)) {
+ I18n msg = FileSystemI18n.deleteFailed;
+ throw new RepositorySourceException(getSourceName(),
msg.text(parentPath, getName(), getSourceName()));
+ }
+
+ if (!temp.renameTo(parentFile)) {
+ I18n msg = FileSystemI18n.couldNotUpdateData;
+ throw new RepositorySourceException(getSourceName(),
msg.text(parentPath, getName(), getSourceName()));
+ }
+ } catch (IOException ioe) {
+ I18n msg = FileSystemI18n.couldNotWriteData;
+ throw new RepositorySourceException(getSourceName(),
msg.text(parentPath,
+
getName(),
+
getSourceName(),
+
ioe.getMessage()), ioe);
+
+ } finally {
+ try {
+ if (fos != null) fos.close();
+ } catch (Exception ex) {
+ }
+ }
+ }
+ customPropertiesFactory.recordResourceProperties(context,
+ getSourceName(),
+
Location.create(parentPath),
+ newFile,
+ properties);
+
+ } else if (JcrNtLexicon.FOLDER.equals(primaryType) || primaryType == null) {
+ ensureValidPathLength(newFile);
+
+ if (!newFile.mkdir()) {
+ I18n msg = FileSystemI18n.couldNotCreateFile;
+ throw new RepositorySourceException(getSourceName(),
+ msg.text(parentPath,
+ getName(),
+ getSourceName(),
+ primaryType == null ?
"null" : primaryType.getString(registry)));
+ }
+ customPropertiesFactory.recordDirectoryProperties(context,
+ getSourceName(),
+
Location.create(newPath),
+ newFile,
+ properties);
+
+ } else {
+ // Set error and return
+ I18n msg = FileSystemI18n.unsupportedPrimaryType;
+ throw new RepositorySourceException(getSourceName(),
msg.text(primaryType.getString(registry),
+
parentPath,
+ getName(),
+
getSourceName()));
+ }
+ return getNode(newPath);
+ }
+
+ @Override
+ public boolean removeNode( ExecutionContext context,
+ Path nodePath ) {
+ File nodeFile;
+
+ if (!nodePath.isRoot() &&
JcrLexicon.CONTENT.equals(nodePath.getLastSegment().getName())) {
+ nodeFile = fileFor(nodePath.getParent());
+ if (!nodeFile.exists()) return false;
+
+ FileOutputStream fos = null;
+ try {
+ fos = new FileOutputStream(nodeFile);
+ IoUtil.write("", fos);
+ } catch (IOException ioe) {
+ throw new RepositorySourceException(getSourceName(),
FileSystemI18n.deleteFailed.text(nodePath,
+
getName(),
+
getSourceName()));
+ } finally {
+ if (fos != null) try {
+ fos.close();
+ } catch (IOException ioe) {
+ }
+ }
+ } else {
+ nodeFile = fileFor(nodePath);
+ if (!nodeFile.exists()) return false;
+
+ FileUtil.delete(nodeFile);
+ }
+
+ return true;
+ }
+
+ @Override
+ public PathNode removeProperties( ExecutionContext context,
+ Path nodePath,
+ Iterable<Name> propertyNames ) {
+
+ PathNode targetNode = getNode(nodePath);
+ if (targetNode == null) return null;
+ if (source.getCustomPropertiesFactory() == null) return targetNode;
+
+ Property primaryTypeProp = targetNode.getProperty(JcrLexicon.PRIMARY_TYPE);
+ Name primaryTypeName = (Name)primaryTypeProp.getFirstValue();
+ Map<Name, Property> properties = new HashMap<Name,
Property>(targetNode.getProperties());
+
+ for (Name propertyName : propertyNames) {
+ properties.remove(propertyName);
+ }
+
+ CustomPropertiesFactory customPropertiesFactory =
source.customPropertiesFactory();
+ Location location = Location.create(nodePath, targetNode.getUuid());
+
+ /*
+ * You can't remove any of the protected properties that the repository
provides by default, but you could
+ * remove custom properties.
+ */
+ if (JcrNtLexicon.FILE.equals(primaryTypeName)) {
+ customPropertiesFactory.recordFileProperties(context, getSourceName(),
location, fileFor(nodePath), properties);
+ } else if (DnaLexicon.RESOURCE.equals(primaryTypeName)) {
+ File file = fileFor(nodePath.getParent());
+ customPropertiesFactory.recordResourceProperties(context,
getSourceName(), location, file, properties);
+ } else {
+ File file = fileFor(nodePath);
+ customPropertiesFactory.recordDirectoryProperties(context,
getSourceName(), location, file, properties);
+ }
+
+ return getNode(nodePath);
+ }
+
+ @Override
+ public PathNode setProperties( ExecutionContext context,
+ Path nodePath,
+ Iterable<Property> newProperties ) {
+ PathNode targetNode = getNode(nodePath);
+ if (targetNode == null) return null;
+ if (source.getCustomPropertiesFactory() == null) return targetNode;
+
+ Property primaryTypeProp = targetNode.getProperty(JcrLexicon.PRIMARY_TYPE);
+ Name primaryTypeName = (Name)primaryTypeProp.getFirstValue();
+ Map<Name, Property> properties = new HashMap<Name,
Property>(targetNode.getProperties());
+
+ for (Property newProperty : newProperties) {
+ properties.put(newProperty.getName(), newProperty);
+ }
+
+ CustomPropertiesFactory customPropertiesFactory =
source.customPropertiesFactory();
+ Location location = Location.create(nodePath, targetNode.getUuid());
+
+ /*
+ * You can't remove any of the protected properties that the repository
provides by default, but you could
+ * remove custom properties.
+ */
+ if (JcrNtLexicon.FILE.equals(primaryTypeName)) {
+ customPropertiesFactory.recordFileProperties(context, getSourceName(),
location, fileFor(nodePath), properties);
+ } else if (DnaLexicon.RESOURCE.equals(primaryTypeName)) {
+ File file = fileFor(nodePath.getParent());
+ customPropertiesFactory.recordResourceProperties(context,
getSourceName(), location, file, properties);
+ } else {
+ File file = fileFor(nodePath);
+ customPropertiesFactory.recordDirectoryProperties(context,
getSourceName(), location, file, properties);
+ }
+
+ return getNode(nodePath);
+ }
+
+ @Override
+ public PathNode moveNode( ExecutionContext context,
+ PathNode node,
+ Name desiredNewName,
+ WritablePathWorkspace originalWorkspace,
+ PathNode newParent,
+ PathNode beforeNode ) {
+ if (beforeNode != null) {
+ throw new
InvalidRequestException(FileSystemI18n.nodeOrderingNotSupported.text(getSourceName()));
+ }
+ return super.moveNode(context, node, desiredNewName, originalWorkspace,
newParent, beforeNode);
+ }
+
+ @Override
+ public Path getLowestExistingPath( Path path ) {
+ File file = workspaceRoot;
+ for (Path.Segment segment : path) {
+ String localName = segment.getName().getLocalName();
+ // Verify the segment is valid ...
+ if (segment.getIndex() > 1) {
+ break;
+ }
+
+ String defaultNamespaceUri =
context.getNamespaceRegistry().getDefaultNamespaceUri();
+ if (!segment.getName().getNamespaceUri().equals(defaultNamespaceUri)) {
+ break;
+ }
+
+ // The segment should exist as a child of the file ...
+ file = new File(file, localName);
+ if (!file.exists() || !file.canRead()) {
+ // Unable to complete the path, so prepare the exception by
determining the lowest path that exists ...
+ Path lowest = path;
+ while (lowest.getLastSegment() != segment) {
+ lowest = lowest.getParent();
+ }
+ return lowest.getParent();
+ }
+ }
+ // Shouldn't be able to get this far is path is truly invalid
+ return path;
+ }
+
+ @Override
+ public PathNode getNode( Path path ) {
+ Map<Name, Property> properties = new HashMap<Name, Property>();
+
+ PropertyFactory factory = context.getPropertyFactory();
+ PathFactory pathFactory = context.getValueFactories().getPathFactory();
+ DateTimeFactory dateFactory = context.getValueFactories().getDateFactory();
+ MimeTypeDetector mimeTypeDetector = context.getMimeTypeDetector();
+ CustomPropertiesFactory customPropertiesFactory =
source.customPropertiesFactory();
+ NamespaceRegistry registry = context.getNamespaceRegistry();
+ Location location = Location.create(path);
+
+ if (!path.isRoot() &&
JcrLexicon.CONTENT.equals(path.getLastSegment().getName())) {
+ File file = fileFor(path.getParent());
+ if (file == null) return null;
+ // Discover the mime type ...
+ String mimeType = null;
+ InputStream contents = null;
+ try {
+ contents = new BufferedInputStream(new FileInputStream(file));
+ mimeType = mimeTypeDetector.mimeTypeOf(file.getName(), contents);
+ if (mimeType == null) mimeType = DEFAULT_MIME_TYPE;
+ properties.put(JcrLexicon.MIMETYPE,
factory.create(JcrLexicon.MIMETYPE, mimeType));
+ } catch (IOException e) {
+ I18n msg = FileSystemI18n.couldNotReadData;
+ throw new RepositorySourceException(getSourceName(),
msg.text(getSourceName(),
+
getName(),
+
path.getString(registry)));
+ } finally {
+ if (contents != null) {
+ try {
+ contents.close();
+ } catch (IOException e) {
+ }
+ }
+ }
+
+ // First add any custom properties ...
+ Collection<Property> customProps =
customPropertiesFactory.getResourceProperties(context,
+
location,
+
file,
+
mimeType);
+ for (Property customProp : customProps) {
+ properties.put(customProp.getName(), customProp);
+ }
+
+ // The request is to get properties of the "jcr:content" child
node ...
+ // ... use the dna:resource node type. This is the same as nt:resource,
but is not referenceable
+ // since we cannot assume that we control all access to this file and can
track its movements
+ properties.put(JcrLexicon.PRIMARY_TYPE,
factory.create(JcrLexicon.PRIMARY_TYPE, DnaLexicon.RESOURCE));
+ properties.put(JcrLexicon.LAST_MODIFIED,
factory.create(JcrLexicon.LAST_MODIFIED,
+
dateFactory.create(file.lastModified())));
+ // Don't really know the encoding, either ...
+ // request.addProperty(factory.create(JcrLexicon.ENCODED,
stringFactory.create("UTF-8")));
+
+ // Now put the file's content into the "jcr:data" property
...
+ BinaryFactory binaryFactory =
context.getValueFactories().getBinaryFactory();
+ properties.put(JcrLexicon.DATA, factory.create(JcrLexicon.DATA,
binaryFactory.create(file)));
+ return new DefaultPathNode(path, null, properties,
Collections.<Segment>emptyList());
+ }
+
+ File file = fileFor(path);
+ if (file == null) return null;
+
+ if (file.isDirectory()) {
+ String[] childNames = file.list(source.filenameFilter());
+ Arrays.sort(childNames);
+
+ List<Segment> childSegments = new
ArrayList<Segment>(childNames.length);
+ for (String childName : childNames) {
+ childSegments.add(pathFactory.createSegment(childName));
+ }
+
+ Collection<Property> customProps =
customPropertiesFactory.getDirectoryProperties(context, location, file);
+ for (Property customProp : customProps) {
+ properties.put(customProp.getName(), customProp);
+ }
+
+ if (path.isRoot()) {
+ properties.put(JcrLexicon.PRIMARY_TYPE,
factory.create(JcrLexicon.PRIMARY_TYPE, DnaLexicon.ROOT));
+ return new DefaultPathNode(path, source.getRootNodeUuid(),
properties, childSegments);
+ }
+ properties.put(JcrLexicon.PRIMARY_TYPE,
factory.create(JcrLexicon.PRIMARY_TYPE, JcrNtLexicon.FOLDER));
+ return new DefaultPathNode(path, source.getRootNodeUuid(), properties,
childSegments);
+ }
+
+ Collection<Property> customProps =
customPropertiesFactory.getFileProperties(context, location, file);
+ for (Property customProp : customProps) {
+ properties.put(customProp.getName(), customProp);
+ }
+ properties.put(JcrLexicon.PRIMARY_TYPE,
factory.create(JcrLexicon.PRIMARY_TYPE, JcrNtLexicon.FILE));
+ properties.put(JcrLexicon.CREATED, factory.create(JcrLexicon.CREATED,
dateFactory.create(file.lastModified())));
+ return new DefaultPathNode(path, null, properties,
+
Collections.singletonList(pathFactory.createSegment(JcrLexicon.CONTENT)));
+
+ }
+
+ /**
+ * This utility files the existing {@link File} at the supplied path, and in the
process will verify that the path is
+ * actually valid.
+ * <p>
+ * Note that this connector represents a file as two nodes: a parent node with a
name that matches the file and a "
+ * <code>jcr:primaryType</code>" of
"<code>nt:file</code>"; and a child node with the name
"<code>jcr:content</code>
+ * " and a " <code>jcr:primaryType</code>" of
"<code>nt:resource</code>". The parent
"<code>nt:file</code>" node and its
+ * properties represents the file itself, whereas the child
"<code>nt:resource</code>" node and its properties represent
+ * the content of the file.
+ * </p>
+ * <p>
+ * As such, this method will return the File object for paths representing both
the parent "<code>nt:file</code>
+ * " and child " <code>nt:resource</code>" node.
+ * </p>
+ *
+ * @param path
+ * @return the existing {@link File file} for the path; or null if the path does
not represent an existing file and a
+ * {@link PathNotFoundException} was set as the {@link
Request#setError(Throwable) error} on the request
+ */
+ protected File fileFor( Path path ) {
+ assert path != null;
+ if (path.isRoot()) {
+ return workspaceRoot;
+ }
+ // See if the path is a "jcr:content" node ...
+ if (path.getLastSegment().getName().equals(JcrLexicon.CONTENT)) {
+ // We only want to use the parent path to find the actual file ...
+ path = path.getParent();
+ }
+ File file = workspaceRoot;
+ for (Path.Segment segment : path) {
+ String localName = segment.getName().getLocalName();
+ // Verify the segment is valid ...
+ if (segment.getIndex() > 1) {
+ I18n msg = FileSystemI18n.sameNameSiblingsAreNotAllowed;
+ throw new RepositorySourceException(getSourceName(),
msg.text(getSourceName()));
+ }
+
+ String defaultNamespaceUri =
context.getNamespaceRegistry().getDefaultNamespaceUri();
+ if (!segment.getName().getNamespaceUri().equals(defaultNamespaceUri)) {
+ I18n msg = FileSystemI18n.onlyTheDefaultNamespaceIsAllowed;
+ throw new RepositorySourceException(getSourceName(),
msg.text(getSourceName()));
+ }
+
+ // The segment should exist as a child of the file ...
+ file = new File(file, localName);
+ if (!file.exists() || !file.canRead()) {
+ return null;
+ }
+ }
+ assert file != null;
+ return file;
+ }
+
+ protected void ensureValidPathLength( File root ) {
+ ensureValidPathLength(root, 0);
+ }
+
+ /**
+ * Recursively checks if any of the files in the tree rooted at {@code root}
would exceed the {@link #maxPathLength
+ * maximum path length for the processor} if their paths were {@code delta}
characters longer. If any files would exceed
+ * this length, a {@link RepositorySourceException} is thrown.
+ *
+ * @param root the root of the tree to check; may be a file or directory but may
not be null
+ * @param delta the change in the length of the path to check. Used to
preemptively check whether moving a file or
+ * directory to a new path would violate path length rules
+ * @throws RepositorySourceException if any files in the tree rooted at {@code
root} would exceed this
+ * {@link #maxPathLength the maximum path length for this processor}
+ */
+ protected void ensureValidPathLength( File root,
+ int delta ) {
+ try {
+ int len = root.getCanonicalPath().length();
+ if (len > source.getMaxPathLength() - delta) {
+ String msg =
FileSystemI18n.maxPathLengthExceeded.text(source.getMaxPathLength(),
+
getSourceName(),
+
root.getCanonicalPath(),
+ delta);
+ throw new RepositorySourceException(getSourceName(), msg);
+ }
+
+ if (root.isDirectory()) {
+ for (File child : root.listFiles(source.filenameFilter())) {
+ ensureValidPathLength(child, delta);
+ }
+
+ }
+ } catch (IOException ioe) {
+ throw new RepositorySourceException(getSourceName(),
FileSystemI18n.getCanonicalPathFailed.text(), ioe);
+ }
+ }
+
+ }
+}
Property changes on:
trunk/extensions/dna-connector-filesystem/src/main/java/org/jboss/dna/connector/filesystem/FileSystemRepository.java
___________________________________________________________________
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Deleted:
trunk/extensions/dna-connector-filesystem/src/main/java/org/jboss/dna/connector/filesystem/FileSystemRequestProcessor.java
===================================================================
---
trunk/extensions/dna-connector-filesystem/src/main/java/org/jboss/dna/connector/filesystem/FileSystemRequestProcessor.java 2009-12-27
20:45:58 UTC (rev 1483)
+++
trunk/extensions/dna-connector-filesystem/src/main/java/org/jboss/dna/connector/filesystem/FileSystemRequestProcessor.java 2009-12-29
04:25:02 UTC (rev 1484)
@@ -1,1170 +0,0 @@
-/*
- * 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.connector.filesystem;
-
-import java.io.BufferedInputStream;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileOutputStream;
-import java.io.FilenameFilter;
-import java.io.IOException;
-import java.io.InputStream;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.UUID;
-import org.jboss.dna.common.i18n.I18n;
-import org.jboss.dna.common.util.FileUtil;
-import org.jboss.dna.graph.DnaLexicon;
-import org.jboss.dna.graph.ExecutionContext;
-import org.jboss.dna.graph.JcrLexicon;
-import org.jboss.dna.graph.JcrNtLexicon;
-import org.jboss.dna.graph.Location;
-import org.jboss.dna.graph.NodeConflictBehavior;
-import org.jboss.dna.graph.connector.RepositorySourceException;
-import org.jboss.dna.graph.mimetype.MimeTypeDetector;
-import org.jboss.dna.graph.property.Binary;
-import org.jboss.dna.graph.property.BinaryFactory;
-import org.jboss.dna.graph.property.DateTimeFactory;
-import org.jboss.dna.graph.property.Name;
-import org.jboss.dna.graph.property.NameFactory;
-import org.jboss.dna.graph.property.NamespaceRegistry;
-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.UuidFactory;
-import org.jboss.dna.graph.property.Path.Segment;
-import org.jboss.dna.graph.request.CloneBranchRequest;
-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.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.RenameNodeRequest;
-import org.jboss.dna.graph.request.Request;
-import org.jboss.dna.graph.request.UpdatePropertiesRequest;
-import org.jboss.dna.graph.request.VerifyWorkspaceRequest;
-import org.jboss.dna.graph.request.processor.RequestProcessor;
-
-/**
- * The {@link RequestProcessor} implementation for the file systme connector. This is the
class that does the bulk of the work in
- * the file system connector, since it processes all requests.
- */
-public class FileSystemRequestProcessor extends RequestProcessor {
-
- private static final String DEFAULT_MIME_TYPE = "application/octet";
-
- private final String defaultNamespaceUri;
- private final Map<String, File> availableWorkspaces;
- private final boolean creatingWorkspacesAllowed;
- private final String defaultWorkspaceName;
- private final File workspaceRootPath;
- private final int maxPathLength;
- private final FilenameFilter filenameFilter;
- private final boolean updatesAllowed;
- private final MimeTypeDetector mimeTypeDetector;
- private final UUID rootNodeUuid;
- private final CustomPropertiesFactory customPropertiesFactory;
- private final FileSystemTransaction txn;
-
- /**
- * @param sourceName
- * @param defaultWorkspaceName
- * @param availableWorkspaces
- * @param creatingWorkspacesAllowed
- * @param context
- * @param rootNodeUuid the UUID for the root node in this workspace; may be null. If
not specified, a random UUID will be
- * generated each time that the repository is started.
- * @param workspaceRootPath the path to the workspace root directory; may be null. If
specified, all workspace names will be
- * treated as relative paths from this directory.
- * @param maxPathLength the maximum absolute path length supported by this processor
- * @param filenameFilter the filename filter to use to restrict the allowable nodes,
or null if all files/directories are to
- * be exposed by this connector
- * @param updatesAllowed true if this connector supports updating the file system, or
false if the connector is readonly
- * @param customPropertiesFactory the factory that should be used to create custom
properties for "nt:folder", "nt:file", and
- * "nt:resource" nodes
- * @param txn the file system transaction that is used to record the changes and
perform any rollback
- */
- protected FileSystemRequestProcessor( String sourceName,
- String defaultWorkspaceName,
- Map<String, File> availableWorkspaces,
- boolean creatingWorkspacesAllowed,
- UUID rootNodeUuid,
- String workspaceRootPath,
- int maxPathLength,
- ExecutionContext context,
- FilenameFilter filenameFilter,
- boolean updatesAllowed,
- CustomPropertiesFactory
customPropertiesFactory,
- FileSystemTransaction txn ) {
- super(sourceName, context, null);
- assert defaultWorkspaceName != null;
- assert availableWorkspaces != null;
- assert rootNodeUuid != null;
- assert customPropertiesFactory != null;
- this.availableWorkspaces = availableWorkspaces;
- this.creatingWorkspacesAllowed = creatingWorkspacesAllowed;
- this.defaultNamespaceUri =
getExecutionContext().getNamespaceRegistry().getDefaultNamespaceUri();
- this.rootNodeUuid = rootNodeUuid;
- this.maxPathLength = maxPathLength;
- this.filenameFilter = filenameFilter;
- this.defaultWorkspaceName = defaultWorkspaceName;
- this.updatesAllowed = updatesAllowed;
- this.mimeTypeDetector = context.getMimeTypeDetector();
- this.customPropertiesFactory = customPropertiesFactory;
- this.txn = txn;
- assert this.txn != null;
-
- if (workspaceRootPath != null) {
- this.workspaceRootPath = new File(workspaceRootPath);
- if (!this.workspaceRootPath.exists()) {
- throw new
IllegalStateException(FileSystemI18n.pathForWorkspaceRootDoesNotExist.text(workspaceRootPath,
-
sourceName));
- }
- if (!this.workspaceRootPath.isDirectory()) {
- throw new
IllegalStateException(FileSystemI18n.pathForWorkspaceRootIsNotDirectory.text(workspaceRootPath,
-
sourceName));
- }
- if (!this.workspaceRootPath.canRead()) {
- throw new
IllegalStateException(FileSystemI18n.pathForWorkspaceRootCannotBeRead.text(workspaceRootPath,
-
sourceName));
- }
- } else {
- this.workspaceRootPath = null;
- }
- }
-
- /**
- * {@inheritDoc}
- *
- * @see
org.jboss.dna.graph.request.processor.RequestProcessor#process(org.jboss.dna.graph.request.ReadAllChildrenRequest)
- */
- @Override
- public void process( ReadAllChildrenRequest request ) {
-
- // Get the java.io.File object that represents the workspace ...
- File workspaceRoot = getWorkspaceDirectory(request.inWorkspace());
- if (workspaceRoot == null) {
- request.setError(new
InvalidWorkspaceException(FileSystemI18n.workspaceDoesNotExist.text(request.inWorkspace())));
- return;
- }
-
- // Find the existing file for the parent ...
- Location location = request.of();
- Path parentPath = getPathFor(location, request);
-
- if (parentPath.isRoot()) {
- if (!location.hasPath()) location = location.with(parentPath);
- }
-
- File parent = getExistingFileFor(workspaceRoot, parentPath, location, request);
- if (parent == null) {
- // An error was set on the request
- assert request.hasError();
- return;
- }
- // Decide how to represent the children ...
- if (parent.isDirectory()) {
- // Create a Location for each file and directory contained by the parent
directory ...
- PathFactory pathFactory = pathFactory();
- NameFactory nameFactory = nameFactory();
- for (File child : parent.listFiles(filenameFilter)) {
- if (!child.canRead()) continue;
- Name childName = nameFactory.create(defaultNamespaceUri,
child.getName());
- Path childPath = pathFactory.create(parentPath, childName);
- request.addChild(Location.create(childPath));
- }
- } else {
- // The parent is a java.io.File, and the path may refer to the node that is
either the "nt:file" parent
- // node, or the child "jcr:content" node...
- if (!parentPath.getLastSegment().getName().equals(JcrLexicon.CONTENT)) {
- // This node represents the "nt:file" parent node, so the only
child is the "jcr:content" node ...
- Path contentPath = pathFactory().create(parentPath, JcrLexicon.CONTENT);
- Location content = Location.create(contentPath);
- request.addChild(content);
- }
- // otherwise, the path ends in "jcr:content", and there are no
children
- }
- request.setActualLocationOfNode(location);
- setCacheableInfo(request);
- }
-
- /**
- * {@inheritDoc}
- *
- * @see
org.jboss.dna.graph.request.processor.RequestProcessor#process(org.jboss.dna.graph.request.ReadAllPropertiesRequest)
- */
- @Override
- public void process( ReadAllPropertiesRequest request ) {
-
- // Get the java.io.File object that represents the workspace ...
- File workspaceRoot = getWorkspaceDirectory(request.inWorkspace());
- if (workspaceRoot == null) {
- request.setError(new
InvalidWorkspaceException(FileSystemI18n.workspaceDoesNotExist.text(request.inWorkspace())));
- return;
- }
-
- PropertyFactory factory = getExecutionContext().getPropertyFactory();
-
- // Find the existing file for the parent ...
- Location location = request.at();
- Path path = getPathFor(location, request);
- if (path.isRoot()) {
- // Root nodes can be requested by UUID, path, or both
- if (!location.hasPath()) location = location.with(path);
- if (location.getUuid() == null) location = location.with(rootNodeUuid);
-
- request.addProperty(factory.create(JcrLexicon.PRIMARY_TYPE,
DnaLexicon.ROOT));
- request.setActualLocationOfNode(location);
- setCacheableInfo(request);
-
- return;
- }
-
- File file = getExistingFileFor(workspaceRoot, path, location, request);
- if (file == null) {
- // An error was set on the request
- assert request.hasError();
- return;
- }
- // Generate the properties for this File object ...
- final ExecutionContext context = getExecutionContext();
- final DateTimeFactory dateFactory =
context.getValueFactories().getDateFactory();
- // Note that we don't have 'created' timestamps, just last modified,
so we'll have to use them
- if (file.isDirectory()) {
- // Add properties for the directory ...
- request.addProperties(customPropertiesFactory.getDirectoryProperties(context,
location, file));
- request.addProperty(factory.create(JcrLexicon.PRIMARY_TYPE,
JcrNtLexicon.FOLDER));
- request.addProperty(factory.create(JcrLexicon.CREATED,
dateFactory.create(file.lastModified())));
- } else {
- // It is a file, but ...
- if (path.getLastSegment().getName().equals(JcrLexicon.CONTENT)) {
- // Discover the mime type ...
- String mimeType = null;
- InputStream contents = null;
- boolean mimeTypeError = false;
- try {
- contents = new BufferedInputStream(new FileInputStream(file));
- mimeType = mimeTypeDetector.mimeTypeOf(file.getName(), contents);
- if (mimeType == null) mimeType = DEFAULT_MIME_TYPE;
- request.addProperty(factory.create(JcrLexicon.MIMETYPE, mimeType));
- } catch (IOException e) {
- mimeTypeError = true;
- request.setError(e);
- } finally {
- if (contents != null) {
- try {
- contents.close();
- } catch (IOException e) {
- if (!mimeTypeError) request.setError(e);
- }
- }
- }
-
- // First add any custom properties ...
-
request.addProperties(customPropertiesFactory.getResourceProperties(context, location,
file, mimeType));
-
- // The request is to get properties of the "jcr:content" child
node ...
- // ... use the dna:resource node type. This is the same as nt:resource,
but is not referenceable
- // since we cannot assume that we control all access to this file and can
track its movements
- request.addProperty(factory.create(JcrLexicon.PRIMARY_TYPE,
DnaLexicon.RESOURCE));
- request.addProperty(factory.create(JcrLexicon.LAST_MODIFIED,
dateFactory.create(file.lastModified())));
- // Don't really know the encoding, either ...
- // request.addProperty(factory.create(JcrLexicon.ENCODED,
stringFactory.create("UTF-8")));
-
- // Now put the file's content into the "jcr:data" property
...
- BinaryFactory binaryFactory =
context.getValueFactories().getBinaryFactory();
- request.addProperty(factory.create(JcrLexicon.DATA,
binaryFactory.create(file)));
-
- } else {
- // First add any custom properties ...
- request.addProperties(customPropertiesFactory.getFileProperties(context,
location, file));
-
- // The request is to get properties for the node representing the file
- request.addProperty(factory.create(JcrLexicon.PRIMARY_TYPE,
JcrNtLexicon.FILE));
- request.addProperty(factory.create(JcrLexicon.CREATED,
dateFactory.create(file.lastModified())));
- }
-
- }
- request.setActualLocationOfNode(location);
- setCacheableInfo(request);
- }
-
- /**
- * {@inheritDoc}
- *
- * @see
org.jboss.dna.graph.request.processor.RequestProcessor#process(org.jboss.dna.graph.request.CreateNodeRequest)
- */
- @Override
- public void process( CreateNodeRequest request ) {
- if (!updatesAllowed(request)) return;
-
- Path parentPath = getPathFor(request.under(), request);
- if (parentPath == null) return;
-
- File workspace = getWorkspaceDirectory(request.inWorkspace());
- assert workspace != null;
-
- File parent = getExistingFileFor(workspace, parentPath, request.under(),
request);
- if (parent == null) {
- // Error was already set on request in getExistingFileFor
- return;
- }
-
- NamespaceRegistry registry = getExecutionContext().getNamespaceRegistry();
- String newName = request.named().getString(registry);
- File newFile = new File(parent, newName);
-
- Map<Name, Property> properties = new HashMap<Name,
Property>(request.properties().size());
- for (Property property : request.properties()) {
- properties.put(property.getName(), property);
- }
-
- Property primaryTypeProp = properties.get(JcrLexicon.PRIMARY_TYPE);
- Name primaryType = primaryTypeProp == null ? null :
nameFactory().create(primaryTypeProp.getFirstValue());
-
- Path newPath = pathFactory().create(parentPath, request.named());
- Location actualLocation = Location.create(newPath);
- if (JcrNtLexicon.FILE.equals(primaryType)) {
-
- // The FILE node is represented by the existence of the file
- if (!parent.canWrite()) {
- I18n msg = FileSystemI18n.parentIsReadOnly;
- request.setError(new RepositorySourceException(getSourceName(),
msg.text(parent.getPath(),
-
request.inWorkspace(),
-
getSourceName())));
- return;
- }
-
- try {
- ensureValidPathLength(newFile);
- boolean skipWrite = false;
-
- if (newFile.exists()) {
- if (request.conflictBehavior().equals(NodeConflictBehavior.APPEND))
{
- I18n msg = FileSystemI18n.sameNameSiblingsAreNotAllowed;
- throw new InvalidRequestException(msg.text(this.getSourceName(),
newName));
- } else if
(request.conflictBehavior().equals(NodeConflictBehavior.DO_NOT_REPLACE)) {
- skipWrite = true;
- }
- }
-
- // Don't try to write if the node conflict behavior is
DO_NOT_REPLACE
- if (!skipWrite) {
- if (!newFile.createNewFile()) {
- I18n msg = FileSystemI18n.fileAlreadyExists;
- request.setError(new RepositorySourceException(getSourceName(),
msg.text(parent.getPath(),
-
request.inWorkspace(),
-
getSourceName())));
- return;
- }
- }
- } catch (IOException ioe) {
- I18n msg = FileSystemI18n.couldNotCreateFile;
- request.setError(new RepositorySourceException(getSourceName(),
msg.text(parent.getPath(),
-
request.inWorkspace(),
-
getSourceName(),
-
ioe.getMessage()), ioe));
- return;
- }
- customPropertiesFactory.recordFileProperties(getExecutionContext(),
- getSourceName(),
- actualLocation,
- newFile,
- properties);
- } else if (JcrNtLexicon.RESOURCE.equals(primaryType) ||
DnaLexicon.RESOURCE.equals(primaryType)) {
- if (!JcrLexicon.CONTENT.equals(request.named())) {
- I18n msg = FileSystemI18n.invalidNameForResource;
- String nodeName = request.named().getString(registry);
- request.setError(new RepositorySourceException(getSourceName(),
msg.text(parent.getPath(),
-
request.inWorkspace(),
-
getSourceName(),
-
nodeName)));
- return;
- }
-
- if (!parent.isFile()) {
- I18n msg = FileSystemI18n.invalidPathForResource;
- request.setError(new RepositorySourceException(getSourceName(),
msg.text(parent.getPath(),
-
request.inWorkspace(),
-
getSourceName())));
- return;
- }
-
- if (!parent.canWrite()) {
- I18n msg = FileSystemI18n.parentIsReadOnly;
- request.setError(new RepositorySourceException(getSourceName(),
msg.text(parent.getPath(),
-
request.inWorkspace(),
-
getSourceName())));
- return;
- }
-
- boolean skipWrite = false;
-
- if (parent.exists()) {
- if (request.conflictBehavior().equals(NodeConflictBehavior.APPEND)) {
- I18n msg = FileSystemI18n.sameNameSiblingsAreNotAllowed;
- throw new InvalidRequestException(msg.text(this.getSourceName(),
newName));
- } else if
(request.conflictBehavior().equals(NodeConflictBehavior.DO_NOT_REPLACE)) {
- // The content node logically maps to the file contents. If there are
file contents, don't replace them.
- FileInputStream checkForContents = null;
- try {
- checkForContents = new FileInputStream(parent);
- if (-1 != checkForContents.read()) skipWrite = true;
-
- } catch (IOException ignore) {
-
- } finally {
- try {
- if (checkForContents != null) checkForContents.close();
- } catch (Exception ignore) {
- }
- }
- skipWrite = true;
- }
- }
-
- if (!skipWrite) {
- // Copy over data into a temp file, then move it to the correct location
- FileOutputStream fos = null;
- try {
- File temp = File.createTempFile("dna", null);
- fos = new FileOutputStream(temp);
-
- Property dataProp = properties.get(JcrLexicon.DATA);
- if (dataProp == null) {
- I18n msg = FileSystemI18n.missingRequiredProperty;
- String dataPropName = JcrLexicon.DATA.getString(registry);
- request.setError(new RepositorySourceException(getSourceName(),
msg.text(parent.getPath(),
-
request.inWorkspace(),
-
getSourceName(),
-
dataPropName)));
- return;
- }
-
- BinaryFactory binaryFactory =
getExecutionContext().getValueFactories().getBinaryFactory();
- Binary binary =
binaryFactory.create(properties.get(JcrLexicon.DATA).getFirstValue());
- InputStream is = binary.getStream();
-
- final int BUFF_SIZE = 2 << 15;
- byte[] buff = new byte[BUFF_SIZE];
- int len;
- while (-1 != (len = is.read(buff, 0, BUFF_SIZE))) {
- fos.write(buff, 0, len);
- }
- fos.flush();
- fos.close();
- is.close();
-
- if (!FileUtil.delete(parent)) {
- I18n msg = FileSystemI18n.deleteFailed;
- request.setError(new RepositorySourceException(getSourceName(),
msg.text(parent.getPath(),
-
request.inWorkspace(),
-
getSourceName())));
- return;
- }
-
- if (!temp.renameTo(parent)) {
- I18n msg = FileSystemI18n.couldNotUpdateData;
- request.setError(new RepositorySourceException(getSourceName(),
msg.text(parent.getPath(),
-
request.inWorkspace(),
-
getSourceName())));
- return;
-
- }
- } catch (IOException ioe) {
- I18n msg = FileSystemI18n.couldNotWriteData;
- request.setError(new RepositorySourceException(getSourceName(),
msg.text(parent.getPath(),
-
request.inWorkspace(),
-
getSourceName(),
-
ioe.getMessage()), ioe));
- return;
-
- } finally {
- try {
- if (fos != null) fos.close();
- } catch (Exception ex) {
- }
- }
- }
- customPropertiesFactory.recordResourceProperties(getExecutionContext(),
- getSourceName(),
- actualLocation,
- newFile,
- properties);
-
- } else if (JcrNtLexicon.FOLDER.equals(primaryType) || primaryType == null) {
- ensureValidPathLength(newFile);
-
- if (!newFile.mkdir()) {
- I18n msg = FileSystemI18n.couldNotCreateFile;
- request.setError(new RepositorySourceException(
- getSourceName(),
-
msg.text(parent.getPath(),
-
request.inWorkspace(),
- getSourceName(),
- primaryType ==
null ? "null" : primaryType.getString(registry))));
- return;
- }
- customPropertiesFactory.recordDirectoryProperties(getExecutionContext(),
- getSourceName(),
- actualLocation,
- newFile,
- properties);
- } else {
- // Set error and return
- I18n msg = FileSystemI18n.unsupportedPrimaryType;
- request.setError(new RepositorySourceException(getSourceName(),
msg.text(primaryType.getString(registry),
-
parent.getPath(),
-
request.inWorkspace(),
-
getSourceName())));
- return;
- }
-
- request.setActualLocationOfNode(actualLocation);
- }
-
- /**
- * {@inheritDoc}
- *
- * @see
org.jboss.dna.graph.request.processor.RequestProcessor#process(org.jboss.dna.graph.request.UpdatePropertiesRequest)
- */
- @Override
- public void process( UpdatePropertiesRequest request ) {
- if (!updatesAllowed(request)) return;
-
- Path path = request.on().getPath();
- File workspace = getWorkspaceDirectory(request.inWorkspace());
- File target = getExistingFileFor(workspace, path, request.on(), request);
-
- if (!target.exists()) {
- // getExistingFile fills in the PathNotFoundException for non-existent files
- assert request.hasError();
- return;
- }
-
- Location location = request.on();
- Set<Name> createdProperties = null;
- if (target.isFile()) {
- if (path.endsWith(JcrLexicon.CONTENT)) {
- createdProperties =
customPropertiesFactory.recordResourceProperties(getExecutionContext(),
-
getSourceName(),
-
location,
-
target,
-
request.properties());
- } else {
- createdProperties =
customPropertiesFactory.recordFileProperties(getExecutionContext(),
-
getSourceName(),
-
location,
- target,
-
request.properties());
- }
- } else {
- assert target.isDirectory();
- createdProperties =
customPropertiesFactory.recordDirectoryProperties(getExecutionContext(),
-
getSourceName(),
-
location,
-
target,
-
request.properties());
- }
-
- request.setActualLocationOfNode(location);
- if (createdProperties != null) {
- request.setNewProperties(createdProperties);
- }
- }
-
- /**
- * {@inheritDoc}
- *
- * @see
org.jboss.dna.graph.request.processor.RequestProcessor#process(org.jboss.dna.graph.request.CopyBranchRequest)
- */
- @Override
- public void process( CopyBranchRequest request ) {
- if (!updatesAllowed(request)) return;
-
- File fromWorkspace = getWorkspaceDirectory(request.fromWorkspace());
- File intoWorkspace = getWorkspaceDirectory(request.intoWorkspace());
- Path fromPath = getPathFor(request.from(), request);
- if (fromPath == null) return;
- File from = getExistingFileFor(fromWorkspace, fromPath, request.from(),
request);
-
- Path intoPath = getPathFor(request.into(), request);
- if (intoPath == null) return;
- File into = getExistingFileFor(intoWorkspace, intoPath, request.into(),
request);
-
- NamespaceRegistry registry = getExecutionContext().getNamespaceRegistry();
- Name desiredName = request.desiredName();
- String fileName = desiredName != null ? desiredName.getString(registry) :
fromPath.getLastSegment().getString(registry);
- File target = new File(into, fileName);
- File tempInto = null;
-
- Location actualFrom = null;
- Location actualTo = null;
- try {
- actualFrom = locationFor(fromWorkspace, from);
- actualTo = locationFor(intoWorkspace, target);
- } catch (IOException ioe) {
- throw new RepositorySourceException(this.getSourceName(),
FileSystemI18n.getCanonicalPathFailed.text(), ioe);
- }
-
- try {
- int pathLenDelta = into.getCanonicalPath().length() -
from.getCanonicalFile().getParent().length();
- if (pathLenDelta > 0) {
- ensureValidPathLength(from, pathLenDelta);
- }
- } catch (IOException ioe) {
- throw new RepositorySourceException(this.getSourceName(),
FileSystemI18n.getCanonicalPathFailed.text(), ioe);
- }
-
- if (target.exists() && from.isFile()) {
- try {
- tempInto = File.createTempFile("dna", null, into);
- } catch (IOException ioe) {
- throw new RepositorySourceException(this.getSourceName(),
-
FileSystemI18n.couldNotCreateFile.text("temporary file",
-
request.intoWorkspace(),
-
getSourceName(),
-
ioe.getMessage()), ioe);
- }
-
- try {
- FileUtil.copy(from, tempInto);
- } catch (IOException ioe) {
- FileUtil.delete(tempInto);
- throw new RepositorySourceException(this.getSourceName(),
FileSystemI18n.copyFailed.text(from.getPath(),
-
request.fromWorkspace(),
-
tempInto.getPath(),
-
request.intoWorkspace(),
-
getSourceName()), ioe);
- }
-
- // If everything worked, delete whatever was there and rename
- if (target.exists()) {
- if (!FileUtil.delete(target)) {
- I18n msg = FileSystemI18n.deleteFailed;
- request.setError(new RepositorySourceException(getSourceName(),
msg.text(target.getPath(),
-
request.intoWorkspace(),
-
getSourceName())));
- FileUtil.delete(tempInto);
- return;
- }
- }
-
- if (!tempInto.renameTo(target)) {
- I18n msg = FileSystemI18n.couldNotUpdateData;
- request.setError(new RepositorySourceException(getSourceName(),
msg.text(target.getPath(),
-
request.intoWorkspace(),
-
getSourceName())));
- FileUtil.delete(tempInto);
- return;
- }
- } else {
- if (!from.renameTo(target)) {
- I18n msg = FileSystemI18n.couldNotUpdateData;
- request.setError(new RepositorySourceException(getSourceName(),
msg.text(target.getPath(),
-
request.intoWorkspace(),
-
getSourceName())));
- return;
- }
-
- }
- request.setActualLocations(actualFrom, actualTo);
-
- }
-
- /**
- * {@inheritDoc}
- *
- * @see
org.jboss.dna.graph.request.processor.RequestProcessor#process(org.jboss.dna.graph.request.CloneBranchRequest)
- */
- @Override
- public void process( CloneBranchRequest request ) {
- if (!updatesAllowed(request)) return;
-
- CopyBranchRequest copy = new CopyBranchRequest(request.from(),
request.fromWorkspace(), request.into(),
- request.intoWorkspace(),
request.desiredName());
-
- process(copy);
-
- if (copy.hasError()) {
- request.setError(copy.getError());
- return;
- }
-
- request.setActualLocations(copy.getActualLocationBefore(),
copy.getActualLocationAfter());
- request.setRemovedNodes(null);
- }
-
- /**
- * {@inheritDoc}
- *
- * @see
org.jboss.dna.graph.request.processor.RequestProcessor#process(org.jboss.dna.graph.request.DeleteBranchRequest)
- */
- @Override
- public void process( DeleteBranchRequest request ) {
- if (!updatesAllowed(request)) return;
-
- File workspace = getWorkspaceDirectory(request.inWorkspace());
- Path targetPath = getPathFor(request.at(), request);
- if (targetPath == null) return;
-
- File target = getExistingFileFor(workspace, targetPath, request.at(), request);
- Location actual = null;
-
- try {
- actual = locationFor(workspace, target);
- } catch (IOException ioe) {
- throw new RepositorySourceException(this.getSourceName(),
FileSystemI18n.getCanonicalPathFailed.text(), ioe);
- }
-
- if (!FileUtil.delete(target)) {
- request.setError(new RepositorySourceException(this.getSourceName(),
-
FileSystemI18n.deleteFailed.text(target.getPath(),
-
request.inWorkspace(),
-
getSourceName())));
- return;
- }
-
- request.setActualLocationOfNode(actual);
- }
-
- /**
- * {@inheritDoc}
- *
- * @see
org.jboss.dna.graph.request.processor.RequestProcessor#process(org.jboss.dna.graph.request.MoveBranchRequest)
- */
- @Override
- public void process( MoveBranchRequest request ) {
- if (!updatesAllowed(request)) return;
-
- /* This connector does not support node ordering */
- if (request.before() != null) {
- throw new
InvalidRequestException(FileSystemI18n.nodeOrderingNotSupported.text(this.getSourceName()));
- }
-
- File workspace = getWorkspaceDirectory(request.inWorkspace());
- Path fromPath = getPathFor(request.from(), request);
- if (fromPath == null) return;
- File from = getExistingFileFor(workspace, fromPath, request.from(), request);
-
- Path intoPath = getPathFor(request.into(), request);
- if (intoPath == null) return;
- File into = getExistingFileFor(workspace, intoPath, request.into(), request);
-
- NamespaceRegistry registry = getExecutionContext().getNamespaceRegistry();
- Name desiredName = request.desiredName();
- String fileName = desiredName != null ? desiredName.getString(registry) :
fromPath.getLastSegment().getString(registry);
- File target = new File(into, fileName);
-
- Location actualFrom = null;
- Location actualTo = null;
- try {
- actualFrom = locationFor(workspace, from);
- actualTo = locationFor(workspace, target);
- } catch (IOException ioe) {
- request.setError(new RepositorySourceException(this.getSourceName(),
FileSystemI18n.getCanonicalPathFailed.text()));
- return;
- }
-
- try {
- int pathLenDelta = into.getCanonicalPath().length() -
from.getCanonicalFile().getParent().length();
- if (pathLenDelta > 0) {
- ensureValidPathLength(from, pathLenDelta);
- }
- } catch (IOException ioe) {
- request.setError(new RepositorySourceException(this.getSourceName(),
FileSystemI18n.getCanonicalPathFailed.text()));
- return;
- }
-
- if (!from.renameTo(target)) {
- I18n msg = FileSystemI18n.couldNotUpdateData;
- request.setError(new RepositorySourceException(getSourceName(),
- msg.text(target.getPath(),
workspace, getSourceName())));
- return;
- }
-
- request.setActualLocations(actualFrom, actualTo);
-
- }
-
- /**
- * {@inheritDoc}
- *
- * @see
org.jboss.dna.graph.request.processor.RequestProcessor#process(org.jboss.dna.graph.request.RenameNodeRequest)
- */
- @Override
- public void process( RenameNodeRequest request ) {
- if (!updatesAllowed(request)) return;
-
- super.process(request);
- }
-
- /**
- * {@inheritDoc}
- *
- * @see
org.jboss.dna.graph.request.processor.RequestProcessor#process(org.jboss.dna.graph.request.CloneWorkspaceRequest)
- */
- @Override
- public void process( CloneWorkspaceRequest request ) {
- if (!updatesAllowed(request)) return;
-
- CreateWorkspaceRequest create = new
CreateWorkspaceRequest(request.desiredNameOfTargetWorkspace(),
-
request.targetConflictBehavior());
- process(create);
-
- if (create.hasError()) {
- request.setError(create.getError());
- return;
- }
-
- File fromWorkspace = getWorkspaceDirectory(request.nameOfWorkspaceToBeCloned());
- assert fromWorkspace != null;
- File toWorkspace = getWorkspaceDirectory(create.getActualWorkspaceName());
- assert toWorkspace != null;
-
- try {
- FileUtil.copy(fromWorkspace, toWorkspace);
- request.setActualWorkspaceName(create.getActualWorkspaceName());
- request.setActualRootLocation(Location.create(pathFactory().createRootPath(),
this.rootNodeUuid));
- } catch (IOException ioe) {
- throw new RepositorySourceException(this.getSourceName(), ioe.getMessage());
- }
- }
-
- /**
- * {@inheritDoc}
- *
- * @see
org.jboss.dna.graph.request.processor.RequestProcessor#process(org.jboss.dna.graph.request.VerifyWorkspaceRequest)
- */
- @Override
- public void process( VerifyWorkspaceRequest request ) {
- // If the request contains a null name, then we use the default ...
- String workspaceName = request.workspaceName();
- if (workspaceName == null) workspaceName = defaultWorkspaceName;
-
- if (!this.creatingWorkspacesAllowed) {
- // Then the workspace name must be one of the available names ...
-
- boolean found = false;
- for (String available : this.availableWorkspaces.keySet()) {
- if (workspaceName.equals(available)) {
- found = true;
- break;
- }
- }
-
- if (!found) {
- request.setError(new
InvalidWorkspaceException(FileSystemI18n.workspaceDoesNotExist.text(workspaceName)));
- return;
- }
- // We know it is an available workspace, so just continue ...
- }
- // Verify that there is a directory at the path given by the workspace name ...
- File directory = availableWorkspaces.get(workspaceName);
- if (directory.exists() && directory.isDirectory() &&
directory.canRead()) {
- request.setActualWorkspaceName(workspaceName);
-
request.setActualRootLocation(Location.create(pathFactory().createRootPath()));
- } else {
- request.setError(new
InvalidWorkspaceException(FileSystemI18n.workspaceDoesNotExist.text(workspaceName)));
- }
- }
-
- /**
- * {@inheritDoc}
- *
- * @see
org.jboss.dna.graph.request.processor.RequestProcessor#process(org.jboss.dna.graph.request.GetWorkspacesRequest)
- */
- @Override
- public void process( GetWorkspacesRequest request ) {
- // Return the set of available workspace names, even if new workspaces can be
created ...
- Set<String> names = new HashSet<String>();
- for (Map.Entry<String, File> entry : this.availableWorkspaces.entrySet())
{
- File directory = entry.getValue();
- if (directory.exists() && directory.isDirectory() &&
directory.canRead()) {
- names.add(entry.getKey());
- }
- }
-
- request.setAvailableWorkspaceNames(Collections.unmodifiableSet(names));
- }
-
- /**
- * Utility method to return the canonical path (without "." and
".." segments) for a file.
- *
- * @param directory the directory; may not be null
- * @return the canonical path, or if there is an error the absolute path
- */
- protected String getCanonicalWorkspaceName( File directory ) {
- try {
- if (this.workspaceRootPath != null) {
- String directoryCanonicalPath = directory.getCanonicalPath();
- String rootCanonicalPath = workspaceRootPath.getCanonicalPath();
- assert directoryCanonicalPath.startsWith(rootCanonicalPath);
- return directoryCanonicalPath.substring(rootCanonicalPath.length() + 1);
- }
- return directory.getCanonicalPath();
- } catch (IOException e) {
- return directory.getAbsolutePath();
- }
- }
-
- /**
- * {@inheritDoc}
- *
- * @see
org.jboss.dna.graph.request.processor.RequestProcessor#process(org.jboss.dna.graph.request.CreateWorkspaceRequest)
- */
- @Override
- public void process( CreateWorkspaceRequest request ) {
- final String workspaceName = request.desiredNameOfNewWorkspace();
- if (!creatingWorkspacesAllowed) {
- String msg = FileSystemI18n.unableToCreateWorkspaces.text(getSourceName(),
workspaceName);
- request.setError(new InvalidRequestException(msg));
- return;
- }
- // This doesn't create the directory representing the workspace (it must
already exist), but it will add
- // the workspace name to the available names ...
- File directory = getWorkspaceDirectory(workspaceName);
- if (directory.exists() && directory.isDirectory() &&
directory.canRead()) {
- request.setActualWorkspaceName(getCanonicalWorkspaceName(directory));
-
request.setActualRootLocation(Location.create(pathFactory().createRootPath()));
- availableWorkspaces.put(workspaceName, directory);
- recordChange(request);
- } else {
- request.setError(new
InvalidWorkspaceException(FileSystemI18n.workspaceDoesNotExist.text(workspaceName)));
- }
- }
-
- /**
- * {@inheritDoc}
- *
- * @see
org.jboss.dna.graph.request.processor.RequestProcessor#process(org.jboss.dna.graph.request.DestroyWorkspaceRequest)
- */
- @Override
- public void process( DestroyWorkspaceRequest request ) {
- final String workspaceName = request.workspaceName();
- if (!creatingWorkspacesAllowed) {
- String msg = FileSystemI18n.unableToCreateWorkspaces.text(getSourceName(),
workspaceName);
- request.setError(new InvalidRequestException(msg));
- }
- // This doesn't delete the file/directory; rather, it just remove the
workspace from the available set ...
- if (this.availableWorkspaces.remove(workspaceName) == null) {
- request.setError(new
InvalidWorkspaceException(FileSystemI18n.workspaceDoesNotExist.text(workspaceName)));
- } else {
-
request.setActualRootLocation(Location.create(pathFactory().createRootPath()));
- recordChange(request);
- }
- }
-
- protected boolean updatesAllowed( Request request ) {
- if (!updatesAllowed) {
- request.setError(new
InvalidRequestException(FileSystemI18n.sourceIsReadOnly.text(getSourceName())));
- }
- return !request.hasError();
- }
-
- private UUID uuidFor( Location location ) {
- if (location.getUuid() != null) return location.getUuid();
- if (!location.hasIdProperties()) return null;
-
- for (Property idProperty : location.getIdProperties()) {
- if (JcrLexicon.UUID.equals(idProperty.getName())) {
- return uuidFactory().create(idProperty.getFirstValue());
- }
- }
-
- return null;
- }
-
- protected NameFactory nameFactory() {
- return getExecutionContext().getValueFactories().getNameFactory();
- }
-
- protected PathFactory pathFactory() {
- return getExecutionContext().getValueFactories().getPathFactory();
- }
-
- protected UuidFactory uuidFactory() {
- return getExecutionContext().getValueFactories().getUuidFactory();
- }
-
- protected void ensureValidPathLength( File root ) {
- ensureValidPathLength(root, 0);
- }
-
- /**
- * Recursively checks if any of the files in the tree rooted at {@code root} would
exceed the {@link #maxPathLength maximum
- * path length for the processor} if their paths were {@code delta} characters
longer. If any files would exceed this length,
- * a {@link RepositorySourceException} is thrown.
- *
- * @param root the root of the tree to check; may be a file or directory but may not
be null
- * @param delta the change in the length of the path to check. Used to preemptively
check whether moving a file or directory
- * to a new path would violate path length rules
- * @throws RepositorySourceException if any files in the tree rooted at {@code root}
would exceed this {@link #maxPathLength
- * the maximum path length for this processor}
- */
- protected void ensureValidPathLength( File root,
- int delta ) {
- try {
- int len = root.getCanonicalPath().length();
- if (len > maxPathLength - delta) {
- String msg =
FileSystemI18n.maxPathLengthExceeded.text(this.maxPathLength,
-
this.getSourceName(),
-
root.getCanonicalPath(),
- delta);
- throw new RepositorySourceException(this.getSourceName(), msg);
- }
-
- if (root.isDirectory()) {
- for (File child : root.listFiles(filenameFilter)) {
- ensureValidPathLength(child, delta);
- }
-
- }
- } catch (IOException ioe) {
- throw new RepositorySourceException(this.getSourceName(),
FileSystemI18n.getCanonicalPathFailed.text(), ioe);
- }
- }
-
- protected Location locationFor( File workspaceRoot,
- File path ) throws IOException {
- assert workspaceRoot != null;
- assert path != null;
- assert path.getCanonicalPath().startsWith(workspaceRoot.getCanonicalPath());
-
- String relativePath =
path.getCanonicalPath().substring(workspaceRoot.getCanonicalPath().length());
- PathFactory pathFactory = pathFactory();
- List<Segment> segments = new LinkedList<Segment>();
-
- String sepString = File.separator.equals("\\") ? "\\\\" :
File.separator;
- assert relativePath.charAt(0) == File.separatorChar;
- for (String segment : relativePath.substring(1).split(sepString)) {
- segments.add(pathFactory.createSegment(segment, 1));
- }
-
- return Location.create(pathFactory().createAbsolutePath(segments));
- }
-
- protected Path getPathFor( Location location,
- Request request ) {
-
- if (location.hasPath()) return location.getPath();
-
- UUID uuid = uuidFor(location);
- if (rootNodeUuid.equals(uuid)) {
- return pathFactory().createRootPath();
- }
-
- I18n msg = FileSystemI18n.locationInRequestMustHavePath;
- request.setError(new RepositorySourceException(getSourceName(),
msg.text(getSourceName(), request)));
- return null;
- }
-
- protected File getWorkspaceDirectory( String workspaceName ) {
- if (workspaceName == null) workspaceName = defaultWorkspaceName;
-
- File directory = this.workspaceRootPath == null ? new File(workspaceName) : new
File(workspaceRootPath, workspaceName);
- if (directory.exists() && directory.isDirectory() &&
directory.canRead()) return directory;
- return null;
- }
-
- /**
- * This utility files the existing {@link File} at the supplied path, and in the
process will verify that the path is actually
- * valid.
- * <p>
- * Note that this connector represents a file as two nodes: a parent node with a name
that matches the file and a "
- * <code>jcr:primaryType</code>" of
"<code>nt:file</code>"; and a child node with the name
"<code>jcr:content</code>" and a "
- * <code>jcr:primaryType</code>" of
"<code>nt:resource</code>". The parent
"<code>nt:file</code>" node and its properties
- * represents the file itself, whereas the child
"<code>nt:resource</code>" node and its properties represent the
content of
- * the file.
- * </p>
- * <p>
- * As such, this method will return the File object for paths representing both the
parent "<code>nt:file</code>" and child "
- * <code>nt:resource</code>" node.
- * </p>
- *
- * @param workspaceRoot
- * @param path
- * @param location the location containing the path; may not be null
- * @param request the request containing the path (and the location); may not be
null
- * @return the existing {@link File file} for the path; or null if the path does not
represent an existing file and a
- * {@link PathNotFoundException} was set as the {@link
Request#setError(Throwable) error} on the request
- */
- protected File getExistingFileFor( File workspaceRoot,
- Path path,
- Location location,
- Request request ) {
- assert path != null;
- assert location != null;
- assert request != null;
- if (path.isRoot()) {
- return workspaceRoot;
- }
- // See if the path is a "jcr:content" node ...
- if (path.getLastSegment().getName().equals(JcrLexicon.CONTENT)) {
- // We only want to use the parent path to find the actual file ...
- path = path.getParent();
- }
- File file = workspaceRoot;
- for (Path.Segment segment : path) {
- String localName = segment.getName().getLocalName();
- // Verify the segment is valid ...
- if (segment.getIndex() > 1) {
- I18n msg = FileSystemI18n.sameNameSiblingsAreNotAllowed;
- throw new RepositorySourceException(getSourceName(),
msg.text(getSourceName(), request));
- }
- if (!segment.getName().getNamespaceUri().equals(defaultNamespaceUri)) {
- I18n msg = FileSystemI18n.onlyTheDefaultNamespaceIsAllowed;
- throw new RepositorySourceException(getSourceName(),
msg.text(getSourceName(), request));
- }
- // The segment should exist as a child of the file ...
- file = new File(file, localName);
- if (!file.exists() || !file.canRead()) {
- // Unable to complete the path, so prepare the exception by determining
the lowest path that exists ...
- Path lowest = path;
- while (lowest.getLastSegment() != segment) {
- lowest = lowest.getParent();
- }
- lowest = lowest.getParent();
- request.setError(new PathNotFoundException(location, lowest));
- return null;
- }
- }
- assert file != null;
- return file;
- }
-}
Modified:
trunk/extensions/dna-connector-filesystem/src/main/java/org/jboss/dna/connector/filesystem/FileSystemSource.java
===================================================================
---
trunk/extensions/dna-connector-filesystem/src/main/java/org/jboss/dna/connector/filesystem/FileSystemSource.java 2009-12-27
20:45:58 UTC (rev 1483)
+++
trunk/extensions/dna-connector-filesystem/src/main/java/org/jboss/dna/connector/filesystem/FileSystemSource.java 2009-12-29
04:25:02 UTC (rev 1484)
@@ -43,7 +43,6 @@
import java.util.Map;
import java.util.Set;
import java.util.UUID;
-import java.util.concurrent.ConcurrentHashMap;
import java.util.regex.Pattern;
import javax.naming.BinaryRefAddr;
import javax.naming.Context;
@@ -55,7 +54,6 @@
import net.jcip.annotations.ThreadSafe;
import org.jboss.dna.common.i18n.I18n;
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.DnaIntLexicon;
import org.jboss.dna.graph.ExecutionContext;
@@ -67,6 +65,8 @@
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.connector.path.PathRepositoryConnection;
+import org.jboss.dna.graph.connector.path.PathRepositorySource;
import org.jboss.dna.graph.property.Name;
import org.jboss.dna.graph.property.NamespaceRegistry;
import org.jboss.dna.graph.property.Property;
@@ -77,7 +77,7 @@
* workspace. New workspaces can be created, as long as the names represent valid paths
to existing directories.
*/
@ThreadSafe
-public class FileSystemSource implements RepositorySource, ObjectFactory {
+public class FileSystemSource implements PathRepositorySource, ObjectFactory {
/**
* An immutable {@link CustomPropertiesFactory} implementation that is used by
default when none is provided. Note that this
@@ -148,8 +148,9 @@
SUPPORTS_EVENTS,
DEFAULT_SUPPORTS_CREATING_WORKSPACES,
SUPPORTS_REFERENCES);
- private transient CachePolicy cachePolicy;
- private transient Map<String, File> availableWorkspaces;
+ private transient RepositoryContext repositoryContext;
+ private transient CachePolicy defaultCachePolicy;
+ private transient FileSystemRepository repository;
private volatile CustomPropertiesFactory customPropertiesFactory;
/**
@@ -237,6 +238,22 @@
this.exclusionPattern = exclusionPattern;
}
+ FilenameFilter filenameFilter() {
+ FilenameFilter filenameFilter = null;
+ final String filterPattern = exclusionPattern;
+ if (filterPattern != null) {
+ filenameFilter = new FilenameFilter() {
+ Pattern filter = Pattern.compile(filterPattern);
+
+ public boolean accept( File dir,
+ String name ) {
+ return !filter.matcher(name).matches();
+ }
+ };
+ }
+ return filenameFilter;
+ }
+
/**
* Get the UUID that is used for the root node of each workspace
*
@@ -413,7 +430,7 @@
public synchronized void setCacheTimeToLiveInMilliseconds( int cacheTimeToLive ) {
if (cacheTimeToLive < 0) cacheTimeToLive =
DEFAULT_CACHE_TIME_TO_LIVE_IN_SECONDS;
this.cacheTimeToLiveInMilliseconds = cacheTimeToLive;
- this.cachePolicy = cacheTimeToLiveInMilliseconds > 0 ? new
FileSystemCachePolicy(cacheTimeToLiveInMilliseconds) : null;
+ this.defaultCachePolicy = cacheTimeToLiveInMilliseconds > 0 ? new
FileSystemCachePolicy(cacheTimeToLiveInMilliseconds) : null;
}
/**
@@ -425,6 +442,10 @@
return customPropertiesFactory;
}
+ CustomPropertiesFactory customPropertiesFactory() {
+ return customPropertiesFactory != null ? customPropertiesFactory :
DEFAULT_PROPERTIES_FACTORY;
+ }
+
/**
* Set the factory that is used to create custom properties on "nt:folder",
"nt:file", and "nt:resource" nodes.
*
@@ -440,9 +461,19 @@
* @see
org.jboss.dna.graph.connector.RepositorySource#initialize(org.jboss.dna.graph.connector.RepositoryContext)
*/
public synchronized void initialize( RepositoryContext context ) throws
RepositorySourceException {
- // No need to do anything
+ this.repositoryContext = context;
}
+ @Override
+ public RepositoryContext getRepositoryContext() {
+ return this.repositoryContext;
+ }
+
+ @Override
+ public CachePolicy getDefaultCachePolicy() {
+ return defaultCachePolicy;
+ }
+
/**
* {@inheritDoc}
*
@@ -542,18 +573,6 @@
return null;
}
- private String pathFor( String workspaceName ) {
- String path = workspaceName;
- if (this.workspaceRootPath != null) {
- if (this.workspaceRootPath.charAt(workspaceRootPath.length() - 1) ==
File.separatorChar) {
- path = this.workspaceRootPath + workspaceName;
- }
- path = this.workspaceRootPath + File.separatorChar + workspaceName;
- }
-
- return path;
- }
-
/**
* {@inheritDoc}
*
@@ -566,70 +585,17 @@
throw new RepositorySourceException(getName(), msg.text("name"));
}
- if (this.availableWorkspaces == null) {
- // Set up the predefined workspace names ...
- this.availableWorkspaces = new ConcurrentHashMap<String, File>();
- for (String predefined : this.predefinedWorkspaces) {
- // Look for the file at this path ...
- File file = new File(pathFor(predefined));
- if (!file.exists()) {
-
Logger.getLogger(getClass()).warn(FileSystemI18n.pathForPredefinedWorkspaceDoesNotExist,
predefined, name);
- } else if (!file.isDirectory()) {
-
Logger.getLogger(getClass()).warn(FileSystemI18n.pathForPredefinedWorkspaceIsNotDirectory,
predefined, name);
- } else if (!file.canRead()) {
-
Logger.getLogger(getClass()).warn(FileSystemI18n.pathForPredefinedWorkspaceCannotBeRead,
predefined, name);
- }
-
- this.availableWorkspaces.put(predefined, file);
- }
- }
-
- if (defaultWorkspaceName != null) {
- // Look for the file at this path ...
- File file = new File(pathFor(defaultWorkspaceName));
- if (!file.exists()) {
-
Logger.getLogger(getClass()).warn(FileSystemI18n.pathForPredefinedWorkspaceDoesNotExist,
- defaultWorkspaceName,
- name);
- } else if (!file.isDirectory()) {
-
Logger.getLogger(getClass()).warn(FileSystemI18n.pathForPredefinedWorkspaceIsNotDirectory,
- defaultWorkspaceName,
- name);
- } else if (!file.canRead()) {
-
Logger.getLogger(getClass()).warn(FileSystemI18n.pathForPredefinedWorkspaceCannotBeRead,
- defaultWorkspaceName,
- name);
- }
-
- this.availableWorkspaces.put(defaultWorkspaceName, file);
- }
-
- FilenameFilter filenameFilter = null;
- if (exclusionPattern != null) {
- final String filterPattern = exclusionPattern;
- filenameFilter = new FilenameFilter() {
- Pattern filter = Pattern.compile(filterPattern);
-
- public boolean accept( File dir,
- String name ) {
- return !filter.matcher(name).matches();
- }
- };
- }
-
- CustomPropertiesFactory propFactory = customPropertiesFactory != null ?
customPropertiesFactory : DEFAULT_PROPERTIES_FACTORY;
- return new FileSystemConnection(name, defaultWorkspaceName, availableWorkspaces,
isCreatingWorkspacesAllowed(),
- cachePolicy, rootNodeUuid, workspaceRootPath,
maxPathLength, filenameFilter,
- getUpdatesAllowed(), propFactory);
+ if (repository == null) repository = new FileSystemRepository(this);
+ return new PathRepositoryConnection(this, repository);
}
+
/**
* {@inheritDoc}
*
* @see org.jboss.dna.graph.connector.RepositorySource#close()
*/
public synchronized void close() {
- this.availableWorkspaces = null;
}
protected static class StandardPropertiesFactory implements CustomPropertiesFactory
{
Modified:
trunk/extensions/dna-connector-filesystem/src/main/java/org/jboss/dna/connector/filesystem/FileSystemTransaction.java
===================================================================
---
trunk/extensions/dna-connector-filesystem/src/main/java/org/jboss/dna/connector/filesystem/FileSystemTransaction.java 2009-12-27
20:45:58 UTC (rev 1483)
+++
trunk/extensions/dna-connector-filesystem/src/main/java/org/jboss/dna/connector/filesystem/FileSystemTransaction.java 2009-12-29
04:25:02 UTC (rev 1484)
@@ -23,17 +23,19 @@
*/
package org.jboss.dna.connector.filesystem;
+import org.jboss.dna.graph.connector.path.PathRepositoryTransaction;
+
/**
- * A transaction returned by the {@link FileSystemConnection#startTransaction(boolean)}.
+ * A transaction returned by the {@link FileSystemRepository#startTransaction(boolean)}.
*/
-public class FileSystemTransaction {
+public class FileSystemTransaction implements PathRepositoryTransaction {
/**
* Commit any changes that have been made to the repository. This method may throw
runtime exceptions if there are failures
* committing the changes, but the transaction is still expected to be closed.
*
* @see #rollback()
- * @see FileSystemConnection#startTransaction(boolean)
+ * @see FileSystemRepository#startTransaction(boolean)
*/
public void commit() {
}
@@ -43,7 +45,7 @@
* rolling back the changes, but the transaction is still expected to be closed.
*
* @see #commit()
- * @see FileSystemConnection#startTransaction(boolean)
+ * @see FileSystemRepository#startTransaction(boolean)
*/
public void rollback() {
}
Modified:
trunk/extensions/dna-connector-filesystem/src/main/java/org/jboss/dna/connector/filesystem/package-info.java
===================================================================
---
trunk/extensions/dna-connector-filesystem/src/main/java/org/jboss/dna/connector/filesystem/package-info.java 2009-12-27
20:45:58 UTC (rev 1483)
+++
trunk/extensions/dna-connector-filesystem/src/main/java/org/jboss/dna/connector/filesystem/package-info.java 2009-12-29
04:25:02 UTC (rev 1484)
@@ -23,6 +23,7 @@
*/
/**
* The classes that make up the connector that accesses the files and directories on a
local file system and exposes them as content in a repository.
+ * This connector is based on the {@link WritablePathRepository path repository
framework}.
*/
package org.jboss.dna.connector.filesystem;
Modified:
trunk/extensions/dna-connector-filesystem/src/main/resources/org/jboss/dna/connector/filesystem/FileSystemI18n.properties
===================================================================
---
trunk/extensions/dna-connector-filesystem/src/main/resources/org/jboss/dna/connector/filesystem/FileSystemI18n.properties 2009-12-27
20:45:58 UTC (rev 1483)
+++
trunk/extensions/dna-connector-filesystem/src/main/resources/org/jboss/dna/connector/filesystem/FileSystemI18n.properties 2009-12-29
04:25:02 UTC (rev 1484)
@@ -34,9 +34,9 @@
pathForWorkspaceRootCannotBeRead = The path "{0}" for the predefined workspace
for the file system source "{1}" cannot be read
propertyIsRequired = The {0} property is required but has no value
locationInRequestMustHavePath = {0} requires a path in the request: {1}
-sameNameSiblingsAreNotAllowed = {0} does not allow same name siblings on nodes: {1}
+sameNameSiblingsAreNotAllowed = Repository source "{0}" does not allow same
name siblings on nodes: {1}
nodeOrderingNotSupported = {0} does not support node ordering
-onlyTheDefaultNamespaceIsAllowed = {0} requires node names use the default namespace:
{1}
+onlyTheDefaultNamespaceIsAllowed = Repository source "{0}" requires that node
names use the default namespace
sourceIsReadOnly = The source "{0}" does not allow updates. Set the
"updatesAllowed" property to "true" on the repository source
(connector) to enable updates.
pathIsReadOnly = The path "{0}" in workspace "{1}" in {2} cannot be
written to. See java.io.File\#canWrite().
errorSerializingCustomPropertiesFactory = Error serializing a {0} instance owned by the
{1} FileSystemSource
@@ -50,6 +50,7 @@
invalidNameForResource = Invalid node name "{3}" for node at path
"{0}" in workspace "{1}" in {2}. The name of nodes with primary type
nt:resource or dna:resource must be "jcr:content".
invalidPathForResource = Invalid parent type for node at path "{0}" in
workspace "{1}" in {2}. The parent node for nodes with primary type nt:resource
or dna:resource must be of type nt:file.
invalidPropertyNames = Attempt to set or update invalid property names: {0}
+couldNotReadData = Error reading data from path "{2}" in workspace
"{1}" in repository source "{0}"
couldNotWriteData = Error writing data to path "{0}" in workspace
"{1}" in {2}\: {3}
couldNotUpdateData = Error moving temporary data file to path "{0}" in
workspace "{1}" in {2}
missingRequiredProperty = Missing required property "{3}" at path
"{0}" in workspace "{1}" in {2}
Modified:
trunk/extensions/dna-connector-filesystem/src/test/java/org/jboss/dna/connector/filesystem/FileSystemSourceTest.java
===================================================================
---
trunk/extensions/dna-connector-filesystem/src/test/java/org/jboss/dna/connector/filesystem/FileSystemSourceTest.java 2009-12-27
20:45:58 UTC (rev 1483)
+++
trunk/extensions/dna-connector-filesystem/src/test/java/org/jboss/dna/connector/filesystem/FileSystemSourceTest.java 2009-12-29
04:25:02 UTC (rev 1484)
@@ -29,8 +29,13 @@
import static org.junit.Assert.assertThat;
import java.util.ArrayList;
import java.util.List;
+import org.jboss.dna.graph.ExecutionContext;
+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.RepositorySourceException;
+import org.jboss.dna.graph.observe.Observer;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
@@ -45,9 +50,41 @@
@Before
public void beforeEach() throws Exception {
+ final ExecutionContext context = new ExecutionContext();
this.source = new FileSystemSource();
// Set the mandatory properties ...
this.source.setName("Test Repository");
+ this.source.initialize(new RepositoryContext() {
+
+ @Override
+ public Subgraph getConfiguration( int depth ) {
+ return null;
+ }
+
+ @Override
+ public ExecutionContext getExecutionContext() {
+ return context;
+ }
+
+ @Override
+ public Observer getObserver() {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public RepositoryConnectionFactory getRepositoryConnectionFactory() {
+ return new RepositoryConnectionFactory() {
+
+ @Override
+ public RepositoryConnection createConnection( String sourceName )
throws RepositorySourceException {
+ return source.getConnection();
+ }
+
+ };
+ }
+
+ });
}
@After
Modified:
trunk/extensions/dna-connector-jdbc-metadata/src/main/java/org/jboss/dna/connector/meta/jdbc/JdbcMetadataI18n.java
===================================================================
---
trunk/extensions/dna-connector-jdbc-metadata/src/main/java/org/jboss/dna/connector/meta/jdbc/JdbcMetadataI18n.java 2009-12-27
20:45:58 UTC (rev 1483)
+++
trunk/extensions/dna-connector-jdbc-metadata/src/main/java/org/jboss/dna/connector/meta/jdbc/JdbcMetadataI18n.java 2009-12-29
04:25:02 UTC (rev 1484)
@@ -36,6 +36,8 @@
public static I18n errorClosingConnection;
public static I18n errorObtainingConnection;
+ public static I18n sourceIsReadOnly;
+
public static I18n couldNotGetDatabaseMetadata;
public static I18n couldNotGetCatalogNames;
public static I18n couldNotGetSchemaNames;
Modified:
trunk/extensions/dna-connector-jdbc-metadata/src/main/java/org/jboss/dna/connector/meta/jdbc/JdbcMetadataRepository.java
===================================================================
---
trunk/extensions/dna-connector-jdbc-metadata/src/main/java/org/jboss/dna/connector/meta/jdbc/JdbcMetadataRepository.java 2009-12-27
20:45:58 UTC (rev 1483)
+++
trunk/extensions/dna-connector-jdbc-metadata/src/main/java/org/jboss/dna/connector/meta/jdbc/JdbcMetadataRepository.java 2009-12-29
04:25:02 UTC (rev 1484)
@@ -35,6 +35,7 @@
import java.util.List;
import java.util.Map;
import java.util.Set;
+import java.util.UUID;
import net.jcip.annotations.ThreadSafe;
import org.jboss.dna.common.util.Logger;
import org.jboss.dna.graph.DnaLexicon;
@@ -260,7 +261,7 @@
properties.put(JcrLexicon.PRIMARY_TYPE,
propFactory.create(JcrLexicon.PRIMARY_TYPE, JcrNtLexicon.UNSTRUCTURED));
properties.put(JcrLexicon.MIXIN_TYPES,
propFactory.create(JcrLexicon.MIXIN_TYPES, JdbcMetadataLexicon.CATALOG));
- return new DefaultPathNode(nodePath, properties, schemaNames);
+ return new DefaultPathNode(nodePath, null, properties, schemaNames);
} catch (JdbcMetadataException se) {
throw new
RepositorySourceException(JdbcMetadataI18n.couldNotGetSchemaNames.text(catalogName), se);
} finally {
@@ -307,7 +308,7 @@
Segment[] children = new Segment[]
{pathFactory.createSegment(TABLES_SEGMENT_NAME),
pathFactory.createSegment(PROCEDURES_SEGMENT_NAME)};
- return new DefaultPathNode(nodePath, properties,
Arrays.asList(children));
+ return new DefaultPathNode(nodePath, null, properties,
Arrays.asList(children));
} catch (JdbcMetadataException se) {
throw new
RepositorySourceException(JdbcMetadataI18n.couldNotGetSchemaNames.text(catalogName), se);
} finally {
@@ -359,7 +360,7 @@
children.add(pathFactory.createSegment(table.getName()));
}
- return new DefaultPathNode(nodePath, properties, children);
+ return new DefaultPathNode(nodePath, null, properties, children);
} catch (JdbcMetadataException se) {
throw new
RepositorySourceException(JdbcMetadataI18n.couldNotGetTableNames.text(catalogName,
schemaName), se);
} finally {
@@ -442,7 +443,7 @@
children.add(pathFactory.createSegment(column.getName()));
}
- return new DefaultPathNode(nodePath, properties, children);
+ return new DefaultPathNode(nodePath, null, properties, children);
} catch (JdbcMetadataException se) {
throw new
RepositorySourceException(JdbcMetadataI18n.couldNotGetTable.text(catalogName, schemaName,
tableName),
se);
@@ -495,7 +496,7 @@
children.add(pathFactory.createSegment(procedure.getName()));
}
- return new DefaultPathNode(nodePath, properties, children);
+ return new DefaultPathNode(nodePath, null, properties, children);
} catch (JdbcMetadataException se) {
throw new
RepositorySourceException(JdbcMetadataI18n.couldNotGetProcedureNames.text(catalogName,
schemaName), se);
} finally {
@@ -562,7 +563,7 @@
propName = JdbcMetadataLexicon.PROCEDURE_RETURN_TYPE;
properties.put(propName, propFactory.create(propName,
procedure.getType()));
- return new DefaultPathNode(nodePath, properties,
Collections.<Segment>emptyList());
+ return new DefaultPathNode(nodePath, null, properties,
Collections.<Segment>emptyList());
} catch (JdbcMetadataException se) {
throw new
RepositorySourceException(JdbcMetadataI18n.couldNotGetProcedure.text(catalogName,
schemaName,
@@ -656,7 +657,7 @@
propName = JdbcMetadataLexicon.SOURCE_JDBC_DATA_TYPE;
properties.put(propName, propFactory.create(propName,
column.getSourceJdbcDataType()));
}
- return new DefaultPathNode(nodePath, properties,
Collections.<Segment>emptyList());
+ return new DefaultPathNode(nodePath, null, properties,
Collections.<Segment>emptyList());
} catch (JdbcMetadataException se) {
throw new
RepositorySourceException(JdbcMetadataI18n.couldNotGetColumn.text(catalogName,
schemaName,
@@ -757,6 +758,10 @@
return context.getValueFactories().getPathFactory().createRootPath();
}
+ public UUID getUuid() {
+ return source.getRootUuid();
+ }
+
public Map<Name, Property> getProperties() {
return rootNodeProperties;
}
Modified:
trunk/extensions/dna-connector-jdbc-metadata/src/main/java/org/jboss/dna/connector/meta/jdbc/JdbcMetadataSource.java
===================================================================
---
trunk/extensions/dna-connector-jdbc-metadata/src/main/java/org/jboss/dna/connector/meta/jdbc/JdbcMetadataSource.java 2009-12-27
20:45:58 UTC (rev 1483)
+++
trunk/extensions/dna-connector-jdbc-metadata/src/main/java/org/jboss/dna/connector/meta/jdbc/JdbcMetadataSource.java 2009-12-29
04:25:02 UTC (rev 1484)
@@ -752,4 +752,23 @@
return metadataCollector;
}
+ public boolean areUpdatesAllowed() {
+ return false;
+ }
+
+ /**
+ * In-memory connectors aren't shared and cannot be loaded from external sources
if updates are not allowed. Therefore, in
+ * order to avoid setting up an in-memory connector that is permanently empty
(presumably, not a desired outcome), all
+ * in-memory connectors must allow updates.
+ *
+ * @param updatesAllowed must be true
+ * @throws RepositorySourceException if {@code updatesAllowed != true}.
+ */
+ public void setUpdatesAllowed( boolean updatesAllowed ) {
+ if (updatesAllowed == false) {
+ throw new
RepositorySourceException(JdbcMetadataI18n.sourceIsReadOnly.text(this.name));
+ }
+
+ }
+
}
Modified:
trunk/extensions/dna-connector-jdbc-metadata/src/main/resources/org/jboss/dna/connector/meta/jdbc/JdbcMetadataI18n.properties
===================================================================
---
trunk/extensions/dna-connector-jdbc-metadata/src/main/resources/org/jboss/dna/connector/meta/jdbc/JdbcMetadataI18n.properties 2009-12-27
20:45:58 UTC (rev 1483)
+++
trunk/extensions/dna-connector-jdbc-metadata/src/main/resources/org/jboss/dna/connector/meta/jdbc/JdbcMetadataI18n.properties 2009-12-29
04:25:02 UTC (rev 1484)
@@ -25,6 +25,8 @@
errorClosingConnection = Error closing SQL connection
errorObtainingConnection = Error obtaining SQL connection
+sourceIsReadOnly = The JDBC Metadata source "{0}" is read-only and cannot be
changed to allow updates
+
couldNotGetDatabaseMetadata = Could not obtain database-level metadata
couldNotGetCatalogNames = Could not obtain catalog names for database
couldNotGetSchemaNames = Could not obtain schema names for catalog "{0}"