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 ); + } +}
