Fix for ZEST-40, unifying the Identity-to-filename encoding.
Project: http://git-wip-us.apache.org/repos/asf/zest-qi4j/repo Commit: http://git-wip-us.apache.org/repos/asf/zest-qi4j/commit/7c48e8a7 Tree: http://git-wip-us.apache.org/repos/asf/zest-qi4j/tree/7c48e8a7 Diff: http://git-wip-us.apache.org/repos/asf/zest-qi4j/diff/7c48e8a7 Branch: refs/heads/develop Commit: 7c48e8a7cce8b1c6223038c225c4441cdf42c142 Parents: 8f89de5 Author: Niclas Hedhman <[email protected]> Authored: Wed Jul 8 09:38:17 2015 +0300 Committer: Niclas Hedhman <[email protected]> Committed: Wed Jul 8 09:38:17 2015 +0300 ---------------------------------------------------------------------- .../file/FileEntityStoreConfiguration.java | 16 ++++- .../entitystore/file/FileEntityStoreMixin.java | 65 ++++++++++++++++---- 2 files changed, 68 insertions(+), 13 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/zest-qi4j/blob/7c48e8a7/extensions/entitystore-file/src/main/java/org/qi4j/entitystore/file/FileEntityStoreConfiguration.java ---------------------------------------------------------------------- diff --git a/extensions/entitystore-file/src/main/java/org/qi4j/entitystore/file/FileEntityStoreConfiguration.java b/extensions/entitystore-file/src/main/java/org/qi4j/entitystore/file/FileEntityStoreConfiguration.java index 37eefde..4cf490c 100644 --- a/extensions/entitystore-file/src/main/java/org/qi4j/entitystore/file/FileEntityStoreConfiguration.java +++ b/extensions/entitystore-file/src/main/java/org/qi4j/entitystore/file/FileEntityStoreConfiguration.java @@ -34,8 +34,22 @@ public interface FileEntityStoreConfiguration /** * The directory where the File Entity Store will be keep its persisted state. * <p> - * Default: System.getProperty( "user.dir" ) + "/qi4j/filestore"; + * If no configuration is provided at all, then the default location is + * {@code System.getProperty( "user.dir" ) + "/qi4j/filestore"; }. + * If a configuration is given, the entity store will be placed in the + * DATA directory, which is operating system specific. * </p> + * <table> + * <tr><th>OS</th><th>Location</th></tr> + * <tr><td>Linux/Unix</td><td>{user}/.{application}/data</td></tr> + * <tr><td>OSX</td><td>{user}/Library/Application Support/{application}</td></tr> + * <tr><td>Windows</td><td>{user}/Application Data/{application}/data</td></tr> + * </table> + * <pre><code> + * where; + * {user} = Current User's home directory + * {application} = Application's name, as set in assembly. + * </code></pre> * <p> * Ignored if the FileConfiguration service is found. * </p> http://git-wip-us.apache.org/repos/asf/zest-qi4j/blob/7c48e8a7/extensions/entitystore-file/src/main/java/org/qi4j/entitystore/file/FileEntityStoreMixin.java ---------------------------------------------------------------------- diff --git a/extensions/entitystore-file/src/main/java/org/qi4j/entitystore/file/FileEntityStoreMixin.java b/extensions/entitystore-file/src/main/java/org/qi4j/entitystore/file/FileEntityStoreMixin.java index 2151e98..fa71ea7 100644 --- a/extensions/entitystore-file/src/main/java/org/qi4j/entitystore/file/FileEntityStoreMixin.java +++ b/extensions/entitystore-file/src/main/java/org/qi4j/entitystore/file/FileEntityStoreMixin.java @@ -74,17 +74,15 @@ public class FileEntityStoreMixin { if( fileConfiguration != null ) { - pathName = new File( fileConfiguration.dataDirectory(), config.get() - .identity() - .get() ).getAbsolutePath(); + String storeId = config.get().identity().get(); + pathName = new File( fileConfiguration.dataDirectory(), storeId ).getAbsolutePath(); } else { pathName = System.getProperty( "user.dir" ) + "/qi4j/filestore/"; } } - File rootDirectory = new File( pathName ).getAbsoluteFile(); - dataDirectory = new File( rootDirectory, "data" ); + dataDirectory = new File( pathName ).getAbsoluteFile(); if( !dataDirectory.exists() ) { if( !dataDirectory.mkdirs() ) @@ -219,7 +217,7 @@ public class FileEntityStoreMixin File dataFile = getDataFile( ref ); if( dataFile.exists() ) { - throw new EntityAlreadyExistsException(ref); + throw new EntityAlreadyExistsException( ref ); } store( dataFile, stateArray ); } @@ -253,6 +251,7 @@ public class FileEntityStoreMixin { throw new EntityNotFoundException( ref ); } + //noinspection ResultOfMethodCallIgnored dataFile.delete(); } } ); @@ -261,7 +260,7 @@ public class FileEntityStoreMixin { if( e instanceof EntityStoreException ) { - throw (EntityStoreException) e; + throw e; } else { @@ -282,8 +281,8 @@ public class FileEntityStoreMixin output.receiveFrom( new Sender<String, IOException>() { @Override - public <ReceiverThrowableType extends Throwable> void sendTo( Receiver<? super String, ReceiverThrowableType> receiver ) - throws ReceiverThrowableType, IOException + public <ThrowableType extends Throwable> void sendTo( Receiver<? super String, ThrowableType> receiver ) + throws ThrowableType, IOException { for( File sliceDirectory : dataDirectory.listFiles() ) { @@ -336,8 +335,8 @@ public class FileEntityStoreMixin output.receiveFrom( new Sender<Reader, IOException>() { @Override - public <ReceiverThrowableType extends Throwable> void sendTo( Receiver<? super Reader, ReceiverThrowableType> receiver ) - throws ReceiverThrowableType, IOException + public <ThrowableType extends Throwable> void sendTo( Receiver<? super Reader, ThrowableType> receiver ) + throws ThrowableType, IOException { for( File sliceDirectory : dataDirectory.listFiles() ) { @@ -355,15 +354,57 @@ public class FileEntityStoreMixin private File getDataFile( String identity ) { + identity = replaceInvalidChars( identity ); String slice = "" + ( Math.abs( identity.hashCode() ) % slices ); File sliceDirectory = new File( dataDirectory, slice ); if( !sliceDirectory.exists() ) { + //noinspection ResultOfMethodCallIgnored sliceDirectory.mkdirs(); } return new File( sliceDirectory, identity + ".json" ); } + /** + * We need to replace all characters that some file system can't handle. + * <p> + * The resulting files should be portable across filesystems. + * </p> + * + * @param identity The identity that needs a file to be stored in. + * + * @return A filesystem-safe name. + */ + private String replaceInvalidChars( String identity ) + { + StringBuilder b = new StringBuilder( identity.length() + 30 ); + for( int i = 0; i < identity.length(); i++ ) + { + char ch = identity.charAt( i ); + if( ( ch >= 'a' && ch <= 'z' ) + || ( ch >= 'A' && ch <= 'Z' ) + || ( ch >= '0' && ch <= '9' ) + || ch == '_' || ch == '.' || ch == '-' ) + { + b.append( ch ); + } + else + { + int value = (int) ch; + b.append( '~' ); + b.append( toHex( value ) ); + } + + } + return b.toString(); + } + + private String toHex( int value ) + { + String result = "000" + Integer.toHexString( value ); + return result.substring( result.length() - 4 ); + } + private File getDataFile( EntityReference ref ) { return getDataFile( ref.identity() ); @@ -372,7 +413,7 @@ public class FileEntityStoreMixin private byte[] fetch( File dataFile ) throws IOException { - byte[] buf = new byte[ 1000 ]; + byte[] buf = new byte[1000]; BufferedInputStream in = null; FileInputStream fis = null; try
