You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@polygene.apache.org by pa...@apache.org on 2015/04/21 13:50:18 UTC
[06/18] zest-sandbox git commit: Move Qi4j related projects in a
`qi4j/` subfolder
http://git-wip-us.apache.org/repos/asf/zest-sandbox/blob/d4dd9c17/qi4j/extensions/entitystore-s3/src/main/java/org/qi4j/entitystore/s3/S3SerializationStoreMixin.java
----------------------------------------------------------------------
diff --git a/qi4j/extensions/entitystore-s3/src/main/java/org/qi4j/entitystore/s3/S3SerializationStoreMixin.java b/qi4j/extensions/entitystore-s3/src/main/java/org/qi4j/entitystore/s3/S3SerializationStoreMixin.java
new file mode 100644
index 0000000..82640bb
--- /dev/null
+++ b/qi4j/extensions/entitystore-s3/src/main/java/org/qi4j/entitystore/s3/S3SerializationStoreMixin.java
@@ -0,0 +1,109 @@
+/* Copyright 2008 Rickard �berg.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ * implied.
+ *
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.qi4j.entitystore.s3;
+
+import org.jets3t.service.S3Service;
+import org.jets3t.service.impl.rest.httpclient.RestS3Service;
+import org.jets3t.service.model.S3Bucket;
+import org.jets3t.service.security.AWSCredentials;
+import org.qi4j.api.configuration.Configuration;
+import org.qi4j.api.entity.EntityReference;
+import org.qi4j.api.injection.scope.This;
+import org.qi4j.io.Input;
+import org.qi4j.io.Output;
+import org.qi4j.spi.entitystore.EntityStoreException;
+
+import java.io.IOException;
+import java.io.Reader;
+import org.qi4j.api.service.ServiceActivation;
+import org.qi4j.spi.entitystore.helpers.MapEntityStore;
+
+/**
+ * Amazon S3 implementation of SerializationStore.
+ * <p/>
+ * To use this you must supply your own access key and secret key for your Amazon S3 account.
+ */
+public class S3SerializationStoreMixin
+ implements ServiceActivation, MapEntityStore
+{
+
+ @This
+ private Configuration<S3Configuration> configuration;
+
+ private S3Service s3Service;
+ private S3Bucket entityBucket;
+
+ // Activatable implementation
+ public void activateService()
+ throws Exception
+ {
+ String awsAccessKey = configuration.get().accessKey().get();
+ String awsSecretKey = configuration.get().secretKey().get();
+
+ if( awsAccessKey == null || awsSecretKey == null )
+ {
+ throw new IllegalStateException( "No S3 keys configured" );
+ }
+
+ AWSCredentials awsCredentials =
+ new AWSCredentials( awsAccessKey, awsSecretKey );
+ s3Service = new RestS3Service( awsCredentials );
+
+ S3Bucket[] s3Buckets = s3Service.listAllBuckets();
+ System.out.println( "How many buckets do I have in S3? " + s3Buckets.length );
+
+ if( s3Buckets.length == 0 )
+ {
+ entityBucket = s3Service.createBucket( "entity-bucket" );
+ System.out.println( "Created entity bucket: " + entityBucket.getName() );
+ }
+ else
+ {
+ entityBucket = s3Buckets[ 0 ];
+ }
+ }
+
+ public void passivateService()
+ throws Exception
+ {
+ }
+
+ public Reader get( EntityReference entityReference )
+ throws EntityStoreException
+ {
+ return null; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public Input<Reader, IOException> entityStates()
+ {
+ return new Input<Reader, IOException>()
+ {
+ public <ReceiverThrowableType extends Throwable> void transferTo( Output<? super Reader, ReceiverThrowableType> output )
+ throws IOException, ReceiverThrowableType
+ {
+ // TODO Implement this
+ throw new UnsupportedOperationException( "Not supported yet." );
+ }
+ };
+ }
+
+ public void applyChanges( MapChanges changes )
+ throws IOException
+ {
+ //To change body of implemented methods use File | Settings | File Templates.
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/zest-sandbox/blob/d4dd9c17/qi4j/extensions/entitystore-s3/src/test/java/org/qi4j/entitystore/s3/S3EntityStoreTest.java
----------------------------------------------------------------------
diff --git a/qi4j/extensions/entitystore-s3/src/test/java/org/qi4j/entitystore/s3/S3EntityStoreTest.java b/qi4j/extensions/entitystore-s3/src/test/java/org/qi4j/entitystore/s3/S3EntityStoreTest.java
new file mode 100644
index 0000000..7afe3ec
--- /dev/null
+++ b/qi4j/extensions/entitystore-s3/src/test/java/org/qi4j/entitystore/s3/S3EntityStoreTest.java
@@ -0,0 +1,47 @@
+/* Copyright 2008 Rickard Öberg.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ * implied.
+ *
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.qi4j.entitystore.s3;
+
+import org.junit.Test;
+import org.qi4j.api.common.Visibility;
+import org.qi4j.bootstrap.AssemblyException;
+import org.qi4j.bootstrap.ModuleAssembly;
+import org.qi4j.entitystore.memory.MemoryEntityStoreService;
+import org.qi4j.test.entity.AbstractEntityStoreTest;
+
+/**
+ * Amazon S3 EntityStore test
+ */
+public abstract class S3EntityStoreTest
+ extends AbstractEntityStoreTest
+{
+ public void assemble( ModuleAssembly module ) throws AssemblyException
+ {
+ super.assemble( module );
+ module.addServices( S3EntityStoreService.class ).instantiateOnStartup();
+
+ ModuleAssembly config = module.layer().module( "config" );
+ config.entities( S3Configuration.class ).visibleIn( Visibility.layer );
+ config.services( MemoryEntityStoreService.class );
+ }
+
+ @Test
+ public void dummyTest()
+ {
+ // All tests are disabled since by default the S3 store doesn't work due to missing account keys!
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/zest-sandbox/blob/d4dd9c17/qi4j/extensions/entitystore-s3/src/test/resources/org/qi4j/entitystore/s3/S3EntityStoreComposite.properties
----------------------------------------------------------------------
diff --git a/qi4j/extensions/entitystore-s3/src/test/resources/org/qi4j/entitystore/s3/S3EntityStoreComposite.properties b/qi4j/extensions/entitystore-s3/src/test/resources/org/qi4j/entitystore/s3/S3EntityStoreComposite.properties
new file mode 100644
index 0000000..66d19ff
--- /dev/null
+++ b/qi4j/extensions/entitystore-s3/src/test/resources/org/qi4j/entitystore/s3/S3EntityStoreComposite.properties
@@ -0,0 +1,2 @@
+accessKey=yourkey
+secretKey=yoursecretkey
http://git-wip-us.apache.org/repos/asf/zest-sandbox/blob/d4dd9c17/qi4j/extensions/entitystore-swift/osgi.bundle
----------------------------------------------------------------------
diff --git a/qi4j/extensions/entitystore-swift/osgi.bundle b/qi4j/extensions/entitystore-swift/osgi.bundle
new file mode 100644
index 0000000..6571e76
--- /dev/null
+++ b/qi4j/extensions/entitystore-swift/osgi.bundle
@@ -0,0 +1,33 @@
+Bundle-Activator:
+
+Private-Package:
+
+Ignore-Package: com_cenqua_clover
+
+Import-Package: org.qi4j.api.composite, \
+ org.qi4j.api.common, \
+ org.qi4j.api.concern, \
+ org.qi4j.api.configuration, \
+ org.qi4j.api.entity, \
+ org.qi4j.api.injection.scope, \
+ org.qi4j.api.io, \
+ org.qi4j.api.mixin, \
+ org.qi4j.api.property, \
+ org.qi4j.api.sideeffect, \
+ org.qi4j.api.service, \
+ org.qi4j.api.structure, \
+ org.qi4j.bootstrap, \
+ org.qi4j.entitystore.memory, \
+ org.qi4j.entitystore.map, \
+ org.qi4j.library.locking, \
+ org.qi4j.spi, \
+ org.qi4j.spi.composite, \
+ org.qi4j.spi.entity, \
+ org.qi4j.spi.entity.helpers, \
+ org.qi4j.spi.entitystore, \
+ org.qi4j.spi.query, \
+ org.qi4j.spi.service, \
+ org.qi4j.spi.uuid, \
+ org.qi4j.spi.serialization
+
+Export-Package: org.qi4j.entitystore.swift
http://git-wip-us.apache.org/repos/asf/zest-sandbox/blob/d4dd9c17/qi4j/extensions/entitystore-swift/pom.xml
----------------------------------------------------------------------
diff --git a/qi4j/extensions/entitystore-swift/pom.xml b/qi4j/extensions/entitystore-swift/pom.xml
new file mode 100644
index 0000000..95a6cbf
--- /dev/null
+++ b/qi4j/extensions/entitystore-swift/pom.xml
@@ -0,0 +1,48 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+ <parent>
+ <groupId>org.qi4j.sandbox</groupId>
+ <artifactId>qi4j-sandbox-extensions</artifactId>
+ <version>0-SNAPSHOT</version>
+ </parent>
+ <modelVersion>4.0.0</modelVersion>
+ <groupId>org.qi4j.extension</groupId>
+ <artifactId>qi4j-entitystore-swift</artifactId>
+ <name>Qi4j Extension - EntityStore - Swift</name>
+ <packaging>jar</packaging>
+
+ <properties>
+ <version.qi4j-tests>1.3</version.qi4j-tests>
+ </properties>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.qi4j.core</groupId>
+ <artifactId>org.qi4j.core.spi</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.qi4j.core</groupId>
+ <artifactId>org.qi4j.core.bootstrap</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.qi4j.core</groupId>
+ <artifactId>org.qi4j.core.testsupport</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.qi4j.library</groupId>
+ <artifactId>org.qi4j.library.locking</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.qi4j.core</groupId>
+ <artifactId>org.qi4j.core.runtime</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.qi4j.test</groupId>
+ <artifactId>org.qi4j.test.performance</artifactId>
+ <version>1.3.0.RC1</version>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+</project>
http://git-wip-us.apache.org/repos/asf/zest-sandbox/blob/d4dd9c17/qi4j/extensions/entitystore-swift/src/main/java/org/qi4j/entitystore/swift/BucketManager.java
----------------------------------------------------------------------
diff --git a/qi4j/extensions/entitystore-swift/src/main/java/org/qi4j/entitystore/swift/BucketManager.java b/qi4j/extensions/entitystore-swift/src/main/java/org/qi4j/entitystore/swift/BucketManager.java
new file mode 100644
index 0000000..82eb3aa
--- /dev/null
+++ b/qi4j/extensions/entitystore-swift/src/main/java/org/qi4j/entitystore/swift/BucketManager.java
@@ -0,0 +1,142 @@
+/*
+ * Copyright 2008 Niclas Hedhman.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ * implied.
+ *
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.qi4j.entitystore.swift;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.RandomAccessFile;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.LinkedList;
+import org.qi4j.spi.entitystore.EntityStoreException;
+
+public class BucketManager
+ implements Runnable
+{
+ private HashMap<Integer, LruEntry> cache;
+ private File bucketdir;
+ private Thread cleanUpThread;
+ private boolean running;
+
+ public BucketManager( File bucketdir )
+ {
+ cache = new HashMap<Integer, LruEntry>();
+ this.bucketdir = bucketdir;
+ bucketdir.mkdirs();
+ cleanUpThread = new Thread( this, "SwiftEntityStore-cleanup" );
+ cleanUpThread.start();
+ }
+
+ synchronized RandomAccessFile get( int hash )
+ throws IOException
+ {
+ LruEntry entry = cache.get( hash );
+ if( entry != null )
+ {
+ return entry.file;
+ }
+ File bucketFile = new File( bucketdir, Integer.toHexString( hash ) );
+ RandomAccessFile bucket = new RandomAccessFile( bucketFile, "rw" );
+ entry = new LruEntry( bucket, hash );
+ cache.put( hash, entry );
+ return bucket;
+ }
+
+ synchronized void close()
+ throws IOException
+ {
+ running = false;
+ cleanUpThread.interrupt();
+ for( LruEntry entry : cache.values() )
+ {
+ entry.file.close();
+ }
+ }
+
+ private void cleanUp()
+ throws IOException
+ {
+ if( cache.size() < 30 )
+ {
+ return;
+ }
+ LinkedList<LruEntry> sorting = new LinkedList<LruEntry>();
+ sorting.addAll( cache.values() );
+ Collections.sort( sorting, new Comparator<LruEntry>()
+ {
+ public int compare( LruEntry lruEntry1, LruEntry lruEntry2 )
+ {
+ if( lruEntry1.created == lruEntry2.created )
+ {
+ return 0;
+ }
+ if( lruEntry1.created > lruEntry2.created )
+ {
+ return 1;
+ }
+
+ return -1;
+ }
+ } );
+ while( cache.size() > 20 )
+ {
+ LruEntry entry = sorting.removeFirst(); // Check if this is at the right end;
+ entry.file.close();
+ cache.remove( entry.hash );
+ }
+ }
+
+ public void run()
+ {
+ running = true;
+ try
+ {
+ while( running )
+ {
+ synchronized( this )
+ {
+ wait( 15000 );
+ cleanUp();
+ }
+ }
+ }
+ catch( InterruptedException e )
+ {
+ // ignore, normal shutdown
+ }
+ catch( IOException e )
+ {
+ throw new EntityStoreException( "What the hell!!!???", e );
+ }
+ }
+
+ private static class LruEntry
+ {
+ private long created;
+ private RandomAccessFile file;
+ private int hash;
+
+ public LruEntry( RandomAccessFile bucket, int hash )
+ {
+ this.file = bucket;
+ this.hash = hash;
+ created = System.currentTimeMillis();
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/zest-sandbox/blob/d4dd9c17/qi4j/extensions/entitystore-swift/src/main/java/org/qi4j/entitystore/swift/DataBlock.java
----------------------------------------------------------------------
diff --git a/qi4j/extensions/entitystore-swift/src/main/java/org/qi4j/entitystore/swift/DataBlock.java b/qi4j/extensions/entitystore-swift/src/main/java/org/qi4j/entitystore/swift/DataBlock.java
new file mode 100644
index 0000000..fee55d4
--- /dev/null
+++ b/qi4j/extensions/entitystore-swift/src/main/java/org/qi4j/entitystore/swift/DataBlock.java
@@ -0,0 +1,121 @@
+/*
+ * Copyright 2008 Niclas Hedhman.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ * implied.
+ *
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.qi4j.entitystore.swift;
+
+import org.qi4j.spi.entity.QualifiedIdentity;
+import org.qi4j.api.entity.EntityReference;
+
+import java.util.Arrays;
+
+class DataBlock
+{
+ EntityReference reference;
+ byte[] data;
+ long instanceVersion;
+ int schemaVersion;
+
+ public DataBlock( EntityReference reference, byte[] data, long instanceVersion, int schemaVersion )
+ {
+ this.reference = reference;
+ this.data = data;
+ this.instanceVersion = instanceVersion;
+ this.schemaVersion = schemaVersion;
+ }
+
+ public boolean equals( Object o )
+ {
+ if( this == o )
+ {
+ return true;
+ }
+ if( o == null || getClass() != o.getClass() )
+ {
+ return false;
+ }
+
+ DataBlock dataBlock = (DataBlock) o;
+
+ if( instanceVersion != dataBlock.instanceVersion )
+ {
+ return false;
+ }
+ if( schemaVersion != dataBlock.schemaVersion )
+ {
+ return false;
+ }
+ if( !Arrays.equals( data, dataBlock.data ) )
+ {
+ return false;
+ }
+ if( !reference.equals( dataBlock.reference ) )
+ {
+ return false;
+ }
+
+ return true;
+ }
+
+ public int hashCode()
+ {
+ int result;
+ result = reference.hashCode();
+ result = 31 * result + Arrays.hashCode( data );
+ result = 31 * result + (int) ( instanceVersion ^ ( instanceVersion >>> 32 ) );
+ result = 31 * result + schemaVersion;
+ return result;
+ }
+
+ public String toString()
+ {
+ StringBuffer buf = new StringBuffer();
+ buf.append( schemaVersion );
+ buf.append( '[' );
+ buf.append( reference );
+ buf.append( ':' );
+ buf.append( instanceVersion );
+ buf.append( '{' );
+ boolean first = true;
+ for( byte b : data )
+ {
+ if( !first )
+ {
+ buf.append( ',' );
+ }
+ first = false;
+ buf.append( toHex( b ) );
+ }
+ buf.append( '}' );
+ buf.append( ']' );
+ return buf.toString();
+ }
+
+ private String toHex( byte b )
+ {
+ int data = b;
+
+ if( data < 0 )
+ {
+ data = data + 256;
+ }
+ if( data < 16 )
+ {
+ return "0" + Integer.toHexString( data );
+ }
+ return Integer.toHexString( data );
+ }
+}
http://git-wip-us.apache.org/repos/asf/zest-sandbox/blob/d4dd9c17/qi4j/extensions/entitystore-swift/src/main/java/org/qi4j/entitystore/swift/DataStore.java
----------------------------------------------------------------------
diff --git a/qi4j/extensions/entitystore-swift/src/main/java/org/qi4j/entitystore/swift/DataStore.java b/qi4j/extensions/entitystore-swift/src/main/java/org/qi4j/entitystore/swift/DataStore.java
new file mode 100644
index 0000000..8364c2e
--- /dev/null
+++ b/qi4j/extensions/entitystore-swift/src/main/java/org/qi4j/entitystore/swift/DataStore.java
@@ -0,0 +1,480 @@
+/*
+ * Copyright 2008 Niclas Hedhman.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ * implied.
+ *
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.qi4j.entitystore.swift;
+
+import org.qi4j.api.entity.EntityReference;
+import org.qi4j.io.Input;
+import org.qi4j.io.Output;
+import org.qi4j.io.Receiver;
+import org.qi4j.io.Sender;
+import org.qi4j.spi.entitystore.EntityStoreException;
+
+import java.io.*;
+
+/**
+ * This class handles the Heap Data file.
+ * The format of the file is as follows;
+ * <p/>
+ * <code><pre>
+ * At OFFSET = 0
+ * [cleanShutDown] 1 byte
+ * [formatVersion] 4 bytes
+ * [noOfEntries] 4 bytes
+ * [noOfIDentries] 4 bytes
+ * <p/>
+ * At OFFSET 256
+ * [blockSize] 4 bytes
+ * [usage] 1 byte (0=Unused, 1=prime, 2=mirror, 3=primeChanged, 4=mirrorChanged)
+ * [instanceVersion] 8 bytes
+ * [schemaVersion] 4 bytes
+ * [identitySize] 1 byte
+ * [identity] IDENTITY_MAX_LENGTH bytes
+ * [mirrorPointer] 8 bytes
+ * [primeDataLength] 4 bytes
+ * [primeData] n bytes
+ * [mirrorDataLength] 4 bytes
+ * [mirrorData] n bytes
+ * <p/>
+ * At OFFSET 256 + [blockSize]
+ * same as above, repeat until [blockSize] == -1 marking end of DataArea.
+ * </pre></code>
+ * The <b>mirrorPointer</b> points to the mirrorData block.
+ */
+public class DataStore
+{
+ static final long DATA_AREA_OFFSET = 256;
+ private static final int BLOCK_OVERHEAD = 26;
+ private static final int CURRENT_VERSION = 1;
+ private static final String HEAP_DATA_FILENAME = "heap.data";
+
+ private static final int USAGE_UNUSED = 0;
+ private static final int USAGE_PRIME = 1;
+ private static final int USAGE_MIRROR = 2;
+ private static final int USAGE_PRIMECHANGE = 3;
+ private static final int USAGE_MIRRORCHANGE = 4;
+
+ private RandomAccessFile dataFile;
+ private IdentityFile identityFile;
+ private int identityMaxLength;
+ private UndoManager undoManager;
+ private int entries;
+ private File dataDir;
+
+ public DataStore( File dataDirectory, UndoManager undoManager )
+ throws IOException
+ {
+ this.undoManager = undoManager;
+ identityMaxLength = 128; // Default value...
+ this.dataDir = dataDirectory.getAbsoluteFile();
+ dataDir.mkdirs();
+ File file = new File( dataDir, HEAP_DATA_FILENAME );
+ if (!file.exists())
+ {
+ file.createNewFile();
+ }
+ dataFile = new RandomAccessFile( file, "rw" );
+ boolean cleanShutDown;
+ if (file.length() > 0)
+ {
+ dataFile.seek( 0 );
+ cleanShutDown = dataFile.readBoolean();
+ dataFile.seek( 0 );
+ dataFile.writeBoolean( false );
+ dataFile.writeInt( CURRENT_VERSION ); // Write Version.
+ entries = dataFile.readInt();
+ identityMaxLength = dataFile.readInt();
+ } else
+ {
+ cleanShutDown = false;
+ dataFile.writeBoolean( false );
+ entries = 0;
+ dataFile.writeInt( CURRENT_VERSION ); // Write Version.
+ dataFile.writeInt( entries );
+ dataFile.writeInt( identityMaxLength );
+ dataFile.seek( DATA_AREA_OFFSET - 1 );
+ dataFile.writeByte( 0 );
+ dataFile.seek( DATA_AREA_OFFSET );
+ dataFile.writeInt( -1 ); // EOF marker
+ }
+ // Ensure full flush, then reopen...
+ dataFile.close();
+
+ dataFile = new RandomAccessFile( file, "rw" );
+
+ if (!cleanShutDown)
+ {
+ reIndex();
+ } else
+ {
+ File idDir = new File( dataDir, "idx" );
+ try
+ {
+ identityFile = IdentityFile.use( idDir );
+ }
+ catch (MalformedIdentityDirectoryException e)
+ {
+ reIndex();
+ }
+ }
+ if (identityFile.entries() < entries * 2)
+ {
+ reIndex();
+ }
+ }
+
+ RandomAccessFile dataFile()
+ {
+ return dataFile;
+ }
+
+ IdentityFile identityFile()
+ {
+ return identityFile;
+ }
+
+ DataBlock readData( EntityReference reference )
+ throws IOException
+ {
+ long pos = identityFile.find( reference );
+ if (pos < 0)
+ {
+ return null;
+ }
+ dataFile.seek( pos );
+ dataFile.skipBytes( 4 ); // Skip BlockSize
+ return readDataBlock( reference );
+ }
+
+ void putData( DataBlock data )
+ throws IOException
+ {
+ long pos = identityFile.find( data.reference );
+ if (pos < 0)
+ {
+ putNewData( data );
+ } else
+ {
+ dataFile.seek( pos );
+ int blockSize = dataFile.readInt();
+ long usagePointer = dataFile.getFilePointer();
+ byte usage = dataFile.readByte();
+ dataFile.skipBytes( -1 );
+ dataFile.writeByte( usage == USAGE_PRIME ? USAGE_PRIMECHANGE : USAGE_MIRRORCHANGE );
+ int dataAreaSize = (blockSize - BLOCK_OVERHEAD) / 2 - 4;
+ if (dataAreaSize < data.data.length)
+ {
+ putTooLarge( data, pos, usagePointer, usage );
+ } else
+ {
+ putOver( data, pos, usagePointer, usage );
+ }
+ }
+ }
+
+ /* In this case we need to write the new data to the opposite of the current active block. */
+
+ private void putOver( DataBlock data, long pos, long usagePointer, byte usage )
+ throws IOException
+ {
+ dataFile.seek( usagePointer ); // Point to "usage"
+ dataFile.skipBytes( 13 ); // Skip usage, instanceVersion and schemaVersion
+ EntityReference existingReference = readReference();
+ if (!existingReference.equals( data.reference ))
+ {
+ throw new EntityStoreException( "Inconsistent Data Heap: was " + existingReference + ", expected " + data.reference );
+ }
+ long mirror = dataFile.readLong();
+ if (usage == USAGE_PRIME)
+ {
+ dataFile.seek( mirror );
+ }
+ UndoModifyCommand undoModifyCommand = new UndoModifyCommand( pos, usage, data.instanceVersion, data.schemaVersion );
+ undoManager.saveUndoCommand( undoModifyCommand );
+
+ dataFile.writeInt( data.data.length );
+ dataFile.write( data.data );
+ dataFile.seek( usagePointer );
+ dataFile.writeByte( usage == USAGE_PRIME ? USAGE_MIRROR : USAGE_PRIME );
+ }
+
+ /* This case is when the data doesn't fit in the pre-allocated extra space. Write it to the end, and mark the
+ previous block unused.
+ */
+
+ private void putTooLarge( DataBlock data, long pos, long usagePointer, byte usage )
+ throws IOException
+ {
+ long newPosition = addData( data );
+ UndoModifyCommand undoModifyCommand = new UndoModifyCommand( pos, usage, data.instanceVersion, data.schemaVersion );
+ undoManager.saveUndoCommand( undoModifyCommand );
+ dataFile.seek( usagePointer );
+ dataFile.writeByte( USAGE_UNUSED );
+ UndoDropIdentityCommand undoDropIdentityCommand = new UndoDropIdentityCommand( data.reference, pos );
+ undoManager.saveUndoCommand( undoDropIdentityCommand );
+ identityFile.remember( data.reference, newPosition );
+ }
+
+ private void putNewData( DataBlock data )
+ throws IOException
+ {
+ long pos;
+ pos = addData( data );
+ UndoNewIdentityCommand undoNewIdentityCommand = new UndoNewIdentityCommand( data.reference );
+ undoManager.saveUndoCommand( undoNewIdentityCommand );
+ identityFile.remember( data.reference, pos );
+ }
+
+ public void delete( EntityReference reference )
+ throws IOException
+ {
+ long pos = identityFile.find( reference );
+ if (pos < 0)
+ {
+ // Doesn't exist.
+ return;
+ }
+ dataFile.seek( pos );
+ dataFile.skipBytes( 4 ); // Skip BlockSize
+ byte usage = dataFile.readByte();
+ if (usage == USAGE_UNUSED)
+ {
+ // Not used?? Why is the IdentityFile pointing to it then?? Should the following line actually be
+ // executed here.
+ // identityFile.drop( identity );
+ return;
+ }
+ UndoDropIdentityCommand undoDropIdentityCommand = new UndoDropIdentityCommand( reference, pos );
+ undoManager.saveUndoCommand( undoDropIdentityCommand );
+
+ UndoDeleteCommand undoDeleteCommand = new UndoDeleteCommand( pos, usage );
+ undoManager.saveUndoCommand( undoDeleteCommand );
+
+ identityFile.drop( reference );
+ dataFile.skipBytes( -1 );
+ dataFile.writeByte( USAGE_UNUSED ); // Mark Unused block
+ }
+
+ void flush()
+ throws IOException
+ {
+// dataFile.getFD().sync();
+ }
+
+ void close()
+ throws IOException
+ {
+ identityFile.close();
+ dataFile.seek( 0 );
+ dataFile.writeBoolean( true );
+ dataFile.writeInt( entries );
+ dataFile.close();
+ }
+
+ private long addData( DataBlock block )
+ throws IOException
+ {
+ dataFile.seek( dataFile.length() - 4 ); // last 4 bytes contain a -1
+ long blockStart = dataFile.getFilePointer();
+
+ // Allow each datablock to grow to twice its size, and provide a primary and mirror allocation.
+ int dataAreaSize = (block.data.length * 2 + 4) * 2;
+ UndoExtendCommand undoExtendCommand = new UndoExtendCommand( blockStart );
+ undoManager.saveUndoCommand( undoExtendCommand );
+
+ int blockSize = dataAreaSize + identityMaxLength + BLOCK_OVERHEAD;
+ dataFile.writeInt( blockSize );
+ long usagePointer = dataFile.getFilePointer();
+ dataFile.writeByte( USAGE_PRIMECHANGE ); // In-progress
+ dataFile.writeLong( block.instanceVersion );
+ dataFile.writeInt( block.schemaVersion );
+ writeIdentity( block.reference );
+
+ long mirrorPosition = blockStart + BLOCK_OVERHEAD + identityMaxLength + dataAreaSize / 2;
+ dataFile.writeLong( mirrorPosition );
+ dataFile.writeInt( block.data.length );
+ dataFile.write( block.data );
+ dataFile.seek( blockStart + blockSize );
+ dataFile.writeInt( -1 ); // Write EOF marker.
+ dataFile.seek( usagePointer );
+ dataFile.write( USAGE_PRIME );
+ return blockStart;
+ }
+
+ private void writeIdentity( EntityReference reference )
+ throws IOException
+ {
+ byte[] idBytes = reference.identity().getBytes( "UTF-8" );
+ if (idBytes.length > identityMaxLength)
+ {
+ throw new EntityStoreException( "Identity is too long. Only " + identityMaxLength + " characters are allowed in this EntityStore." );
+ }
+ byte[] id = new byte[identityMaxLength];
+ System.arraycopy( idBytes, 0, id, 0, idBytes.length );
+ dataFile.writeByte( idBytes.length );
+ dataFile.write( id );
+ }
+
+ private void compact()
+ throws IOException
+ {
+/*
+ File newFileName = new File( dataDir, "heap-compacting.data" );
+ RandomAccessFile newFile = new RandomAccessFile( newFileName, "rw" );
+ File oldFileName = new File( dataDir, "heap.data" );
+ RandomAccessFile oldFile = new RandomAccessFile( oldFileName, "r" );
+
+ oldFile.seek( DATA_AREA_OFFSET ); // Skip initial bytes;
+ newFile.seek( DATA_AREA_OFFSET ); // Skip initial bytes;
+
+ int counter = 0;
+
+ // Move the Records!!
+
+ entries = counter;
+
+ newFile.writeBoolean( false );
+ newFile.writeInt( CURRENT_VERSION ); // Write Version.
+
+ newFile.writeInt( entries );
+ reIndex( dataDir );
+ dataFile.close();
+ newFile.close();
+
+ File standardFilename = new File( dataDir, "heap.data" );
+ newFileName.renameTo( standardFilename );
+ dataFile = new RandomAccessFile( standardFilename, "rw" );
+*/
+ }
+
+ private void reIndex()
+ throws IOException
+ {
+ identityFile = IdentityFile.create( new File( dataDir, "idx" ), identityMaxLength + 16, entries < 5000 ? 10000 : entries * 2 );
+
+ dataFile.seek( DATA_AREA_OFFSET );
+ while (dataFile.getFilePointer() < dataFile.length())
+ {
+ long blockStart = dataFile.getFilePointer();
+ int blockSize = dataFile.readInt();
+ if (blockSize == -1)
+ {
+ break;
+ }
+ byte usage = dataFile.readByte();
+ dataFile.skipBytes( 12 ); // Skip instanceVersion and schemaVersion
+ EntityReference reference = readReference();
+ if (usage != USAGE_UNUSED)
+ {
+ identityFile.remember( reference, blockStart );
+ }
+ dataFile.seek( blockStart + blockSize );
+ }
+ }
+
+ public Input<Reader, IOException> data()
+ {
+ return new Input<Reader, IOException>()
+ {
+ public <ReceiverThrowableType extends Throwable> void transferTo( Output<? super Reader, ReceiverThrowableType> output )
+ throws IOException, ReceiverThrowableType
+ {
+ output.receiveFrom(
+ new Sender<Reader, IOException>()
+ {
+ public <ReceiverThrowableType extends Throwable> void sendTo( Receiver<? super Reader, ReceiverThrowableType> receiver )
+ throws ReceiverThrowableType, IOException
+ {
+ long position = DATA_AREA_OFFSET;
+ while( position < dataFile.length() )
+ {
+ dataFile.seek( position );
+ int blockSize = dataFile.readInt();
+ if( blockSize == -1 ) // EOF marker
+ {
+ return;
+ }
+ if( blockSize == 0 )
+ {
+ // TODO This is a bug. Why does it occur??
+ throw new InternalError();
+ }
+ position = position + blockSize; // position for next round...
+ DataBlock block = readDataBlock( null );
+ if( block != null )
+ {
+ receiver.receive( new StringReader( new String( block.data, "UTF-8" ) ) );
+ }
+ }
+ }
+ }
+ );
+ }
+ };
+ }
+
+ private DataBlock readDataBlock( EntityReference reference )
+ throws IOException
+ {
+ byte usage = dataFile.readByte();
+ if (usage == USAGE_UNUSED)
+ {
+ return null;
+ }
+ long instanceVersion = dataFile.readLong();
+ int schemaVersion = dataFile.readInt();
+ EntityReference existingReference = readReference();
+ if (reference == null)
+ {
+ reference = existingReference;
+ }
+ if (!existingReference.equals( reference ))
+ {
+ throw new EntityStoreException( "Inconsistent Data Heap." );
+ }
+ if (usage == USAGE_MIRROR)
+ {
+ long mirror = dataFile.readLong();
+ dataFile.seek( mirror );
+ } else
+ {
+ dataFile.skipBytes( 8 ); // skip the MirrorPointer
+ }
+ int dataSize = dataFile.readInt();
+ if (dataSize < 0)
+ {
+ throw new InternalError();
+ }
+ byte[] data = new byte[dataSize];
+ dataFile.read( data );
+ return new DataBlock( reference, data, instanceVersion, schemaVersion );
+ }
+
+ private EntityReference readReference()
+ throws IOException
+ {
+ int idSize = dataFile.readByte();
+ if (idSize < 0)
+ {
+ idSize = idSize + 256; // Fix 2's-complement negative values of bytes into unsigned 8 bit.
+ }
+ byte[] idData = new byte[idSize];
+ dataFile.read( idData );
+ dataFile.skipBytes( identityMaxLength - idSize );
+ return new EntityReference( new String( idData ) );
+ }
+}
http://git-wip-us.apache.org/repos/asf/zest-sandbox/blob/d4dd9c17/qi4j/extensions/entitystore-swift/src/main/java/org/qi4j/entitystore/swift/FileUtils.java
----------------------------------------------------------------------
diff --git a/qi4j/extensions/entitystore-swift/src/main/java/org/qi4j/entitystore/swift/FileUtils.java b/qi4j/extensions/entitystore-swift/src/main/java/org/qi4j/entitystore/swift/FileUtils.java
new file mode 100644
index 0000000..14f77f7
--- /dev/null
+++ b/qi4j/extensions/entitystore-swift/src/main/java/org/qi4j/entitystore/swift/FileUtils.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2008 Niclas Hedhman.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ * implied.
+ *
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.qi4j.entitystore.swift;
+
+import java.io.File;
+
+public class FileUtils
+{
+
+ public static void delete( File file )
+ {
+ if( !file.exists() )
+ {
+ return;
+ }
+ if( file.isDirectory() )
+ {
+ for( File child : file.listFiles() )
+ {
+ delete( child );
+ }
+ }
+ file.delete();
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/zest-sandbox/blob/d4dd9c17/qi4j/extensions/entitystore-swift/src/main/java/org/qi4j/entitystore/swift/HeapDump.java
----------------------------------------------------------------------
diff --git a/qi4j/extensions/entitystore-swift/src/main/java/org/qi4j/entitystore/swift/HeapDump.java b/qi4j/extensions/entitystore-swift/src/main/java/org/qi4j/entitystore/swift/HeapDump.java
new file mode 100644
index 0000000..e9fb3f2
--- /dev/null
+++ b/qi4j/extensions/entitystore-swift/src/main/java/org/qi4j/entitystore/swift/HeapDump.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright 2009 Niclas Hedhman.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ * implied.
+ *
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.qi4j.entitystore.swift;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.RandomAccessFile;
+import org.qi4j.api.entity.EntityReference;
+
+public class HeapDump
+{
+ static final long DATA_AREA_OFFSET = 256;
+ private static final String HEAP_DATA_FILENAME = "heap.data";
+
+ private static RandomAccessFile dataFile;
+
+ public static void main( String[] args )
+ throws Exception
+ {
+ File dataDirectory = new File( args[ 0 ] ).getAbsoluteFile();
+ File dataDir = dataDirectory.getAbsoluteFile();
+ File file = new File( dataDir, HEAP_DATA_FILENAME );
+ dataFile = new RandomAccessFile( file, "rw" );
+
+ long position = 256;
+ dataFile.seek( position ); // skip maintenance block.
+ while( dataFile.getFilePointer() < dataFile.length() )
+ {
+ int blockSize = dataFile.readInt();
+ if( blockSize == -1 )
+ {
+ break;
+ }
+ int usage = dataFile.readByte();
+ if( usage != 0 )
+ {
+ long instanceVersion = dataFile.readLong();
+ int schemaVersion = dataFile.readInt();
+ long refPos = dataFile.getFilePointer();
+ String ref = readReference().identity();
+ System.out.print( ref );
+ dataFile.seek( refPos + 129 );
+ long mirror = dataFile.readLong();
+ if( usage == 2 )
+ dataFile.seek( mirror );
+ if( usage == 3 || usage == 4 )
+ {
+ System.err.println( "Inconsistent Heap: " + usage + ", pos: " + position );
+ System.err.flush();
+ }
+ int dataSize = dataFile.readInt();
+ byte[] data = new byte[ dataSize ];
+ dataFile.read( data, 0, dataSize );
+ System.out.println( new String( data, "UTF-8" ) );
+ System.out.flush();
+ }
+ position = position + blockSize;
+ dataFile.seek( position );
+ }
+ }
+
+ private static EntityReference readReference()
+ throws IOException
+ {
+ int idSize = dataFile.readByte();
+ if( idSize < 0 )
+ {
+ idSize = idSize + 256; // Fix 2's-complement negative values of bytes into unsigned 8 bit.
+ }
+ byte[] idData = new byte[idSize];
+ dataFile.read( idData );
+ dataFile.skipBytes( 128 - idSize );
+ return new EntityReference( new String( idData ) );
+ }
+}
http://git-wip-us.apache.org/repos/asf/zest-sandbox/blob/d4dd9c17/qi4j/extensions/entitystore-swift/src/main/java/org/qi4j/entitystore/swift/IdentityFile.java
----------------------------------------------------------------------
diff --git a/qi4j/extensions/entitystore-swift/src/main/java/org/qi4j/entitystore/swift/IdentityFile.java b/qi4j/extensions/entitystore-swift/src/main/java/org/qi4j/entitystore/swift/IdentityFile.java
new file mode 100644
index 0000000..69af039
--- /dev/null
+++ b/qi4j/extensions/entitystore-swift/src/main/java/org/qi4j/entitystore/swift/IdentityFile.java
@@ -0,0 +1,302 @@
+/*
+ * Copyright 2008 Niclas Hedhman.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ * implied.
+ *
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.qi4j.entitystore.swift;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.RandomAccessFile;
+import org.qi4j.api.entity.EntityReference;
+
+/**
+ * For Slot 0
+ * [version] - 4 bytes
+ * [noOfEntries] - 4 bytes
+ * [slotSize] - 4 bytes
+ *
+ * For Slot 1..n
+ * [isExtended] - 1 byte
+ * [position] - 8 bytes
+ * [identity] - [slotSize-16] bytes
+ */
+public class IdentityFile
+{
+ private static final int CURRENT_VERSION = 1;
+
+ private RandomAccessFile identityStore;
+ private int entries;
+ private int slotSize;
+ private boolean closed;
+ private BucketManager bucketManager;
+
+ private IdentityFile( RandomAccessFile store, File bucketDir, int slotSize, int entries )
+ throws IOException
+ {
+ this.closed = false;
+ identityStore = store;
+ bucketManager = new BucketManager( bucketDir );
+ this.slotSize = slotSize;
+ this.entries = entries;
+ }
+
+ int entries()
+ {
+ return entries;
+ }
+
+ long find( EntityReference reference )
+ throws IOException
+ {
+ if( closed )
+ {
+ throw new IdentityFileClosedException();
+ }
+ if( reference.identity().length() > slotSize - 16 )
+ {
+ throw new IdentityTooLongException( reference );
+ }
+ final int slot = getSlot( reference );
+ identityStore.seek( slot * slotSize );
+ boolean isExtended = identityStore.readBoolean();
+ if( !isExtended )
+ {
+ long pos = identityStore.readLong();
+ String idString = identityStore.readUTF();
+ if( idString.length() == 0 )
+ {
+ return -1;
+ }
+ EntityReference foundReference = new EntityReference( idString );
+ if( foundReference.equals( reference ) )
+ {
+ return pos;
+ }
+ return -1;
+ }
+ RandomAccessFile buckets = bucketManager.get( slot );
+ int next = 0;
+ while( next * slotSize < buckets.length() )
+ {
+ buckets.seek( next * slotSize );
+ boolean isUsed = buckets.readBoolean();
+ long pos = buckets.readLong();
+ EntityReference foundReference = new EntityReference( buckets.readUTF() );
+ if( isUsed && foundReference.equals( reference ) )
+ {
+ return pos;
+ }
+ next++;
+ }
+ return -1;
+ }
+
+ void remember( EntityReference reference, long pos )
+ throws IOException
+ {
+ if( closed )
+ {
+ throw new IdentityFileClosedException();
+ }
+ if( reference.identity().length() > slotSize - 16 )
+ {
+ throw new IdentityTooLongException( reference );
+ }
+ final int slot = getSlot( reference );
+ identityStore.seek( slot * slotSize );
+ boolean isExtended = identityStore.readBoolean();
+ if( isExtended )
+ {
+ RandomAccessFile bucket = bucketManager.get( slot );
+ bucket.seek( 0 );
+ int next = 0;
+ while( next * slotSize < bucket.length() )
+ {
+ bucket.seek( next * slotSize );
+ boolean isUsed = bucket.readBoolean();
+ if( !isUsed )
+ {
+ break;
+ }
+ next++;
+ }
+ bucket.seek( next * slotSize );
+ bucket.writeBoolean( true );
+ bucket.writeLong( pos );
+ bucket.writeUTF( reference.identity() );
+ fillExtras( bucket, next, slotSize );
+ }
+ else
+ {
+ long existingPos = identityStore.readLong();
+ if( existingPos == -1 )
+ {
+ // Not used yet.
+ identityStore.seek( slot * slotSize );
+ identityStore.writeBoolean( false );
+ identityStore.writeLong( pos );
+ identityStore.writeUTF( reference.identity() );
+ }
+ else
+ {
+ // Move existing record over to a new bucket.
+ RandomAccessFile bucket = bucketManager.get( slot );
+ bucket.seek( 0 );
+ bucket.writeBoolean( true );
+ bucket.writeLong( existingPos );
+ bucket.writeUTF( identityStore.readUTF() );
+ fillExtras( bucket, 0, slotSize );
+ bucket.seek( slotSize );
+ bucket.writeBoolean( true );
+ bucket.writeLong( pos );
+ bucket.writeUTF( reference.identity() );
+ fillExtras( bucket, 1, slotSize );
+ identityStore.seek( slot * slotSize );
+ identityStore.writeBoolean( true );
+ identityStore.writeLong( -1 );
+ identityStore.writeUTF( "" );
+ fillExtras( identityStore, slot, slotSize );
+ }
+ }
+ }
+
+ void drop( EntityReference reference )
+ throws IOException
+ {
+ if( closed )
+ {
+ throw new IdentityFileClosedException();
+ }
+ if( reference.identity().length() > slotSize - 16 )
+ {
+ throw new IdentityTooLongException( reference );
+ }
+ final int slot = getSlot( reference );
+ identityStore.seek( slot * slotSize );
+ boolean isExtended = identityStore.readBoolean();
+ if( isExtended )
+ {
+ RandomAccessFile buckets = bucketManager.get( slot );
+ int next = 0;
+ while( next * slotSize < buckets.length() )
+ {
+ buckets.seek( next * slotSize );
+ boolean isUsed = buckets.readBoolean();
+ buckets.readLong(); //ignore, should probably be changed to skip(8);
+ EntityReference foundReference = new EntityReference( buckets.readUTF() );
+ if( isUsed && foundReference.equals( reference ) )
+ {
+ buckets.seek( next * slotSize );
+ buckets.writeBoolean( false );
+ return;
+ }
+ next++;
+ }
+ }
+ else
+ {
+ identityStore.readLong(); // ignore the pos
+ EntityReference foundReference = new EntityReference( identityStore.readUTF() );
+ if( reference.equals( foundReference ) )
+ {
+ // found, no erase.
+ identityStore.seek( slot * slotSize );
+ identityStore.writeBoolean( false );
+ identityStore.writeLong( -1 );
+ identityStore.writeUTF( "" );
+ fillExtras( identityStore, slot, slotSize );
+ }
+ }
+ }
+
+ private int getSlot( EntityReference identity )
+ {
+ int hashCode = identity.hashCode();
+ hashCode = hashCode < 0 ? -hashCode : hashCode;
+ return 1 + hashCode % entries;
+ }
+
+ public void close()
+ throws IOException
+ {
+ bucketManager.close();
+ identityStore.close();
+ closed = true;
+ }
+
+ private static void initialize( RandomAccessFile newFile, int entries, int slotSize )
+ throws IOException
+ {
+ for( int i = 1; i <= entries + 1; i++ )
+ {
+ newFile.seek( i * slotSize );
+ newFile.writeBoolean( false ); // Extended
+ newFile.writeLong( -1 ); // Position
+ newFile.writeUTF( "" ); // Identity
+ fillExtras( newFile, i, slotSize );
+ }
+ newFile.seek( 0 );
+ newFile.writeInt( CURRENT_VERSION );
+ newFile.writeInt( entries );
+ newFile.writeInt( slotSize );
+ }
+
+ private static void fillExtras( RandomAccessFile accessFile, int slot, int slotSize )
+ throws IOException
+ {
+ long pointer = accessFile.getFilePointer();
+ long fillTo = ( slot + 1 ) * slotSize - 1;
+ long arraysize = fillTo - pointer;
+ if( arraysize < 0 )
+ {
+ System.err.println( "Negative Array Size detected:" + arraysize );
+ }
+ byte[] extras = new byte[(int) arraysize];
+ accessFile.write( extras );
+ }
+
+ public static IdentityFile use( File identityDir )
+ throws MalformedIdentityDirectoryException, IOException
+ {
+ File idFile = new File( identityDir, "id-hash.data" );
+ if( !idFile.exists() )
+ {
+ throw new MalformedIdentityDirectoryException( identityDir );
+ }
+ File bucketDir = new File( identityDir, "buckets" );
+ if( !bucketDir.exists() )
+ {
+ throw new MalformedIdentityDirectoryException( identityDir );
+ }
+ RandomAccessFile store = new RandomAccessFile( idFile, "rw" );
+ int version = store.readInt(); // Read Version
+ int entries = store.readInt(); // Read entries
+ int slotSize = store.readInt(); // Read slotSize
+ return new IdentityFile( store, bucketDir, slotSize, entries );
+ }
+
+ public static IdentityFile create( File identityDir, int slotSize, int idEntries )
+ throws IOException
+ {
+ FileUtils.delete( identityDir );
+ identityDir.mkdirs();
+ File idFile = new File( identityDir, "id-hash.data" );
+ RandomAccessFile store = new RandomAccessFile( idFile, "rw" );
+ initialize( store, idEntries, slotSize );
+ File bucketDir = new File( identityDir, "buckets" );
+ return new IdentityFile( store, bucketDir, slotSize, idEntries );
+ }
+}
http://git-wip-us.apache.org/repos/asf/zest-sandbox/blob/d4dd9c17/qi4j/extensions/entitystore-swift/src/main/java/org/qi4j/entitystore/swift/IdentityFileClosedException.java
----------------------------------------------------------------------
diff --git a/qi4j/extensions/entitystore-swift/src/main/java/org/qi4j/entitystore/swift/IdentityFileClosedException.java b/qi4j/extensions/entitystore-swift/src/main/java/org/qi4j/entitystore/swift/IdentityFileClosedException.java
new file mode 100644
index 0000000..4b663b6
--- /dev/null
+++ b/qi4j/extensions/entitystore-swift/src/main/java/org/qi4j/entitystore/swift/IdentityFileClosedException.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2008 Niclas Hedhman.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ * implied.
+ *
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.qi4j.entitystore.swift;
+
+import org.qi4j.spi.entitystore.EntityStoreException;
+
+public class IdentityFileClosedException extends EntityStoreException
+{
+}
http://git-wip-us.apache.org/repos/asf/zest-sandbox/blob/d4dd9c17/qi4j/extensions/entitystore-swift/src/main/java/org/qi4j/entitystore/swift/IdentityTooLongException.java
----------------------------------------------------------------------
diff --git a/qi4j/extensions/entitystore-swift/src/main/java/org/qi4j/entitystore/swift/IdentityTooLongException.java b/qi4j/extensions/entitystore-swift/src/main/java/org/qi4j/entitystore/swift/IdentityTooLongException.java
new file mode 100644
index 0000000..bb5c4a0
--- /dev/null
+++ b/qi4j/extensions/entitystore-swift/src/main/java/org/qi4j/entitystore/swift/IdentityTooLongException.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2008 Niclas Hedhman.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ * implied.
+ *
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.qi4j.entitystore.swift;
+
+import org.qi4j.api.entity.EntityReference;
+import org.qi4j.spi.entitystore.EntityStoreException;
+
+public class IdentityTooLongException extends EntityStoreException
+{
+ public IdentityTooLongException( EntityReference identity )
+ {
+ super( "The identity is too long for the configured store: " + identity.toString().length() + ", " + identity );
+
+ }
+}
http://git-wip-us.apache.org/repos/asf/zest-sandbox/blob/d4dd9c17/qi4j/extensions/entitystore-swift/src/main/java/org/qi4j/entitystore/swift/MalformedIdentityDirectoryException.java
----------------------------------------------------------------------
diff --git a/qi4j/extensions/entitystore-swift/src/main/java/org/qi4j/entitystore/swift/MalformedIdentityDirectoryException.java b/qi4j/extensions/entitystore-swift/src/main/java/org/qi4j/entitystore/swift/MalformedIdentityDirectoryException.java
new file mode 100644
index 0000000..613e35d
--- /dev/null
+++ b/qi4j/extensions/entitystore-swift/src/main/java/org/qi4j/entitystore/swift/MalformedIdentityDirectoryException.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2008 Niclas Hedhman.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ * implied.
+ *
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.qi4j.entitystore.swift;
+
+import java.io.File;
+
+public class MalformedIdentityDirectoryException extends Exception
+{
+ private File idDir;
+
+ public MalformedIdentityDirectoryException( File idDir )
+ {
+ this.idDir = idDir;
+ }
+
+ public File getDirectory()
+ {
+ return idDir;
+ }
+}
http://git-wip-us.apache.org/repos/asf/zest-sandbox/blob/d4dd9c17/qi4j/extensions/entitystore-swift/src/main/java/org/qi4j/entitystore/swift/RecordManager.java
----------------------------------------------------------------------
diff --git a/qi4j/extensions/entitystore-swift/src/main/java/org/qi4j/entitystore/swift/RecordManager.java b/qi4j/extensions/entitystore-swift/src/main/java/org/qi4j/entitystore/swift/RecordManager.java
new file mode 100644
index 0000000..9bd7efa
--- /dev/null
+++ b/qi4j/extensions/entitystore-swift/src/main/java/org/qi4j/entitystore/swift/RecordManager.java
@@ -0,0 +1,197 @@
+/*
+ * Copyright 2008 Niclas Hedhman.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ * implied.
+ *
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.qi4j.entitystore.swift;
+
+import org.qi4j.api.entity.EntityReference;
+import org.qi4j.io.Input;
+import org.qi4j.spi.entitystore.EntityStoreException;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.RandomAccessFile;
+import java.io.Reader;
+import java.util.ArrayList;
+
+public class RecordManager
+ implements UndoManager
+{
+ private static final byte UNDO_DELETE = 2;
+ private static final byte UNDO_MODIFY = 3;
+ private static final byte UNDO_NEW_IDENTITY = 4;
+ private static final byte UNDO_DROP_IDENTITY = 5;
+ private static final byte UNDO_EXTEND = 6;
+
+ private DataStore dataStore;
+ private RandomAccessFile undoJournal;
+ private ArrayList<UndoCommand> commands;
+
+ public RecordManager( File dataDir, boolean recover )
+ throws IOException
+ {
+ File undoFile = new File( dataDir, "undo.data" );
+ dataStore = new DataStore( dataDir, this );
+ commands = new ArrayList<UndoCommand>();
+ if( undoFile.exists() )
+ {
+ undoJournal = new RandomAccessFile( undoFile, "rw" );
+ if( recover && undoJournal.length() > 0 )
+ {
+ recover();
+ }
+ }
+ else
+ {
+ undoFile.createNewFile();
+ undoJournal = new RandomAccessFile( undoFile, "rw" );
+ }
+
+ }
+
+ public void putData( DataBlock data )
+ throws IOException
+ {
+ dataStore.putData( data );
+ }
+
+ public void deleteData( EntityReference reference )
+ throws IOException
+ {
+ dataStore.delete( reference );
+ }
+
+ public DataBlock readData( EntityReference reference )
+ throws IOException
+ {
+ return dataStore.readData( reference );
+ }
+
+
+ public void commit()
+ throws IOException
+ {
+ dataStore.flush();
+ commands.clear();
+ undoJournal.setLength( 0 );
+ }
+
+ public void discard()
+ throws IOException
+ {
+ for( UndoCommand command : commands )
+ {
+ command.undo( dataStore.dataFile(), dataStore.identityFile() );
+ }
+ commands.clear();
+ undoJournal.setLength( 0 );
+ }
+
+ public void close()
+ throws IOException
+ {
+ dataStore.close();
+ }
+
+ public void saveUndoCommand( UndoCommand command )
+ {
+ commands.add( command );
+ try
+ {
+ if( command instanceof UndoDeleteCommand )
+ {
+ undoJournal.write( UNDO_DELETE );
+ command.save( undoJournal );
+ }
+ else if( command instanceof UndoModifyCommand )
+ {
+ undoJournal.write( UNDO_MODIFY );
+ command.save( undoJournal );
+ }
+ else if( command instanceof UndoDropIdentityCommand )
+ {
+ undoJournal.write( UNDO_DROP_IDENTITY );
+ command.save( undoJournal );
+ }
+ else if( command instanceof UndoNewIdentityCommand )
+ {
+ undoJournal.write( UNDO_NEW_IDENTITY );
+ command.save( undoJournal );
+ }
+ else if( command instanceof UndoExtendCommand )
+ {
+ undoJournal.write( UNDO_EXTEND );
+ command.save( undoJournal );
+ }
+ else
+ {
+ throw new InternalError();
+ }
+ }
+ catch( IOException e )
+ {
+ throw new EntityStoreException( "Undo storage medium is malfunctioning." );
+ }
+ }
+
+ private void recover()
+ {
+ try
+ {
+ undoJournal.seek( 0 );
+ while( undoJournal.getFilePointer() < undoJournal.length() )
+ {
+ byte type = undoJournal.readByte();
+ UndoCommand command;
+ if( type == UNDO_MODIFY )
+ {
+ command = UndoModifyCommand.load( undoJournal );
+ }
+ else if( type == UNDO_DELETE )
+ {
+ command = UndoDeleteCommand.load( undoJournal );
+ }
+ else if( type == UNDO_DROP_IDENTITY )
+ {
+ command = UndoDropIdentityCommand.load( undoJournal );
+ }
+ else if( type == UNDO_EXTEND )
+ {
+ command = UndoExtendCommand.load( undoJournal );
+ }
+ else if( type == UNDO_NEW_IDENTITY )
+ {
+ command = UndoNewIdentityCommand.load( undoJournal );
+ }
+ else
+ {
+ throw new InternalError();
+ }
+ commands.add( command );
+ }
+ discard();
+ }
+ catch( IOException e )
+ {
+ throw new EntityStoreException( "Unable to recover from previous crash." );
+ }
+ }
+
+ public Input<Reader, IOException> data()
+ {
+ return dataStore.data();
+ }
+}
http://git-wip-us.apache.org/repos/asf/zest-sandbox/blob/d4dd9c17/qi4j/extensions/entitystore-swift/src/main/java/org/qi4j/entitystore/swift/StoreIterator.java
----------------------------------------------------------------------
diff --git a/qi4j/extensions/entitystore-swift/src/main/java/org/qi4j/entitystore/swift/StoreIterator.java b/qi4j/extensions/entitystore-swift/src/main/java/org/qi4j/entitystore/swift/StoreIterator.java
new file mode 100644
index 0000000..69d1fd6
--- /dev/null
+++ b/qi4j/extensions/entitystore-swift/src/main/java/org/qi4j/entitystore/swift/StoreIterator.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright 2008 Niclas Hedhman.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ * implied.
+ *
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.qi4j.entitystore.swift;
+
+import org.qi4j.api.entity.EntityReference;
+
+import java.io.IOException;
+import java.io.RandomAccessFile;
+import java.util.Iterator;
+
+class StoreIterator
+ implements Iterator<EntityReference>
+{
+ private boolean isAvailable;
+ private RandomAccessFile store;
+ private EntityReference identity;
+ private long position;
+ private int identityMaxLength;
+
+ StoreIterator( RandomAccessFile store, int identityMaxLength )
+ {
+ this.store = store;
+ this.position = DataStore.DATA_AREA_OFFSET;
+ this.identityMaxLength = identityMaxLength;
+ getNext();
+ }
+
+ public boolean hasNext()
+ {
+ return isAvailable;
+ }
+
+ public EntityReference next()
+ {
+ EntityReference result = identity;
+ getNext();
+ return result;
+ }
+
+ private void getNext()
+ {
+ try
+ {
+ while( store.getFilePointer() < store.length() )
+ {
+ store.seek( position );
+ int blockSize = store.readInt();
+ if( blockSize == 0 )
+ {
+ // TODO This is a bug. Why does it occur??
+ isAvailable = false;
+ return;
+ }
+ position = position + blockSize; // position for next round...
+ byte usage = store.readByte();
+ if( usage == 1 || usage == 2 )
+ {
+ store.skipBytes( 12 );
+ identity = readIdentity();
+ isAvailable = true;
+ return;
+ }
+ }
+ isAvailable = false;
+ }
+ catch( IOException e )
+ {
+ identity = null;
+ isAvailable = false;
+ }
+ }
+
+ public void remove()
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ private EntityReference readIdentity()
+ throws IOException
+ {
+ int idSize = store.readByte();
+ if( idSize < 0 )
+ {
+ idSize = idSize + 256; // Fix 2's-complement negative values of bytes into unsigned 8 bit.
+ }
+ byte[] idData = new byte[idSize];
+ store.read( idData );
+ store.skipBytes( identityMaxLength - idSize );
+ return new EntityReference( new String( idData ) );
+ }
+}
http://git-wip-us.apache.org/repos/asf/zest-sandbox/blob/d4dd9c17/qi4j/extensions/entitystore-swift/src/main/java/org/qi4j/entitystore/swift/SwiftConfiguration.java
----------------------------------------------------------------------
diff --git a/qi4j/extensions/entitystore-swift/src/main/java/org/qi4j/entitystore/swift/SwiftConfiguration.java b/qi4j/extensions/entitystore-swift/src/main/java/org/qi4j/entitystore/swift/SwiftConfiguration.java
new file mode 100644
index 0000000..b36db15
--- /dev/null
+++ b/qi4j/extensions/entitystore-swift/src/main/java/org/qi4j/entitystore/swift/SwiftConfiguration.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2008 Niclas Hedhman.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ * implied.
+ *
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.qi4j.entitystore.swift;
+
+import org.qi4j.api.common.Optional;
+import org.qi4j.api.common.UseDefaults;
+import org.qi4j.api.configuration.ConfigurationComposite;
+import org.qi4j.api.property.Property;
+
+public interface SwiftConfiguration extends ConfigurationComposite
+{
+ Property<String> storageDirectory();
+
+ @Optional @UseDefaults Property<Boolean> turboMode();
+
+ @Optional @UseDefaults Property<Boolean> recover();
+}
http://git-wip-us.apache.org/repos/asf/zest-sandbox/blob/d4dd9c17/qi4j/extensions/entitystore-swift/src/main/java/org/qi4j/entitystore/swift/SwiftEntityStoreAssembler.java
----------------------------------------------------------------------
diff --git a/qi4j/extensions/entitystore-swift/src/main/java/org/qi4j/entitystore/swift/SwiftEntityStoreAssembler.java b/qi4j/extensions/entitystore-swift/src/main/java/org/qi4j/entitystore/swift/SwiftEntityStoreAssembler.java
new file mode 100644
index 0000000..55c217b
--- /dev/null
+++ b/qi4j/extensions/entitystore-swift/src/main/java/org/qi4j/entitystore/swift/SwiftEntityStoreAssembler.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2008 Niclas Hedhman.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ * implied.
+ *
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.qi4j.entitystore.swift;
+
+import org.qi4j.api.common.Visibility;
+import org.qi4j.bootstrap.Assembler;
+import org.qi4j.bootstrap.AssemblyException;
+import org.qi4j.bootstrap.ModuleAssembly;
+import org.qi4j.spi.uuid.UuidIdentityGeneratorService;
+
+public class SwiftEntityStoreAssembler
+ implements Assembler
+{
+ private String configurationModuleName;
+
+ public SwiftEntityStoreAssembler( String configurationModule )
+ {
+ this.configurationModuleName = configurationModule;
+ }
+
+ public void assemble( ModuleAssembly module ) throws AssemblyException
+ {
+ module.services( SwiftEntityStoreService.class, UuidIdentityGeneratorService.class );
+ ModuleAssembly config = module.layer().module( configurationModuleName );
+ config.entities( SwiftConfiguration.class ).visibleIn( Visibility.layer );
+ }
+}
http://git-wip-us.apache.org/repos/asf/zest-sandbox/blob/d4dd9c17/qi4j/extensions/entitystore-swift/src/main/java/org/qi4j/entitystore/swift/SwiftEntityStoreMixin.java
----------------------------------------------------------------------
diff --git a/qi4j/extensions/entitystore-swift/src/main/java/org/qi4j/entitystore/swift/SwiftEntityStoreMixin.java b/qi4j/extensions/entitystore-swift/src/main/java/org/qi4j/entitystore/swift/SwiftEntityStoreMixin.java
new file mode 100644
index 0000000..89cf3e5
--- /dev/null
+++ b/qi4j/extensions/entitystore-swift/src/main/java/org/qi4j/entitystore/swift/SwiftEntityStoreMixin.java
@@ -0,0 +1,162 @@
+/*
+ * Copyright 2008 Niclas Hedhman.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ * implied.
+ *
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.qi4j.entitystore.swift;
+
+import org.qi4j.api.configuration.Configuration;
+import org.qi4j.api.entity.EntityReference;
+import org.qi4j.api.injection.scope.This;
+import org.qi4j.api.injection.scope.Uses;
+import org.qi4j.api.service.ServiceActivation;
+import org.qi4j.api.service.ServiceDescriptor;
+import org.qi4j.io.Input;
+import org.qi4j.spi.entitystore.EntityNotFoundException;
+import org.qi4j.spi.entitystore.EntityStoreException;
+import org.qi4j.spi.entitystore.helpers.MapEntityStore;
+
+import java.io.*;
+import java.util.concurrent.locks.ReadWriteLock;
+import org.qi4j.api.entity.EntityDescriptor;
+
+public class SwiftEntityStoreMixin
+ implements ServiceActivation, MapEntityStore
+{
+ private @This ReadWriteLock lock;
+ @Uses private ServiceDescriptor descriptor;
+ @This private Configuration<SwiftConfiguration> configuration;
+ private RecordManager recordManager;
+
+ public void activateService()
+ throws Exception
+ {
+ SwiftConfiguration conf = configuration.get();
+ String storage = conf.storageDirectory().get();
+ File storageDir;
+ storageDir = new File( storage );
+ Boolean recover = conf.recover().get();
+ if( recover == null )
+ {
+ recover = Boolean.TRUE;
+ }
+ recordManager = new RecordManager( storageDir, recover );
+ }
+
+ public void passivateService()
+ throws Exception
+ {
+ recordManager.close();
+ }
+
+ public Reader get( EntityReference entityReference )
+ throws EntityStoreException
+ {
+ try
+ {
+ DataBlock dataBlock = recordManager.readData( entityReference );
+ if( dataBlock == null )
+ {
+ throw new EntityNotFoundException( entityReference );
+ }
+ StringReader reader = new StringReader( new String( dataBlock.data, "UTF-8" ) );
+ return reader;
+ }
+ catch( UnsupportedEncodingException e )
+ {
+ // Can not happen.
+ throw new InternalError();
+ }
+ catch( IOException e )
+ {
+ throw new EntityStoreException( "Unable to read '" + entityReference + "' from the store.", e );
+ }
+ }
+
+ public Input<Reader, IOException> entityStates()
+ {
+ return recordManager.data();
+ }
+
+ public void applyChanges( MapChanges changes )
+ throws IOException
+ {
+ try
+ {
+ changes.visitMap( new MapChanger()
+ {
+ public Writer newEntity( final EntityReference ref, EntityDescriptor entityType ) throws IOException
+ {
+ return new StringWriter( 1000 )
+ {
+ @Override public void close() throws IOException
+ {
+ super.close();
+
+ byte[] stateArray = toString().getBytes( "UTF-8" );
+ DataBlock block = new DataBlock( ref, stateArray, 0, 0 );
+ recordManager.putData( block );
+ }
+ };
+ }
+
+ public Writer updateEntity( final EntityReference ref, EntityDescriptor entityType ) throws IOException
+ {
+ return new StringWriter( 1000 )
+ {
+ @Override public void close() throws IOException
+ {
+ super.close();
+ byte[] stateArray = toString().getBytes( "UTF-8" );
+ DataBlock block = new DataBlock( ref, stateArray, 0, 0 );
+ recordManager.putData( block );
+ }
+ };
+ }
+
+ public void removeEntity( EntityReference ref, EntityDescriptor entityType ) throws EntityNotFoundException
+ {
+ try
+ {
+ recordManager.deleteData( ref );
+ }
+ catch( IOException e )
+ {
+ throw new EntityStoreException( e );
+ }
+ }
+ } );
+ recordManager.commit();
+ }
+ catch( Exception e )
+ {
+ recordManager.discard();
+ if( e instanceof IOException )
+ {
+ throw (IOException) e;
+ }
+ else if( e instanceof EntityStoreException )
+ {
+ throw (EntityStoreException) e;
+ }
+ else
+ {
+ IOException exception = new IOException();
+ exception.initCause( e );
+ throw exception;
+ }
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/zest-sandbox/blob/d4dd9c17/qi4j/extensions/entitystore-swift/src/main/java/org/qi4j/entitystore/swift/SwiftEntityStoreService.java
----------------------------------------------------------------------
diff --git a/qi4j/extensions/entitystore-swift/src/main/java/org/qi4j/entitystore/swift/SwiftEntityStoreService.java b/qi4j/extensions/entitystore-swift/src/main/java/org/qi4j/entitystore/swift/SwiftEntityStoreService.java
new file mode 100644
index 0000000..3386862
--- /dev/null
+++ b/qi4j/extensions/entitystore-swift/src/main/java/org/qi4j/entitystore/swift/SwiftEntityStoreService.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2008 Niclas Hedhman.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ * implied.
+ *
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.qi4j.entitystore.swift;
+
+import org.qi4j.api.concern.Concerns;
+import org.qi4j.api.mixin.Mixins;
+import org.qi4j.api.service.ServiceActivation;
+import org.qi4j.library.locking.LockingAbstractComposite;
+import org.qi4j.spi.entitystore.ConcurrentModificationCheckConcern;
+import org.qi4j.spi.entitystore.EntityStateVersions;
+import org.qi4j.spi.entitystore.EntityStore;
+import org.qi4j.spi.entitystore.StateChangeNotificationConcern;
+import org.qi4j.spi.entitystore.helpers.MapEntityStoreMixin;
+
+@Concerns( { StateChangeNotificationConcern.class, ConcurrentModificationCheckConcern.class } )
+@Mixins( { MapEntityStoreMixin.class, SwiftEntityStoreMixin.class } )
+public interface SwiftEntityStoreService
+ extends ServiceActivation, EntityStore, EntityStateVersions, LockingAbstractComposite
+{
+}
http://git-wip-us.apache.org/repos/asf/zest-sandbox/blob/d4dd9c17/qi4j/extensions/entitystore-swift/src/main/java/org/qi4j/entitystore/swift/UndoCommand.java
----------------------------------------------------------------------
diff --git a/qi4j/extensions/entitystore-swift/src/main/java/org/qi4j/entitystore/swift/UndoCommand.java b/qi4j/extensions/entitystore-swift/src/main/java/org/qi4j/entitystore/swift/UndoCommand.java
new file mode 100644
index 0000000..8570716
--- /dev/null
+++ b/qi4j/extensions/entitystore-swift/src/main/java/org/qi4j/entitystore/swift/UndoCommand.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2008 Niclas Hedhman.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ * implied.
+ *
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.qi4j.entitystore.swift;
+
+import java.io.IOException;
+import java.io.RandomAccessFile;
+
+public interface UndoCommand
+{
+ void undo( RandomAccessFile dataFile, IdentityFile idFile ) throws IOException;
+
+ void save( RandomAccessFile undoJournal ) throws IOException;
+}
http://git-wip-us.apache.org/repos/asf/zest-sandbox/blob/d4dd9c17/qi4j/extensions/entitystore-swift/src/main/java/org/qi4j/entitystore/swift/UndoDeleteCommand.java
----------------------------------------------------------------------
diff --git a/qi4j/extensions/entitystore-swift/src/main/java/org/qi4j/entitystore/swift/UndoDeleteCommand.java b/qi4j/extensions/entitystore-swift/src/main/java/org/qi4j/entitystore/swift/UndoDeleteCommand.java
new file mode 100644
index 0000000..32eff50
--- /dev/null
+++ b/qi4j/extensions/entitystore-swift/src/main/java/org/qi4j/entitystore/swift/UndoDeleteCommand.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright 2008 Niclas Hedhman.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ * implied.
+ *
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.qi4j.entitystore.swift;
+
+import java.io.IOException;
+import java.io.RandomAccessFile;
+
+/**
+ * Record has been deleted and we want to restore it.
+ *
+ * Block Structure
+ * [blockSize] 4 bytes
+ * [usage] 1 byte (0=Unused, 1=prime, 2=mirror, 3=primeChanged, 4=mirrorChanged)
+ * [instanceVersion] 8 bytes
+ * [schemaVersion] 4 bytes
+ * [identitySize] 1 byte
+ * [identity] IDENTITY_MAX_LENGTH bytes
+ * [mirrorPointer] 8 bytes
+ * [primeDataLength] 4 bytes
+ * [primeData] n bytes
+ * [mirrorDataLength] 4 bytes
+ * [mirrorData] n bytes
+ */
+public class UndoDeleteCommand
+ implements UndoCommand
+{
+ private long position;
+ private byte usage;
+
+ public UndoDeleteCommand( long position, byte usage )
+ {
+ this.position = position;
+ this.usage = usage;
+ }
+
+ public void undo( RandomAccessFile dataFile, IdentityFile idFile )
+ throws IOException
+ {
+ dataFile.seek( position );
+ dataFile.skipBytes( 4 );
+ dataFile.writeByte( usage );
+ }
+
+ public void save( RandomAccessFile undoJournal ) throws IOException
+ {
+ undoJournal.writeLong( position );
+ undoJournal.writeByte( usage );
+ }
+
+ static UndoDeleteCommand load( RandomAccessFile undoJournal )
+ throws IOException
+ {
+ long position = undoJournal.readLong();
+ byte usage = undoJournal.readByte();
+ return new UndoDeleteCommand( position, usage );
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/zest-sandbox/blob/d4dd9c17/qi4j/extensions/entitystore-swift/src/main/java/org/qi4j/entitystore/swift/UndoDropIdentityCommand.java
----------------------------------------------------------------------
diff --git a/qi4j/extensions/entitystore-swift/src/main/java/org/qi4j/entitystore/swift/UndoDropIdentityCommand.java b/qi4j/extensions/entitystore-swift/src/main/java/org/qi4j/entitystore/swift/UndoDropIdentityCommand.java
new file mode 100644
index 0000000..f0b6f43
--- /dev/null
+++ b/qi4j/extensions/entitystore-swift/src/main/java/org/qi4j/entitystore/swift/UndoDropIdentityCommand.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2008 Niclas Hedhman.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ * implied.
+ *
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.qi4j.entitystore.swift;
+
+
+import java.io.IOException;
+import java.io.RandomAccessFile;
+import org.qi4j.api.entity.EntityReference;
+
+public class UndoDropIdentityCommand
+ implements UndoCommand
+{
+ private EntityReference reference;
+ private long position;
+
+ public UndoDropIdentityCommand( EntityReference reference, long position )
+ {
+ this.reference = reference;
+ this.position = position;
+ }
+
+ public void undo( RandomAccessFile dataFile, IdentityFile idFile ) throws IOException
+ {
+ idFile.remember( reference, position );
+ }
+
+ public void save( RandomAccessFile undoJournal ) throws IOException
+ {
+ undoJournal.writeUTF( reference.identity() );
+ undoJournal.writeLong( position );
+ }
+
+ static UndoDropIdentityCommand load( RandomAccessFile undoJournal )
+ throws IOException
+ {
+ String idString = undoJournal.readUTF();
+ EntityReference ref = new EntityReference( idString );
+ long pos = undoJournal.readLong();
+ return new UndoDropIdentityCommand( ref, pos );
+ }
+}