Index: docs/reference/src/main/docbook/en-US/content/connectors/file_system.xml
===================================================================
--- docs/reference/src/main/docbook/en-US/content/connectors/file_system.xml (revision 2307)
+++ docs/reference/src/main/docbook/en-US/content/connectors/file_system.xml (working copy)
@@ -36,20 +36,34 @@
workspace or the name of subdirectory within a root directory (see the workspaceRootPath property below). Each
connector can define whether it allows new workspaces to be created. If the directory for a workspace does not exist, this connector
will attempt to create the directory (and any missing parent directories).
-
-
- The file nodes returned by this connector will have a primary type of nt:file and a child node named jcr:content.
- The jcr:content node will have a primary type of mode:resource. The mode:resource node type is equivalent
- to the built-in nt:resource node type in all ways except one: it does not extend mix:referenceable. This is because
- ModeShape cannot assign a persistent UUID to the files in the file system or guarantee that no other process will move or delete the files outside of ModeShape.
- The mix:referenceable node type cannot be implemented if either of these conditions cannot be met.
-
- Additional properties (including mixin types) can be added by setting the customPropertiesFactory property to point to an implementation of
- the &CustomPropertiesFactory; interface.
-
-
+ By default, this connector is not capable of storing extra properties other than those defined on the nt:file, nt:folder
+ and nt:resource node types. This is because such properties cannot be represented natively on the file system.
+ When the connector is asked to store such properties, the default behavior is to log warnings and then to ignore these extra properties.
+ Obviously this is probably not sufficient for production (unless only the standard properties are to be used). To explicitly turn on this
+ behavior, set the "extraPropertiesBehavior" to "log".
+
+
+ However, the connector can be configured differently. If the "extraPropertiesBehavior" is set to "ignore", then these extra properties will
+ simply be silently ignored and lost: none will be stored, none will be loaded, and no warnings will be logged. If the "extraPropertiesBehavior"
+ is set to "error", the connector will throw an exception if any extra properties are used.
+
+
+ Perhaps the best setting for general use, however, is to set the "extraPropertiesBehavior" to "store". In this mode, any extra properties
+ are written to files on the file system that are adjacent to the actual file or folder. For example, given a "nt:folder" node that represents
+ the "folder1" directory, all extra properties will be stored in a text file named "folder1.modeshape" in the same parent
+ directory as the "folder1" directory. Similarly, given a "nt:file" node that represents the "file1" file on the file system, all
+ extra properties will be stored in a text file named "file1.modeshape" located next to the "file1" file. Note that the "nt:resource" node for
+ our "nt:file" node also is stored in the same location, so we can't use the "file1.modeshape" file (it's already used for the "nt:file" node),
+ so the connector uses the "file1.content.modeshape" file instead.
+
+
+
+ The "store" behavior may result in the creation of many "*.modeshape" files, and because of this the "store" behavior is not the default.
+
+
+
The &FileSystemSource; class provides a number of JavaBean properties that control its behavior:
@@ -81,6 +95,17 @@
available on each node. This property can be set either from an object that implements the &CustomPropertiesFactory;
interface or from the name of a class with a public, no-argument constructor that implements the &CustomPropertiesFactory;
interface. In the latter case, a the named class will be instantiated and used as the custom properties factory implementation.
+ See also the "extraPropertiesBehavior" setting.
+
+
+
+ extraPropertiesBehavior
+
+ Optional setting that specifies how to handle the extra properties on "nt:file", "nt:folder", and "nt:resource" nodes that
+ cannot be represented on the native files themselves. Set this to "log" if warnings are to be sent to the log
+ (the default), or "error" if setting such properties should cause an error, or "store" if they should be stored in ancillary
+ files next to the files and folders, or "ignore" if they should be silently ignored. The "log" value will be used by default
+ or an invalid value is specified. This setting will be ignored if a "customPropertiesFactory" class name is specified.
Index: extensions/modeshape-connector-filesystem/src/main/java/org/modeshape/connector/filesystem/BasePropertiesFactory.java
new file mode 100644
===================================================================
--- /dev/null (revision 2307)
+++ extensions/modeshape-connector-filesystem/src/main/java/org/modeshape/connector/filesystem/BasePropertiesFactory.java (working copy)
@@ -0,0 +1,89 @@
+/*
+ * ModeShape (http://www.modeshape.org)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership. Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of
+ * individual contributors.
+ *
+ * ModeShape is free software. Unless otherwise indicated, all code in ModeShape
+ * is licensed to you under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * ModeShape is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this 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.modeshape.connector.filesystem;
+
+import java.io.FilenameFilter;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+import org.modeshape.graph.JcrLexicon;
+import org.modeshape.graph.ModeShapeIntLexicon;
+import org.modeshape.graph.property.Name;
+import org.modeshape.graph.property.Property;
+
+/**
+ * A base class for {@link CustomPropertiesFactory} implementations that handle "extra" or "custom" properties for 'nt:file',
+ * 'nt:folder', or 'nt:resource' nodes.
+ */
+public abstract class BasePropertiesFactory implements CustomPropertiesFactory {
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * Only certain properties are tolerated when writing content (dna:resource or jcr:resource) nodes. These properties are
+ * implicitly stored (primary type, data) or silently ignored (encoded, mimetype, last modified). The silently ignored
+ * properties must be accepted to stay compatible with the JCR specification.
+ */
+ protected final Set STANDARD_PROPERTIES_FOR_CONTENT = Collections.unmodifiableSet(new HashSet(
+ Arrays.asList(new Name[] {
+ JcrLexicon.PRIMARY_TYPE,
+ JcrLexicon.DATA,
+ JcrLexicon.ENCODING,
+ JcrLexicon.MIMETYPE,
+ JcrLexicon.LAST_MODIFIED,
+ JcrLexicon.LAST_MODIFIED_BY,
+ JcrLexicon.UUID,
+ ModeShapeIntLexicon.NODE_DEFINITON})));
+ /**
+ * Only certain properties are tolerated when writing files (nt:file) or folders (nt:folder) nodes. These properties are
+ * implicitly stored in the file or folder (primary type, created).
+ */
+ protected final Set STANDARD_PROPERTIES_FOR_FILE_OR_FOLDER = Collections.unmodifiableSet(new HashSet(
+ Arrays.asList(new Name[] {
+ JcrLexicon.PRIMARY_TYPE,
+ JcrLexicon.CREATED,
+ JcrLexicon.CREATED_BY,
+ JcrLexicon.UUID,
+ ModeShapeIntLexicon.NODE_DEFINITON})));
+
+ protected static final Collection NO_PROPERTIES_COLLECTION = Collections.emptyList();
+ protected static final Set NO_NAMES = Collections.emptySet();
+
+ /**
+ * Create an instance of this factory.
+ */
+ protected BasePropertiesFactory() {
+ }
+
+ /**
+ * Create a filename filter that will ignore any files needed by this implementation.
+ *
+ * @param exclusionFilter the default filter, which should be included; may be null if there is no such filter
+ * @return the filter
+ */
+ public FilenameFilter getFilenameFilter( FilenameFilter exclusionFilter ) {
+ return exclusionFilter;
+ }
+}
Index: extensions/modeshape-connector-filesystem/src/main/java/org/modeshape/connector/filesystem/FileSystemI18n.java
===================================================================
--- extensions/modeshape-connector-filesystem/src/main/java/org/modeshape/connector/filesystem/FileSystemI18n.java (revision 2307)
+++ extensions/modeshape-connector-filesystem/src/main/java/org/modeshape/connector/filesystem/FileSystemI18n.java (working copy)
@@ -73,6 +73,12 @@ public final class FileSystemI18n {
public static I18n determineMimeTypeUsingContentPropertyDescription;
public static I18n determineMimeTypeUsingContentPropertyLabel;
public static I18n determineMimeTypeUsingContentPropertyCategory;
+ public static I18n extraPropertiesPropertyDescription;
+ public static I18n extraPropertiesPropertyLabel;
+ public static I18n extraPropertiesPropertyCategory;
+ public static I18n customPropertiesFactoryPropertyDescription;
+ public static I18n customPropertiesFactoryPropertyLabel;
+ public static I18n customPropertiesFactoryPropertyCategory;
// Writable messages
public static I18n parentIsReadOnly;
@@ -89,6 +95,8 @@ public final class FileSystemI18n {
public static I18n deleteFailed;
public static I18n getCanonicalPathFailed;
public static I18n maxPathLengthExceeded;
+ public static I18n couldNotStoreProperty;
+ public static I18n couldNotStoreProperties;
static {
try {
Index: extensions/modeshape-connector-filesystem/src/main/java/org/modeshape/connector/filesystem/FileSystemSource.java
===================================================================
--- extensions/modeshape-connector-filesystem/src/main/java/org/modeshape/connector/filesystem/FileSystemSource.java (revision 2307)
+++ extensions/modeshape-connector-filesystem/src/main/java/org/modeshape/connector/filesystem/FileSystemSource.java (working copy)
@@ -26,15 +26,11 @@ package org.modeshape.connector.filesystem;
import java.io.File;
import java.io.FilenameFilter;
-import java.util.Arrays;
-import java.util.Collection;
import java.util.Collections;
-import java.util.HashSet;
+import java.util.HashMap;
import java.util.Hashtable;
-import java.util.LinkedList;
import java.util.List;
import java.util.Map;
-import java.util.Set;
import java.util.regex.Pattern;
import javax.naming.Context;
import javax.naming.Reference;
@@ -46,12 +42,10 @@ import org.modeshape.common.annotation.Description;
import org.modeshape.common.annotation.Label;
import org.modeshape.common.i18n.I18n;
import org.modeshape.common.util.CheckArg;
+import org.modeshape.common.util.Logger;
import org.modeshape.common.util.StringUtil;
import org.modeshape.connector.filesystem.FileSystemRepository.FileSystemTransaction;
import org.modeshape.graph.ExecutionContext;
-import org.modeshape.graph.JcrLexicon;
-import org.modeshape.graph.Location;
-import org.modeshape.graph.ModeShapeIntLexicon;
import org.modeshape.graph.connector.RepositoryConnection;
import org.modeshape.graph.connector.RepositorySource;
import org.modeshape.graph.connector.RepositorySourceCapabilities;
@@ -60,9 +54,6 @@ import org.modeshape.graph.connector.base.AbstractRepositorySource;
import org.modeshape.graph.connector.base.Connection;
import org.modeshape.graph.connector.base.PathNode;
import org.modeshape.graph.property.Binary;
-import org.modeshape.graph.property.Name;
-import org.modeshape.graph.property.NamespaceRegistry;
-import org.modeshape.graph.property.Property;
import org.modeshape.graph.request.CreateWorkspaceRequest.CreateConflictBehavior;
/**
@@ -74,12 +65,6 @@ import org.modeshape.graph.request.CreateWorkspaceRequest.CreateConflictBehavior
public class FileSystemSource extends AbstractRepositorySource implements ObjectFactory {
/**
- * An immutable {@link CustomPropertiesFactory} implementation that is used by default when none is provided. Note that this
- * implementation does restrict the properties that can be placed on file, folder and resource nodes.
- */
- protected static CustomPropertiesFactory DEFAULT_PROPERTIES_FACTORY = new StandardPropertiesFactory();
-
- /**
* The first serialized version of this source. Version {@value} .
*/
private static final long serialVersionUID = 1L;
@@ -100,6 +85,7 @@ public class FileSystemSource extends AbstractRepositorySource implements Object
protected static final String CUSTOM_PROPERTY_FACTORY = "customPropertyFactory";
protected static final String EAGER_FILE_LOADING = "eagerFileLoading";
protected static final String DETERMINE_MIME_TYPE_USING_CONTENT = "determineMimeTypeUsingContent";
+ protected static final String EXTRA_PROPERTIES = "extraProperties";
/**
* This source supports events.
@@ -119,6 +105,11 @@ public class FileSystemSource extends AbstractRepositorySource implements Object
public static final boolean DEFAULT_SUPPORTS_UPDATES = false;
/**
+ * The default behavior for dealing with extra properties on 'nt:file', 'nt:folder' and 'nt:resource' nodes is to log them.
+ */
+ public static final String DEFAULT_EXTRA_PROPERTIES = "log";
+
+ /**
* This source supports creating references.
*/
protected static final boolean SUPPORTS_REFERENCES = false;
@@ -137,6 +128,17 @@ public class FileSystemSource extends AbstractRepositorySource implements Object
public static final String DEFAULT_EXCLUSION_PATTERN = null;
public static final FilenameFilter DEFAULT_FILENAME_FILTER = null;
+ protected static Map EXTRA_PROPERTIES_CLASSNAME_BY_KEY;
+
+ static {
+ Map byKey = new HashMap();
+ byKey.put(DEFAULT_EXTRA_PROPERTIES, new LogProperties(Logger.getLogger(FileSystemSource.class)));
+ byKey.put("store", new StoreProperties());
+ byKey.put("error", new ThrowProperties());
+ byKey.put("ignore", new IgnoreProperties());
+ EXTRA_PROPERTIES_CLASSNAME_BY_KEY = Collections.unmodifiableMap(byKey);
+ }
+
@Description( i18n = FileSystemI18n.class, value = "defaultWorkspaceNamePropertyDescription" )
@Label( i18n = FileSystemI18n.class, value = "defaultWorkspaceNamePropertyLabel" )
@Category( i18n = FileSystemI18n.class, value = "defaultWorkspaceNamePropertyCategory" )
@@ -172,6 +174,11 @@ public class FileSystemSource extends AbstractRepositorySource implements Object
@Category( i18n = FileSystemI18n.class, value = "determineMimeTypeUsingContentPropertyCategory" )
private volatile boolean determineMimeTypeUsingContent = DEFAULT_DETERMINE_MIME_TYPE_USING_CONTENT;
+ @Description( i18n = FileSystemI18n.class, value = "extraPropertiesPropertyDescription" )
+ @Label( i18n = FileSystemI18n.class, value = "extraPropertiesPropertyLabel" )
+ @Category( i18n = FileSystemI18n.class, value = "extraPropertiesPropertyCategory" )
+ private volatile String extraProperties = DEFAULT_EXTRA_PROPERTIES;
+
private volatile FilenameFilter filenameFilter = DEFAULT_FILENAME_FILTER;
private volatile RepositorySourceCapabilities capabilities = new RepositorySourceCapabilities(
SUPPORTS_SAME_NAME_SIBLINGS,
@@ -306,9 +313,10 @@ public class FileSystemSource extends AbstractRepositorySource implements Object
this.exclusionPattern = null;
}
- FilenameFilter filenameFilter() {
+ FilenameFilter filenameFilter( boolean hideFilesForCustomProperties ) {
if (this.filenameFilter != null) return this.filenameFilter;
+ // Otherwise, create one that take into account the exclusion pattern ...
FilenameFilter filenameFilter = null;
final String filterPattern = exclusionPattern;
if (filterPattern != null) {
@@ -321,6 +329,14 @@ public class FileSystemSource extends AbstractRepositorySource implements Object
}
};
}
+
+ if (hideFilesForCustomProperties) {
+ // And the properties factory ...
+ CustomPropertiesFactory customPropsFactory = customPropertiesFactory();
+ if (customPropsFactory instanceof BasePropertiesFactory) {
+ filenameFilter = ((BasePropertiesFactory)customPropsFactory).getFilenameFilter(filenameFilter);
+ }
+ }
return filenameFilter;
}
@@ -468,6 +484,32 @@ public class FileSystemSource extends AbstractRepositorySource implements Object
}
/**
+ * Get the desired behavior for handling extra properties on "nt:foldeR", "nt:file", and "nt:resource" nodes.
+ *
+ * @return one of "log", "ignore", "error", or "store"
+ * @see #getCustomPropertiesFactory()
+ */
+ public String getExtraPropertiesBehavior() {
+ return extraProperties;
+ }
+
+ /**
+ * Set the desired behavior for handling extra properties on "nt:foldeR", "nt:file", and "nt:resource" nodes.
+ *
+ * @param behavior "log", "ignore", "error", or "store"
+ * @see #setCustomPropertiesFactory(CustomPropertiesFactory)
+ * @see #setCustomPropertiesFactory(String)
+ */
+ public void setExtraPropertiesBehavior( String behavior ) {
+ if (behavior != null) behavior = behavior.trim().toLowerCase();
+ if (EXTRA_PROPERTIES_CLASSNAME_BY_KEY.containsKey(behavior)) {
+ this.extraProperties = behavior;
+ } else {
+ this.extraProperties = DEFAULT_EXTRA_PROPERTIES;
+ }
+ }
+
+ /**
* Get the factory that is used to create custom properties on "nt:folder", "nt:file", and "nt:resource" nodes.
*
* @return the factory, or null if no custom properties are to be created
@@ -477,13 +519,21 @@ public class FileSystemSource extends AbstractRepositorySource implements Object
}
CustomPropertiesFactory customPropertiesFactory() {
- return customPropertiesFactory != null ? customPropertiesFactory : DEFAULT_PROPERTIES_FACTORY;
+ if (customPropertiesFactory == null) {
+ customPropertiesFactory = EXTRA_PROPERTIES_CLASSNAME_BY_KEY.get(extraProperties);
+ if (customPropertiesFactory == null) {
+ customPropertiesFactory = EXTRA_PROPERTIES_CLASSNAME_BY_KEY.get(DEFAULT_EXTRA_PROPERTIES);
+ }
+ assert customPropertiesFactory != null;
+ }
+ return customPropertiesFactory;
}
/**
* Set the factory that is used to create custom properties on "nt:folder", "nt:file", and "nt:resource" nodes.
*
* @param customPropertiesFactory the factory reference, or null if no custom properties will be created
+ * @see #setExtraPropertiesBehavior(String)
*/
public synchronized void setCustomPropertiesFactory( CustomPropertiesFactory customPropertiesFactory ) {
this.customPropertiesFactory = customPropertiesFactory;
@@ -503,6 +553,7 @@ public class FileSystemSource extends AbstractRepositorySource implements Object
* reason.
* @throws ClassCastException if the class named by {@code customPropertiesFactoryClassName} does not implement the {@code
* CustomPropertiesFactory} interface
+ * @see #setExtraPropertiesBehavior(String)
*/
public synchronized void setCustomPropertiesFactory( String customPropertiesFactoryClassName )
throws ClassCastException, ClassNotFoundException, IllegalAccessException, InstantiationException {
@@ -550,6 +601,7 @@ public class FileSystemSource extends AbstractRepositorySource implements Object
ref.add(new StringRefAddr(DEFAULT_WORKSPACE, getDefaultWorkspaceName()));
ref.add(new StringRefAddr(ALLOW_CREATING_WORKSPACES, Boolean.toString(isCreatingWorkspacesAllowed())));
ref.add(new StringRefAddr(MAX_PATH_LENGTH, String.valueOf(maxPathLength)));
+ ref.add(new StringRefAddr(EXTRA_PROPERTIES, String.valueOf(extraProperties)));
String[] workspaceNames = getPredefinedWorkspaceNames();
if (workspaceNames != null && workspaceNames.length != 0) {
ref.add(new StringRefAddr(PREDEFINED_WORKSPACE_NAMES, StringUtil.combineLines(workspaceNames)));
@@ -586,6 +638,7 @@ public class FileSystemSource extends AbstractRepositorySource implements Object
String filenameFilterClassName = (String)values.get(FILENAME_FILTER);
String maxPathLength = (String)values.get(DEFAULT_MAX_PATH_LENGTH);
String customPropertiesFactoryClassName = (String)values.get(CUSTOM_PROPERTY_FACTORY);
+ String extraPropertiesBehavior = (String)values.get(EXTRA_PROPERTIES);
String eagerFileLoading = (String)values.get(EAGER_FILE_LOADING);
String useContentForMimeType = (String)values.get(DETERMINE_MIME_TYPE_USING_CONTENT);
@@ -605,6 +658,7 @@ public class FileSystemSource extends AbstractRepositorySource implements Object
if (exclusionPattern != null) source.setExclusionPattern(exclusionPattern);
if (filenameFilterClassName != null) source.setFilenameFilter(filenameFilterClassName);
if (maxPathLength != null) source.setMaxPathLength(Integer.valueOf(maxPathLength));
+ if (extraPropertiesBehavior != null) source.setExtraPropertiesBehavior(extraPropertiesBehavior);
if (customPropertiesFactoryClassName != null) source.setCustomPropertiesFactory(customPropertiesFactoryClassName);
if (eagerFileLoading != null) source.setEagerFileLoading(Boolean.parseBoolean(eagerFileLoading));
if (useContentForMimeType != null) source.setContentUsedToDetermineMimeType(Boolean.parseBoolean(useContentForMimeType));
@@ -642,130 +696,4 @@ public class FileSystemSource extends AbstractRepositorySource implements Object
}
return new Connection(this, repository);
}
-
- protected static class StandardPropertiesFactory implements CustomPropertiesFactory {
- private static final long serialVersionUID = 1L;
- private final Collection empty = Collections.emptyList();
-
- /**
- * Only certain properties are tolerated when writing content (dna:resource or jcr:resource) nodes. These properties are
- * implicitly stored (primary type, data) or silently ignored (encoded, mimetype, last modified). The silently ignored
- * properties must be accepted to stay compatible with the JCR specification.
- */
- private final Set ALLOWABLE_PROPERTIES_FOR_CONTENT = Collections.unmodifiableSet(new HashSet(
- Arrays.asList(new Name[] {
- JcrLexicon.PRIMARY_TYPE,
- JcrLexicon.DATA,
- JcrLexicon.ENCODED,
- JcrLexicon.MIMETYPE,
- JcrLexicon.LAST_MODIFIED,
- JcrLexicon.LAST_MODIFIED_BY,
- JcrLexicon.UUID,
- ModeShapeIntLexicon.NODE_DEFINITON})));
- /**
- * Only certain properties are tolerated when writing files (nt:file) or folders (nt:folder) nodes. These properties are
- * implicitly stored in the file or folder (primary type, created).
- */
- private final Set ALLOWABLE_PROPERTIES_FOR_FILE_OR_FOLDER = Collections.unmodifiableSet(new HashSet(
- Arrays.asList(new Name[] {
- JcrLexicon.PRIMARY_TYPE,
- JcrLexicon.CREATED,
- JcrLexicon.CREATED_BY,
- JcrLexicon.UUID,
- ModeShapeIntLexicon.NODE_DEFINITON})));
-
- public Collection getDirectoryProperties( ExecutionContext context,
- Location location,
- File directory ) {
- return empty;
- }
-
- public Collection getFileProperties( ExecutionContext context,
- Location location,
- File file ) {
- return empty;
- }
-
- public Collection getResourceProperties( ExecutionContext context,
- Location location,
- File file,
- String mimeType ) {
- return empty;
- }
-
- /**
- * {@inheritDoc}
- *
- * @see org.modeshape.connector.filesystem.CustomPropertiesFactory#recordDirectoryProperties(org.modeshape.graph.ExecutionContext,
- * java.lang.String, org.modeshape.graph.Location, java.io.File, java.util.Map)
- */
- public Set recordDirectoryProperties( ExecutionContext context,
- String sourceName,
- Location location,
- File file,
- Map properties ) throws RepositorySourceException {
- ensureValidProperties(context, sourceName, properties.values(), ALLOWABLE_PROPERTIES_FOR_FILE_OR_FOLDER);
- return null;
- }
-
- /**
- * {@inheritDoc}
- *
- * @see org.modeshape.connector.filesystem.CustomPropertiesFactory#recordFileProperties(org.modeshape.graph.ExecutionContext,
- * java.lang.String, org.modeshape.graph.Location, java.io.File, java.util.Map)
- */
- public Set recordFileProperties( ExecutionContext context,
- String sourceName,
- Location location,
- File file,
- Map properties ) throws RepositorySourceException {
- ensureValidProperties(context, sourceName, properties.values(), ALLOWABLE_PROPERTIES_FOR_FILE_OR_FOLDER);
- return null;
- }
-
- /**
- * {@inheritDoc}
- *
- * @see org.modeshape.connector.filesystem.CustomPropertiesFactory#recordResourceProperties(org.modeshape.graph.ExecutionContext,
- * java.lang.String, org.modeshape.graph.Location, java.io.File, java.util.Map)
- */
- public Set recordResourceProperties( ExecutionContext context,
- String sourceName,
- Location location,
- File file,
- Map properties ) throws RepositorySourceException {
- ensureValidProperties(context, sourceName, properties.values(), ALLOWABLE_PROPERTIES_FOR_CONTENT);
- return null;
- }
-
- /**
- * Checks that the collection of {@code properties} only contains properties with allowable names.
- *
- * @param context
- * @param sourceName
- * @param properties
- * @param validPropertyNames
- * @throws RepositorySourceException if {@code properties} contains a
- * @see #ALLOWABLE_PROPERTIES_FOR_CONTENT
- * @see #ALLOWABLE_PROPERTIES_FOR_FILE_OR_FOLDER
- */
- protected void ensureValidProperties( ExecutionContext context,
- String sourceName,
- Collection properties,
- Set validPropertyNames ) {
- List invalidNames = new LinkedList();
- NamespaceRegistry registry = context.getNamespaceRegistry();
-
- for (Property property : properties) {
- if (!validPropertyNames.contains(property.getName())) {
- invalidNames.add(property.getName().getString(registry));
- }
- }
-
- if (!invalidNames.isEmpty()) {
- throw new RepositorySourceException(sourceName, FileSystemI18n.invalidPropertyNames.text(invalidNames.toString()));
- }
- }
-
- }
}
Index: extensions/modeshape-connector-filesystem/src/main/java/org/modeshape/connector/filesystem/FileSystemWorkspace.java
===================================================================
--- extensions/modeshape-connector-filesystem/src/main/java/org/modeshape/connector/filesystem/FileSystemWorkspace.java (revision 2307)
+++ extensions/modeshape-connector-filesystem/src/main/java/org/modeshape/connector/filesystem/FileSystemWorkspace.java (working copy)
@@ -49,6 +49,7 @@ import org.modeshape.graph.request.Request;
* Workspace implementation for the file system connector.
*/
class FileSystemWorkspace extends PathWorkspace {
+ private static final Map NO_PROPERTIES = Collections.emptyMap();
private static final String DEFAULT_MIME_TYPE = "application/octet";
private static final Set VALID_PRIMARY_TYPES = new HashSet(Arrays.asList(new Name[] {JcrNtLexicon.FOLDER,
JcrNtLexicon.FILE, JcrNtLexicon.RESOURCE, ModeShapeLexicon.RESOURCE}));
@@ -60,6 +61,8 @@ class FileSystemWorkspace extends PathWorkspace {
private final boolean eagerLoading;
private final boolean contentUsedToDetermineMimeType;
private final Logger logger;
+ private final ValueFactory stringFactory;
+ private final NameFactory nameFactory;
public FileSystemWorkspace( String name,
FileSystemWorkspace originalToClone,
@@ -73,6 +76,8 @@ class FileSystemWorkspace extends PathWorkspace {
this.eagerLoading = this.source.isEagerFileLoading();
this.contentUsedToDetermineMimeType = this.source.isContentUsedToDetermineMimeType();
this.logger = Logger.getLogger(getClass());
+ this.stringFactory = context.getValueFactories().getStringFactory();
+ this.nameFactory = context.getValueFactories().getNameFactory();
cloneWorkspace(originalToClone);
}
@@ -87,6 +92,8 @@ class FileSystemWorkspace extends PathWorkspace {
this.eagerLoading = this.source.isEagerFileLoading();
this.contentUsedToDetermineMimeType = this.source.isContentUsedToDetermineMimeType();
this.logger = Logger.getLogger(getClass());
+ this.stringFactory = context.getValueFactories().getStringFactory();
+ this.nameFactory = context.getValueFactories().getNameFactory();
}
private void cloneWorkspace( FileSystemWorkspace original ) {
@@ -94,7 +101,7 @@ class FileSystemWorkspace extends PathWorkspace {
File newRoot = repository.getWorkspaceDirectory(this.getName());
try {
- FileUtil.copy(originalRoot, newRoot, source.filenameFilter());
+ FileUtil.copy(originalRoot, newRoot, source.filenameFilter(false));
} catch (IOException ioe) {
throw new IllegalStateException(ioe);
}
@@ -105,22 +112,78 @@ class FileSystemWorkspace extends PathWorkspace {
PathNode newNode ) {
PathFactory pathFactory = context.getValueFactories().getPathFactory();
Path newPath = pathFactory.create(newNode.getParent(), newNode.getName());
-
- File originalFile = fileFor(pathFactory.create(node.getParent(), node.getName()));
+ Path oldPath = pathFactory.create(node.getParent(), node.getName());
+ File originalFile = fileFor(oldPath);
File newFile = fileFor(newPath, false);
if (newFile.exists()) {
newFile.delete();
}
+ // Read the custom properties ...
+ CustomPropertiesFactory customPropertiesFactory = source.customPropertiesFactory();
+ Collection existingProps = null;
+ Collection existingResourceProps = null;
+ String sourceName = source.getName();
+ Location originalLocation = Location.create(oldPath);
+ if (originalFile.isDirectory()) {
+ existingProps = customPropertiesFactory.getDirectoryProperties(context, originalLocation, originalFile);
+ customPropertiesFactory.recordDirectoryProperties(context, sourceName, originalLocation, originalFile, NO_PROPERTIES);
+ } else {
+ Path resourcePath = pathFactory.create(oldPath, JcrLexicon.CONTENT);
+ Location originalResourceLocation = Location.create(resourcePath);
+ existingProps = customPropertiesFactory.getFileProperties(context, originalLocation, originalFile);
+ existingResourceProps = customPropertiesFactory.getResourceProperties(context,
+ originalResourceLocation,
+ originalFile,
+ null);
+ customPropertiesFactory.recordFileProperties(context, sourceName, originalLocation, originalFile, NO_PROPERTIES);
+ customPropertiesFactory.recordResourceProperties(context,
+ sourceName,
+ originalResourceLocation,
+ originalFile,
+ NO_PROPERTIES);
+ }
+
originalFile.renameTo(newFile);
+ // Set the custom properties on the new location ...
+ Location newLocation = Location.create(newPath);
+ if (originalFile.isDirectory()) {
+ customPropertiesFactory.recordDirectoryProperties(context,
+ sourceName,
+ newLocation,
+ newFile,
+ extraFolder(mapOf(existingProps)));
+ } else {
+ Path resourcePath = pathFactory.create(newPath, JcrLexicon.CONTENT);
+ Location resourceLocation = Location.create(resourcePath);
+ customPropertiesFactory.recordFileProperties(context,
+ sourceName,
+ newLocation,
+ newFile,
+ extraFile(mapOf(existingProps)));
+ customPropertiesFactory.recordResourceProperties(context,
+ sourceName,
+ resourceLocation,
+ newFile,
+ extraResource(mapOf(existingResourceProps)));
+ }
+
return getNode(newPath);
}
+ protected Map mapOf( Collection properties ) {
+ if (properties == null || properties.isEmpty()) return Collections.emptyMap();
+ Map result = new HashMap();
+ for (Property property : properties) {
+ result.put(property.getName(), property);
+ }
+ return result;
+ }
+
@Override
public PathNode putNode( PathNode node ) {
- NameFactory nameFactory = context.getValueFactories().getNameFactory();
PathFactory pathFactory = context.getValueFactories().getPathFactory();
NamespaceRegistry registry = context.getNamespaceRegistry();
CustomPropertiesFactory customPropertiesFactory = source.customPropertiesFactory();
@@ -135,7 +198,7 @@ class FileSystemWorkspace extends PathWorkspace {
source.getName(),
rootLocation,
workspaceRoot,
- node.getProperties());
+ extraFolder(node.getProperties()));
return getNode(rootPath);
}
@@ -183,7 +246,11 @@ class FileSystemWorkspace extends PathWorkspace {
ioe.getMessage()), ioe);
}
- customPropertiesFactory.recordFileProperties(context, source.getName(), Location.create(newPath), newFile, properties);
+ customPropertiesFactory.recordFileProperties(context,
+ source.getName(),
+ Location.create(newPath),
+ newFile,
+ extraFile(properties));
} else if (JcrNtLexicon.RESOURCE.equals(primaryType) || ModeShapeLexicon.RESOURCE.equals(primaryType)) {
assert parentFile != null;
@@ -206,7 +273,7 @@ class FileSystemWorkspace extends PathWorkspace {
// Copy over data into a temp file, then move it to the correct location
FileOutputStream fos = null;
try {
- File temp = File.createTempFile("dna", null);
+ File temp = File.createTempFile("modeshape", null);
fos = new FileOutputStream(temp);
Property dataProp = properties.get(JcrLexicon.DATA);
@@ -248,8 +315,8 @@ class FileSystemWorkspace extends PathWorkspace {
customPropertiesFactory.recordResourceProperties(context,
source.getName(),
Location.create(parentPath),
- newFile,
- properties);
+ parentFile,
+ extraResource(properties));
} else if (JcrNtLexicon.FOLDER.equals(primaryType) || primaryType == null) {
ensureValidPathLength(newFile);
@@ -266,7 +333,7 @@ class FileSystemWorkspace extends PathWorkspace {
source.getName(),
Location.create(newPath),
newFile,
- properties);
+ extraFolder(properties));
} else {
// Set error and return
@@ -286,8 +353,17 @@ class FileSystemWorkspace extends PathWorkspace {
public PathNode removeNode( Path nodePath ) {
File nodeFile;
+ CustomPropertiesFactory customPropertiesFactory = source.customPropertiesFactory();
+
if (!nodePath.isRoot() && JcrLexicon.CONTENT.equals(nodePath.getLastSegment().getName())) {
nodeFile = fileFor(nodePath.getParent());
+
+ // Have the custom property factory remote all properties ...
+ customPropertiesFactory.recordResourceProperties(context,
+ source.getName(),
+ Location.create(nodePath),
+ nodeFile,
+ NO_PROPERTIES);
if (!nodeFile.exists()) return null;
FileOutputStream fos = null;
@@ -306,6 +382,12 @@ class FileSystemWorkspace extends PathWorkspace {
}
} else {
nodeFile = fileFor(nodePath);
+ // Have the custom property factory remote all properties ...
+ customPropertiesFactory.recordResourceProperties(context,
+ source.getName(),
+ Location.create(nodePath),
+ nodeFile,
+ NO_PROPERTIES);
if (!nodeFile.exists()) return null;
FileUtil.delete(nodeFile);
@@ -338,49 +420,53 @@ class FileSystemWorkspace extends PathWorkspace {
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 {
- // First try the file name (so we don't have to create an input stream,
- // which may have too much latency if a remote network file) ...
- mimeType = mimeTypeDetector.mimeTypeOf(file.getName(), null);
- if (mimeType == null && contentUsedToDetermineMimeType) {
- // Try to find the mime type using the content ...
- contents = new BufferedInputStream(new FileInputStream(file));
- mimeType = mimeTypeDetector.mimeTypeOf(null, 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(source.getName(), msg.text(source.getName(),
- getName(),
- path.getString(registry)));
- } finally {
- if (contents != null) {
- try {
- contents.close();
- } catch (IOException e) {
- }
- }
- }
// First add any custom properties ...
- Collection customProps = customPropertiesFactory.getResourceProperties(context,
- location,
- file,
- mimeType);
+ Collection customProps = customPropertiesFactory.getResourceProperties(context, location, file, null);
for (Property customProp : customProps) {
properties.put(customProp.getName(), customProp);
}
+ if (!properties.containsKey(JcrLexicon.MIMETYPE)) {
+ // Discover the mime type ...
+ String mimeType = null;
+ InputStream contents = null;
+ try {
+ // First try the file name (so we don't have to create an input stream,
+ // which may have too much latency if a remote network file) ...
+ mimeType = mimeTypeDetector.mimeTypeOf(file.getName(), null);
+ if (mimeType == null && contentUsedToDetermineMimeType) {
+ // Try to find the mime type using the content ...
+ contents = new BufferedInputStream(new FileInputStream(file));
+ mimeType = mimeTypeDetector.mimeTypeOf(null, 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(source.getName(), msg.text(source.getName(),
+ getName(),
+ path.getString(registry)));
+ } finally {
+ if (contents != null) {
+ try {
+ contents.close();
+ } catch (IOException e) {
+ }
+ }
+ }
+ }
+
// 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
- nodeType = JcrNtLexicon.RESOURCE;
- properties.put(JcrLexicon.PRIMARY_TYPE, factory.create(JcrLexicon.PRIMARY_TYPE, ModeShapeLexicon.RESOURCE));
+ Property primaryType = properties.get(JcrLexicon.PRIMARY_TYPE);
+ if (primaryType == null) {
+ nodeType = JcrNtLexicon.RESOURCE;
+ properties.put(JcrLexicon.PRIMARY_TYPE, factory.create(JcrLexicon.PRIMARY_TYPE, ModeShapeLexicon.RESOURCE));
+ } else {
+ nodeType = nameValueFor(primaryType);
+ }
properties.put(JcrLexicon.LAST_MODIFIED, factory.create(JcrLexicon.LAST_MODIFIED,
dateFactory.create(file.lastModified())));
@@ -399,7 +485,7 @@ class FileSystemWorkspace extends PathWorkspace {
if (file == null) return null;
if (file.isDirectory()) {
- String[] childNames = file.list(source.filenameFilter());
+ String[] childNames = file.list(source.filenameFilter(true));
Arrays.sort(childNames);
List childSegments = new ArrayList(childNames.length);
@@ -420,8 +506,13 @@ class FileSystemWorkspace extends PathWorkspace {
childSegments);
}
- nodeType = JcrNtLexicon.FOLDER;
- properties.put(JcrLexicon.PRIMARY_TYPE, factory.create(JcrLexicon.PRIMARY_TYPE, JcrNtLexicon.FOLDER));
+ Property primaryType = properties.get(JcrLexicon.PRIMARY_TYPE);
+ if (primaryType == null) {
+ nodeType = JcrNtLexicon.FOLDER;
+ properties.put(JcrLexicon.PRIMARY_TYPE, factory.create(JcrLexicon.PRIMARY_TYPE, JcrNtLexicon.FOLDER));
+ } else {
+ nodeType = nameValueFor(primaryType);
+ }
// return new DefaultPathNode(path, source.getRootNodeUuidObject(), properties, childSegments);
return new PathNode(null, path.getParent(), path.getLastSegment(), properties, childSegments);
@@ -432,9 +523,17 @@ class FileSystemWorkspace extends PathWorkspace {
properties.put(customProp.getName(), customProp);
}
- nodeType = JcrNtLexicon.FILE;
- properties.put(JcrLexicon.PRIMARY_TYPE, factory.create(JcrLexicon.PRIMARY_TYPE, JcrNtLexicon.FILE));
- properties.put(JcrLexicon.CREATED, factory.create(JcrLexicon.CREATED, dateFactory.create(file.lastModified())));
+ Property primaryType = properties.get(JcrLexicon.PRIMARY_TYPE);
+ if (primaryType == null) {
+ nodeType = JcrNtLexicon.FILE;
+ properties.put(JcrLexicon.PRIMARY_TYPE, factory.create(JcrLexicon.PRIMARY_TYPE, JcrNtLexicon.FILE));
+ } else {
+ nodeType = nameValueFor(primaryType);
+ }
+ if (!properties.containsKey(JcrLexicon.CREATED)) {
+ properties.put(JcrLexicon.CREATED, factory.create(JcrLexicon.CREATED, dateFactory.create(file.lastModified())));
+ }
+
// node = new DefaultPathNode(path, null, properties,
// Collections.singletonList(pathFactory.createSegment(JcrLexicon.CONTENT)));
@@ -444,7 +543,6 @@ class FileSystemWorkspace extends PathWorkspace {
if (nodeType != null && logger.isTraceEnabled()) {
long stopTime = System.nanoTime();
long ms = TimeUnit.MICROSECONDS.convert(stopTime - startTime, TimeUnit.NANOSECONDS);
- ValueFactory stringFactory = context.getValueFactories().getStringFactory();
String pathStr = stringFactory.create(path);
String typeStr = stringFactory.create(nodeType);
logger.trace("Loaded '{0}' node '{1}' in {2}microsec", typeStr, pathStr, ms);
@@ -563,7 +661,6 @@ class FileSystemWorkspace extends PathWorkspace {
// Don't validate the root node
if (node.getParent() == null) return;
- NameFactory nameFactory = context.getValueFactories().getNameFactory();
Map properties = node.getProperties();
Property primaryTypeProp = properties.get(JcrLexicon.PRIMARY_TYPE);
Name primaryType = primaryTypeProp == null ? JcrNtLexicon.FOLDER : nameFactory.create(primaryTypeProp.getFirstValue());
@@ -612,7 +709,7 @@ class FileSystemWorkspace extends PathWorkspace {
}
if (root.isDirectory()) {
- for (File child : root.listFiles(source.filenameFilter())) {
+ for (File child : root.listFiles(source.filenameFilter(false))) {
ensureValidPathLength(child, delta);
}
@@ -622,4 +719,68 @@ class FileSystemWorkspace extends PathWorkspace {
}
}
+ /**
+ * Determine the 'extra' properties for a folder that should be stored by the CustomPropertiesFactory.
+ *
+ * @param properties
+ * @return the extra properties, or null if the supplied properties reference is null
+ */
+ protected Map extraFolder( Map properties ) {
+ if (properties == null) return null;
+ if (properties.isEmpty()) return properties;
+ Map extra = new HashMap();
+ for (Property property : properties.values()) {
+ Name name = property.getName();
+ if (name.equals(JcrLexicon.PRIMARY_TYPE) && primaryTypeIs(property, JcrNtLexicon.FOLDER)) continue;
+ extra.put(name, property);
+ }
+ return extra;
+ }
+
+ /**
+ * Determine the 'extra' properties for a file node that should be stored by the CustomPropertiesFactory.
+ *
+ * @param properties
+ * @return the extra properties, or null if the supplied properties reference is null
+ */
+ protected Map extraFile( Map properties ) {
+ if (properties == null) return null;
+ if (properties.isEmpty()) return properties;
+ Map extra = new HashMap();
+ for (Property property : properties.values()) {
+ Name name = property.getName();
+ if (name.equals(JcrLexicon.PRIMARY_TYPE) && primaryTypeIs(property, JcrNtLexicon.FILE)) continue;
+ extra.put(name, property);
+ }
+ return extra;
+ }
+
+ /**
+ * Determine the 'extra' properties for a resource node that should be stored by the CustomPropertiesFactory.
+ *
+ * @param properties
+ * @return the extra properties, or null if the supplied properties reference is null
+ */
+ protected Map extraResource( Map properties ) {
+ if (properties == null) return null;
+ if (properties.isEmpty()) return properties;
+ Map extra = new HashMap();
+ for (Property property : properties.values()) {
+ Name name = property.getName();
+ if (name.equals(JcrLexicon.PRIMARY_TYPE) && primaryTypeIs(property, JcrNtLexicon.RESOURCE)) continue;
+ else if (name.equals(JcrLexicon.DATA)) continue;
+ extra.put(name, property);
+ }
+ return extra;
+ }
+
+ protected boolean primaryTypeIs( Property property,
+ Name primaryType ) {
+ Name actualPrimaryType = nameValueFor(property);
+ return actualPrimaryType.equals(primaryType);
+ }
+
+ protected Name nameValueFor( Property property ) {
+ return nameFactory.create(property.getFirstValue());
+ }
}
Index: extensions/modeshape-connector-filesystem/src/main/java/org/modeshape/connector/filesystem/IgnoreProperties.java
new file mode 100644
===================================================================
--- /dev/null (revision 2307)
+++ extensions/modeshape-connector-filesystem/src/main/java/org/modeshape/connector/filesystem/IgnoreProperties.java (working copy)
@@ -0,0 +1,134 @@
+/*
+ * ModeShape (http://www.modeshape.org)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership. Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of
+ * individual contributors.
+ *
+ * ModeShape is free software. Unless otherwise indicated, all code in ModeShape
+ * is licensed to you under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * ModeShape is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this 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.modeshape.connector.filesystem;
+
+import java.io.File;
+import java.util.Collection;
+import java.util.Map;
+import java.util.Set;
+import org.modeshape.graph.ExecutionContext;
+import org.modeshape.graph.Location;
+import org.modeshape.graph.connector.RepositorySourceException;
+import org.modeshape.graph.property.Name;
+import org.modeshape.graph.property.Property;
+
+/**
+ * A {@link CustomPropertiesFactory} implementation that ignores the "extra" or "custom" properties for 'nt:file', 'nt:folder',
+ * and 'nt:resource' nodes.
+ */
+public class IgnoreProperties extends BasePropertiesFactory {
+
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * Create an instance of this factory.
+ */
+ public IgnoreProperties() {
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.modeshape.connector.filesystem.CustomPropertiesFactory#getDirectoryProperties(org.modeshape.graph.ExecutionContext,
+ * org.modeshape.graph.Location, java.io.File)
+ */
+ @Override
+ public Collection getDirectoryProperties( ExecutionContext context,
+ Location location,
+ File directory ) {
+ return NO_PROPERTIES_COLLECTION;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.modeshape.connector.filesystem.CustomPropertiesFactory#getFileProperties(org.modeshape.graph.ExecutionContext,
+ * org.modeshape.graph.Location, java.io.File)
+ */
+ @Override
+ public Collection getFileProperties( ExecutionContext context,
+ Location location,
+ File file ) {
+ return NO_PROPERTIES_COLLECTION;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.modeshape.connector.filesystem.CustomPropertiesFactory#getResourceProperties(org.modeshape.graph.ExecutionContext,
+ * org.modeshape.graph.Location, java.io.File, java.lang.String)
+ */
+ @Override
+ public Collection getResourceProperties( ExecutionContext context,
+ Location location,
+ File file,
+ String mimeType ) {
+ return NO_PROPERTIES_COLLECTION;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.modeshape.connector.filesystem.CustomPropertiesFactory#recordDirectoryProperties(org.modeshape.graph.ExecutionContext,
+ * java.lang.String, org.modeshape.graph.Location, java.io.File, java.util.Map)
+ */
+ @Override
+ public Set recordDirectoryProperties( ExecutionContext context,
+ String sourceName,
+ Location location,
+ File file,
+ Map properties ) throws RepositorySourceException {
+ return NO_NAMES;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.modeshape.connector.filesystem.CustomPropertiesFactory#recordFileProperties(org.modeshape.graph.ExecutionContext,
+ * java.lang.String, org.modeshape.graph.Location, java.io.File, java.util.Map)
+ */
+ @Override
+ public Set recordFileProperties( ExecutionContext context,
+ String sourceName,
+ Location location,
+ File file,
+ Map properties ) throws RepositorySourceException {
+ return NO_NAMES;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.modeshape.connector.filesystem.CustomPropertiesFactory#recordResourceProperties(org.modeshape.graph.ExecutionContext,
+ * java.lang.String, org.modeshape.graph.Location, java.io.File, java.util.Map)
+ */
+ @Override
+ public Set recordResourceProperties( ExecutionContext context,
+ String sourceName,
+ Location location,
+ File file,
+ Map properties ) throws RepositorySourceException {
+ return NO_NAMES;
+ }
+}
Index: extensions/modeshape-connector-filesystem/src/main/java/org/modeshape/connector/filesystem/LogProperties.java
new file mode 100644
===================================================================
--- /dev/null (revision 2307)
+++ extensions/modeshape-connector-filesystem/src/main/java/org/modeshape/connector/filesystem/LogProperties.java (working copy)
@@ -0,0 +1,164 @@
+/*
+ * ModeShape (http://www.modeshape.org)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership. Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of
+ * individual contributors.
+ *
+ * ModeShape is free software. Unless otherwise indicated, all code in ModeShape
+ * is licensed to you under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * ModeShape is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this 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.modeshape.connector.filesystem;
+
+import java.io.File;
+import java.util.Collection;
+import java.util.Map;
+import java.util.Set;
+import org.modeshape.common.util.Logger;
+import org.modeshape.graph.ExecutionContext;
+import org.modeshape.graph.Location;
+import org.modeshape.graph.connector.RepositorySourceException;
+import org.modeshape.graph.property.Name;
+import org.modeshape.graph.property.Property;
+import org.modeshape.graph.property.ValueFactory;
+
+/**
+ * A {@link CustomPropertiesFactory} implementation that logs information about the "extra" or "custom" properties for 'nt:file',
+ * 'nt:folder', and 'nt:resource' nodes.
+ */
+public class LogProperties extends BasePropertiesFactory {
+ private static final long serialVersionUID = 1L;
+
+ private final Logger logger;
+
+ /**
+ * Create an instance of this factory.
+ *
+ * @param logger the logger that should be used, or null if the default logger should be used
+ */
+ public LogProperties( Logger logger ) {
+ this.logger = logger != null ? logger : Logger.getLogger(getClass());
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.modeshape.connector.filesystem.CustomPropertiesFactory#getDirectoryProperties(org.modeshape.graph.ExecutionContext,
+ * org.modeshape.graph.Location, java.io.File)
+ */
+ @Override
+ public Collection getDirectoryProperties( ExecutionContext context,
+ Location location,
+ File directory ) {
+ return NO_PROPERTIES_COLLECTION;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.modeshape.connector.filesystem.CustomPropertiesFactory#getFileProperties(org.modeshape.graph.ExecutionContext,
+ * org.modeshape.graph.Location, java.io.File)
+ */
+ @Override
+ public Collection getFileProperties( ExecutionContext context,
+ Location location,
+ File file ) {
+ return NO_PROPERTIES_COLLECTION;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.modeshape.connector.filesystem.CustomPropertiesFactory#getResourceProperties(org.modeshape.graph.ExecutionContext,
+ * org.modeshape.graph.Location, java.io.File, java.lang.String)
+ */
+ @Override
+ public Collection getResourceProperties( ExecutionContext context,
+ Location location,
+ File file,
+ String mimeType ) {
+ return NO_PROPERTIES_COLLECTION;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.modeshape.connector.filesystem.CustomPropertiesFactory#recordDirectoryProperties(org.modeshape.graph.ExecutionContext,
+ * java.lang.String, org.modeshape.graph.Location, java.io.File, java.util.Map)
+ */
+ @Override
+ public Set recordDirectoryProperties( ExecutionContext context,
+ String sourceName,
+ Location location,
+ File file,
+ Map properties ) throws RepositorySourceException {
+ if (!properties.isEmpty()) {
+ ValueFactory strings = context.getValueFactories().getStringFactory();
+ for (Property property : properties.values()) {
+ if (STANDARD_PROPERTIES_FOR_FILE_OR_FOLDER.contains(property.getName())) continue;
+ String name = strings.create(property.getName());
+ logger.warn(FileSystemI18n.couldNotStoreProperty, name, file.getPath(), sourceName);
+ }
+ }
+ return NO_NAMES;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.modeshape.connector.filesystem.CustomPropertiesFactory#recordFileProperties(org.modeshape.graph.ExecutionContext,
+ * java.lang.String, org.modeshape.graph.Location, java.io.File, java.util.Map)
+ */
+ @Override
+ public Set recordFileProperties( ExecutionContext context,
+ String sourceName,
+ Location location,
+ File file,
+ Map properties ) throws RepositorySourceException {
+ if (!properties.isEmpty()) {
+ ValueFactory strings = context.getValueFactories().getStringFactory();
+ for (Property property : properties.values()) {
+ if (STANDARD_PROPERTIES_FOR_FILE_OR_FOLDER.contains(property.getName())) continue;
+ String name = strings.create(property.getName());
+ logger.warn(FileSystemI18n.couldNotStoreProperty, name, file.getPath(), sourceName);
+ }
+ }
+ return NO_NAMES;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see org.modeshape.connector.filesystem.CustomPropertiesFactory#recordResourceProperties(org.modeshape.graph.ExecutionContext,
+ * java.lang.String, org.modeshape.graph.Location, java.io.File, java.util.Map)
+ */
+ @Override
+ public Set recordResourceProperties( ExecutionContext context,
+ String sourceName,
+ Location location,
+ File file,
+ Map properties ) throws RepositorySourceException {
+ if (!properties.isEmpty()) {
+ ValueFactory strings = context.getValueFactories().getStringFactory();
+ for (Property property : properties.values()) {
+ if (STANDARD_PROPERTIES_FOR_CONTENT.contains(property.getName())) continue;
+ String name = strings.create(property.getName());
+ logger.warn(FileSystemI18n.couldNotStoreProperty, name, file.getPath(), sourceName);
+ }
+ }
+ return NO_NAMES;
+ }
+}
Index: extensions/modeshape-connector-filesystem/src/main/java/org/modeshape/connector/filesystem/StoreProperties.java
new file mode 100644
===================================================================
--- /dev/null (revision 2307)
+++ extensions/modeshape-connector-filesystem/src/main/java/org/modeshape/connector/filesystem/StoreProperties.java (working copy)
@@ -0,0 +1,405 @@
+/*
+ * ModeShape (http://www.modeshape.org)
+ * See the COPYRIGHT.txt file distributed with this work for information
+ * regarding copyright ownership. Some portions may be licensed
+ * to Red Hat, Inc. under one or more contributor license agreements.
+ * See the AUTHORS.txt file in the distribution for a full listing of
+ * individual contributors.
+ *
+ * ModeShape is free software. Unless otherwise indicated, all code in ModeShape
+ * is licensed to you under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * ModeShape is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this 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.modeshape.connector.filesystem;
+
+import java.io.File;
+import java.io.FileWriter;
+import java.io.FilenameFilter;
+import java.io.IOException;
+import java.io.Writer;
+import java.util.ArrayList;
+import java.util.Collection;
+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 java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import org.modeshape.common.text.QuoteEncoder;
+import org.modeshape.common.text.TextDecoder;
+import org.modeshape.common.text.TextEncoder;
+import org.modeshape.common.text.XmlNameEncoder;
+import org.modeshape.common.util.IoUtil;
+import org.modeshape.common.util.StringUtil;
+import org.modeshape.graph.ExecutionContext;
+import org.modeshape.graph.JcrLexicon;
+import org.modeshape.graph.Location;
+import org.modeshape.graph.connector.RepositorySourceException;
+import org.modeshape.graph.property.Binary;
+import org.modeshape.graph.property.Name;
+import org.modeshape.graph.property.Property;
+import org.modeshape.graph.property.PropertyFactory;
+import org.modeshape.graph.property.PropertyType;
+import org.modeshape.graph.property.ValueFactories;
+import org.modeshape.graph.property.ValueFactory;
+
+/**
+ * A {@link CustomPropertiesFactory} implementation that stores "extra" or "custom" properties for 'nt:file', 'nt:folder', and
+ * 'nt:resource' nodes in a separate file that is named the same as the original but with a different extension.
+ */
+public class StoreProperties extends BasePropertiesFactory {
+
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * The regex pattern string used to parse properties. The capture groups are as follows:
+ *
+ *
property name (encoded)
+ *
property type string
+ *
a '[' if the value is multi-valued
+ *
the single value, or comma-separated values
+ *
+ *
+ * The expression is: ([\S]+)\s*[(](\w+)[)]\s*([\[]?)?([^\]]+)[\]]?
+ *
+ */
+ protected static final String PROPERTY_PATTERN_STRING = "([\\S]+)\\s*[(](\\w+)[)]\\s*([\\[]?)?([^\\]]+)[\\]]?";
+ protected static final Pattern PROPERTY_PATTERN = Pattern.compile(PROPERTY_PATTERN_STRING);
+
+ /**
+ * The regex pattern string used to parse quoted string property values. This is a repeating expression, and group(0) captures
+ * the characters within the quotes (including escaped double quotes).
+ *
+ * The expression is: \"((((?<=\\)\")|[^"])*)\"
+ *
+ */
+ protected static final String STRING_VALUE_PATTERN_STRING = "\\\"((((?<=\\\\)\\\")|[^\"])*)\\\"";
+ protected static final Pattern STRING_VALUE_PATTERN = Pattern.compile(STRING_VALUE_PATTERN_STRING);
+
+ /**
+ * The regex pattern string used to parse non-string property values (including hexadecimal-encoded binary values). This is a
+ * repeating expression, and group(1) captures the individual values.
+ *