polygene-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From paulmer...@apache.org
Subject [05/18] zest-sandbox git commit: Move Qi4j related projects in a `qi4j/` subfolder
Date Tue, 21 Apr 2015 11:50:17 GMT
http://git-wip-us.apache.org/repos/asf/zest-sandbox/blob/d4dd9c17/qi4j/extensions/entitystore-swift/src/main/java/org/qi4j/entitystore/swift/UndoExtendCommand.java
----------------------------------------------------------------------
diff --git a/qi4j/extensions/entitystore-swift/src/main/java/org/qi4j/entitystore/swift/UndoExtendCommand.java b/qi4j/extensions/entitystore-swift/src/main/java/org/qi4j/entitystore/swift/UndoExtendCommand.java
new file mode 100644
index 0000000..f4145f7
--- /dev/null
+++ b/qi4j/extensions/entitystore-swift/src/main/java/org/qi4j/entitystore/swift/UndoExtendCommand.java
@@ -0,0 +1,68 @@
+/*
+ * 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;
+
+/**
+ * The DataFile has been extended at the end.
+ *
+ * To undo this we simply set the Length of the file to the previously known length.
+ * 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 UndoExtendCommand
+    implements UndoCommand
+{
+    private long previousLength;
+
+    public UndoExtendCommand( long previousLength )
+    {
+        this.previousLength = previousLength;
+    }
+
+    public void undo( RandomAccessFile dataFile, IdentityFile idFile ) throws IOException
+    {
+        dataFile.setLength( previousLength );
+        dataFile.seek( dataFile.length() );
+        dataFile.writeInt( -1 );  // Put in the EOF
+    }
+
+    public void save( RandomAccessFile undoJournal ) throws IOException
+    {
+        undoJournal.writeLong( previousLength );
+    }
+
+    static UndoExtendCommand load( RandomAccessFile undoJournal )
+        throws IOException
+    {
+        long position = undoJournal.readLong();
+        return new UndoExtendCommand( position );
+    }
+}

http://git-wip-us.apache.org/repos/asf/zest-sandbox/blob/d4dd9c17/qi4j/extensions/entitystore-swift/src/main/java/org/qi4j/entitystore/swift/UndoManager.java
----------------------------------------------------------------------
diff --git a/qi4j/extensions/entitystore-swift/src/main/java/org/qi4j/entitystore/swift/UndoManager.java b/qi4j/extensions/entitystore-swift/src/main/java/org/qi4j/entitystore/swift/UndoManager.java
new file mode 100644
index 0000000..a9dc23e
--- /dev/null
+++ b/qi4j/extensions/entitystore-swift/src/main/java/org/qi4j/entitystore/swift/UndoManager.java
@@ -0,0 +1,23 @@
+/*
+ * 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;
+
+public interface UndoManager
+{
+    void saveUndoCommand( UndoCommand command );
+}

http://git-wip-us.apache.org/repos/asf/zest-sandbox/blob/d4dd9c17/qi4j/extensions/entitystore-swift/src/main/java/org/qi4j/entitystore/swift/UndoModifyCommand.java
----------------------------------------------------------------------
diff --git a/qi4j/extensions/entitystore-swift/src/main/java/org/qi4j/entitystore/swift/UndoModifyCommand.java b/qi4j/extensions/entitystore-swift/src/main/java/org/qi4j/entitystore/swift/UndoModifyCommand.java
new file mode 100644
index 0000000..2331725
--- /dev/null
+++ b/qi4j/extensions/entitystore-swift/src/main/java/org/qi4j/entitystore/swift/UndoModifyCommand.java
@@ -0,0 +1,81 @@
+/*
+ * 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 modified and we can 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 UndoModifyCommand
+    implements UndoCommand
+{
+    private long position;
+    private byte usage;
+    private long instanceVersion;
+    private int schemaVersion;
+
+    public UndoModifyCommand( long position, byte usage, long instanceVersion, int schemaVersion )
+    {
+        this.position = position;
+        this.usage = usage;
+        this.instanceVersion = instanceVersion;
+        this.schemaVersion = schemaVersion;
+    }
+
+    public void undo( RandomAccessFile dataFile, IdentityFile idFile ) throws IOException
+    {
+        dataFile.seek( position + 4 );
+        dataFile.writeByte( usage );
+        dataFile.writeLong( instanceVersion );
+        dataFile.writeInt( schemaVersion );
+    }
+
+    public void save( RandomAccessFile undoJournal )
+        throws IOException
+    {
+        undoJournal.writeLong( position );
+        undoJournal.writeByte( usage );
+        undoJournal.writeLong( instanceVersion );
+        undoJournal.writeInt( schemaVersion );
+    }
+
+    static UndoCommand load( RandomAccessFile undoJournal )
+        throws IOException
+    {
+        long position = undoJournal.readLong();
+        byte usage = undoJournal.readByte();
+        long instanceVersion = undoJournal.readLong();
+        int schemaVersion = undoJournal.readInt();
+        return new UndoModifyCommand( position, usage, instanceVersion, schemaVersion );
+    }
+}

http://git-wip-us.apache.org/repos/asf/zest-sandbox/blob/d4dd9c17/qi4j/extensions/entitystore-swift/src/main/java/org/qi4j/entitystore/swift/UndoNewIdentityCommand.java
----------------------------------------------------------------------
diff --git a/qi4j/extensions/entitystore-swift/src/main/java/org/qi4j/entitystore/swift/UndoNewIdentityCommand.java b/qi4j/extensions/entitystore-swift/src/main/java/org/qi4j/entitystore/swift/UndoNewIdentityCommand.java
new file mode 100644
index 0000000..9293513
--- /dev/null
+++ b/qi4j/extensions/entitystore-swift/src/main/java/org/qi4j/entitystore/swift/UndoNewIdentityCommand.java
@@ -0,0 +1,53 @@
+/*
+ * 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.io.IOException;
+import java.io.RandomAccessFile;
+
+public class UndoNewIdentityCommand
+    implements UndoCommand
+{
+    private EntityReference reference;
+
+    public UndoNewIdentityCommand( EntityReference reference )
+    {
+        this.reference = reference;
+    }
+
+    public void undo( RandomAccessFile dataFile, IdentityFile idFile ) throws IOException
+    {
+        idFile.drop( reference );
+    }
+
+    public void save( RandomAccessFile undoJournal ) throws IOException
+    {
+        undoJournal.writeUTF( reference.toString() );
+    }
+
+    static UndoNewIdentityCommand load( RandomAccessFile undoJournal )
+        throws IOException
+    {
+        String idString = undoJournal.readUTF();
+        EntityReference ref = new EntityReference( idString );
+        return new UndoNewIdentityCommand( ref );
+    }
+}

http://git-wip-us.apache.org/repos/asf/zest-sandbox/blob/d4dd9c17/qi4j/extensions/entitystore-swift/src/main/resources/org/qi4j/entitystore/quick/QuickEntityStoreService.properties
----------------------------------------------------------------------
diff --git a/qi4j/extensions/entitystore-swift/src/main/resources/org/qi4j/entitystore/quick/QuickEntityStoreService.properties b/qi4j/extensions/entitystore-swift/src/main/resources/org/qi4j/entitystore/quick/QuickEntityStoreService.properties
new file mode 100644
index 0000000..32422c2
--- /dev/null
+++ b/qi4j/extensions/entitystore-swift/src/main/resources/org/qi4j/entitystore/quick/QuickEntityStoreService.properties
@@ -0,0 +1,17 @@
+
+### Configuration for the Quick EntityStore.
+### The commented out properties are the default values if not specified.
+
+
+### The recovery system is needed to ensure a consistent database after a non-managed shutdown, such
+### power-failure, JVM crash and "kill -9"
+## If there is an Undo file on the file system, use it to undo the pending changes.
+# recover=true
+
+### Storage Directory is the place (relative to current working directory) where the database
+### will store its files. This directory needs to be dedicated to Quick EntityStore.
+# storageDirectory=qi4j/quickstore
+
+### TurboMode disables the use of regular Java serialization and uses custom Objcet streams
+### for performance reasons.
+# turboMode=false
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/zest-sandbox/blob/d4dd9c17/qi4j/extensions/entitystore-swift/src/test/java/org/qi4j/entitystore/swift/DataFileTest.java
----------------------------------------------------------------------
diff --git a/qi4j/extensions/entitystore-swift/src/test/java/org/qi4j/entitystore/swift/DataFileTest.java b/qi4j/extensions/entitystore-swift/src/test/java/org/qi4j/entitystore/swift/DataFileTest.java
new file mode 100644
index 0000000..783b416
--- /dev/null
+++ b/qi4j/extensions/entitystore-swift/src/test/java/org/qi4j/entitystore/swift/DataFileTest.java
@@ -0,0 +1,184 @@
+/*
+ * 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.junit.Assert;
+import org.junit.Test;
+import org.qi4j.api.entity.EntityReference;
+
+import java.io.File;
+
+public class DataFileTest
+{
+    @Test
+    public void whenPuttingDataThenExpectSameDataBack()
+        throws Exception
+    {
+        File dir = new File( "swift-store" );
+        RecordManager man = new RecordManager( dir, false );
+        try
+        {
+            EntityReference ref = createReference( "12345678" );
+            String data = "Hej hopp du glade man!!";
+            DataBlock originalData = new DataBlock( ref, data.getBytes(), 81273, 91283 );
+            man.putData( originalData );
+
+            DataBlock retrieveData = man.readData( ref );
+            Assert.assertEquals( "Incorrect Data retrieved.", originalData, retrieveData );
+        }
+        finally
+        {
+            FileUtils.delete( dir );
+        }
+    }
+
+    @Test
+    public void whenPutting100DataWithIndividualCommitsThenExpectSameDataBack()
+        throws Exception
+    {
+        File dir = new File( "swift-store" );
+        RecordManager man = new RecordManager( dir, false );
+        try
+        {
+            EntityReference[] ids = new EntityReference[100];
+            String[] values = new String[100];
+            int[] schemas = new int[100];
+            long[] versions = new long[100];
+            long t0 = System.currentTimeMillis();
+            for( int i = 0; i < 100; i++ )
+            {
+                ids[ i ] = createReference( "habba" + i );
+                values[ i ] = "Hej hopp du glade man!!" + Math.random();
+                DataBlock originalData = new DataBlock( ids[ i ], values[ i ].getBytes(), versions[ i ], schemas[ i ] );
+                man.putData( originalData );
+                man.commit();
+            }
+            long t1 = System.currentTimeMillis();
+            System.out.println( "Thruoughput: " + 100000 / ( t1 - t0 ) + " creations/sec" );
+            for( int i = 0; i < 100; i++ )
+            {
+                DataBlock data = man.readData( ids[ i ] );
+                Assert.assertEquals( "Incorrect Data retrieved.", ids[ i ], data.reference );
+                Assert.assertEquals( "Incorrect Data retrieved.", versions[ i ], data.instanceVersion );
+                Assert.assertEquals( "Incorrect Data retrieved.", values[ i ], new String( data.data ) );
+                Assert.assertEquals( "Incorrect Data retrieved.", schemas[ i ], data.schemaVersion );
+            }
+        }
+        finally
+        {
+            FileUtils.delete( dir );
+        }
+    }
+
+    @Test
+    public void whenPutting100DataWithSingleCommitThenExpectSameDataBack()
+        throws Exception
+    {
+        File dir = new File( "swift-store" );
+        RecordManager man = new RecordManager( dir, false );
+        try
+        {
+            EntityReference[] ids = new EntityReference[100];
+            String[] values = new String[100];
+            int[] schemas = new int[100];
+            long[] versions = new long[100];
+            long t0 = System.currentTimeMillis();
+            for( int i = 0; i < 100; i++ )
+            {
+                ids[ i ] = createReference( "habba" + i );
+                values[ i ] = "Hej hopp du glade man!!" + Math.random();
+                DataBlock originalData = new DataBlock( ids[ i ], values[ i ].getBytes(), versions[ i ], schemas[ i ] );
+                man.putData( originalData );
+            }
+            man.commit();
+            long t1 = System.currentTimeMillis();
+            System.out.println( "Thruoughput: " + 100000 / ( t1 - t0 ) + " creations/sec" );
+            for( int i = 0; i < 100; i++ )
+            {
+                DataBlock data = man.readData( ids[ i ] );
+                Assert.assertEquals( "Incorrect Data retrieved.", ids[ i ], data.reference );
+                Assert.assertEquals( "Incorrect Data retrieved.", versions[ i ], data.instanceVersion );
+                Assert.assertEquals( "Incorrect Data retrieved.", values[ i ], new String( data.data ) );
+                Assert.assertEquals( "Incorrect Data retrieved.", schemas[ i ], data.schemaVersion );
+            }
+        }
+        finally
+        {
+            FileUtils.delete( dir );
+        }
+    }
+
+
+    @Test
+    public void whenEntityReferenceIsAtMaxThenExpectSameDataBack()
+        throws Exception
+    {
+        File dir = new File( "swift-store" );
+        RecordManager man = new RecordManager( dir, false );
+        try
+        {
+            EntityReference ref = createReference( "012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678912" );
+            String data = "Hej hopp du glade man!!";
+            DataBlock originalData = new DataBlock( ref, data.getBytes(), 81273, 91283 );
+            man.putData( originalData );
+            man.commit();
+
+            DataBlock retrieveData = man.readData( ref );
+            Assert.assertEquals( "Incorrect Data retrieved.", originalData, retrieveData );
+        }
+        finally
+        {
+            FileUtils.delete( dir );
+        }
+
+    }
+
+    @Test
+    public void whenTooLongEntityReferenceThenExpectException()
+        throws Exception
+    {
+        File dir = new File( "swift-store" );
+        RecordManager man = new RecordManager( dir, false );
+        try
+        {
+            StringBuffer buf = new StringBuffer();
+            for( int i=0 ; i < 129 ; i++ )
+                buf.append( (i % 10) );
+            EntityReference ref = createReference( buf.toString() );
+            
+            String data = "Hej hopp du glade man!!";
+            DataBlock originalData = new DataBlock( ref, data.getBytes(), 81273, 91283 );
+            man.putData( originalData );
+            Assert.fail( "Exceeeding max length didn't cause Exception." );
+        }
+        catch( IdentityTooLongException e )
+        {
+            // Expected.
+        }
+        finally
+        {
+            FileUtils.delete( dir );
+        }
+
+    }
+
+    private EntityReference createReference( String identity )
+    {
+        return new EntityReference( identity );
+    }
+}

http://git-wip-us.apache.org/repos/asf/zest-sandbox/blob/d4dd9c17/qi4j/extensions/entitystore-swift/src/test/java/org/qi4j/entitystore/swift/IdentityFileTest.java
----------------------------------------------------------------------
diff --git a/qi4j/extensions/entitystore-swift/src/test/java/org/qi4j/entitystore/swift/IdentityFileTest.java b/qi4j/extensions/entitystore-swift/src/test/java/org/qi4j/entitystore/swift/IdentityFileTest.java
new file mode 100644
index 0000000..6af5d81
--- /dev/null
+++ b/qi4j/extensions/entitystore-swift/src/test/java/org/qi4j/entitystore/swift/IdentityFileTest.java
@@ -0,0 +1,196 @@
+/*
+ * 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.util.Random;
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Test;
+import org.qi4j.api.entity.EntityReference;
+
+@SuppressWarnings( { "ResultOfMethodCallIgnored" } )
+public class IdentityFileTest
+{
+    private File idFile;
+    private IdentityFile file;
+
+    @Test
+    public void whenCreatingNewIdentityFileThenSucceed()
+        throws Exception
+    {
+        idFile = new File( "swift-store" );
+        idFile.mkdirs();
+        file = IdentityFile.create( idFile, 64, 1000 );
+    }
+
+    @Test
+    public void whenCreatingAnEntryThenGetTheResultBack()
+        throws Exception
+    {
+        idFile = new File( "swift-store" );
+        idFile.mkdirs();
+        file = IdentityFile.create( idFile, 64, 1000 );
+        populateRandom( file );
+        long value = 9783249823L;
+        EntityReference identity = createIdentity( "SomeIdentity" );
+        file.remember( identity, value );
+        long recalled = file.find( identity );
+        Assert.assertEquals( "Wrong position retrieved for item.", value, recalled );
+    }
+
+    @Test
+    public void whenCreating50EntriesThenGetTheResultBack()
+        throws Exception
+    {
+        idFile = new File( "swift-store" );
+        idFile.mkdirs();
+        file = IdentityFile.create( idFile, 64, 1000 );
+        populateRandom( file );
+        long[] pos = new long[50];
+        for( int i = 0; i < 50; i++ )
+        {
+            pos[ i ] = (long) ( Math.random() * Long.MAX_VALUE );
+            EntityReference identity = createIdentity( "Identity-" + i );
+            file.remember( identity, pos[ i ] );
+        }
+
+        for( int i = 0; i < 50; i++ )
+        {
+            EntityReference identity = createIdentity( "Identity-" + i );
+            long recalled = file.find( identity );
+            Assert.assertEquals( "Wrong position retrieved for item " + i + ".", pos[ i ], recalled );
+        }
+    }
+
+
+    @Test
+    public void whenIdentityIsLongerThanAllowedExpectException()
+        throws Exception
+    {
+        idFile = new File( "swift-store" );
+        idFile.mkdirs();
+        file = IdentityFile.create( idFile, 24, 1000 );
+        try
+        {
+            EntityReference identity = createIdentity( "12345678901" );
+            file.remember( identity, 827349813743908274L );
+            Assert.fail( "Should not allow this long identity." );
+        }
+        catch( IdentityTooLongException e )
+        {
+            // expected
+        }
+    }
+
+    @Test
+    public void whenNotEnoughSpaceIsAvailableExpectThatCanStillStore()
+        throws Exception
+    {
+        idFile = new File( "swift-store" );
+        idFile.mkdirs();
+        file = IdentityFile.create( idFile, 64, 1 );
+        long[] pos = new long[150];
+        for( int i = 0; i < 150; i++ )
+        {
+            pos[ i ] = (long) ( Math.random() * Long.MAX_VALUE );
+            EntityReference identity = createIdentity( "Identity-" + i );
+            file.remember( identity, pos[ i ] );
+        }
+
+        for( int i = 0; i < 150; i++ )
+        {
+            EntityReference identity = createIdentity( "Identity-" + i );
+            long recalled = file.find( identity );
+            Assert.assertEquals( "Wrong position retrieved for item " + i + ".", pos[ i ], recalled );
+        }
+    }
+
+    @Test
+    public void whenDroppingAnIdentityExpectMinusOneWhenLookingUp()
+        throws Exception
+    {
+        idFile = new File( "swift-store" );
+        idFile.mkdirs();
+        file = IdentityFile.create( idFile, 64, 5 );
+        long[] pos = new long[150];
+        for( int i = 0; i < 150; i++ )
+        {
+            pos[ i ] = (long) ( Math.random() * Long.MAX_VALUE );
+            EntityReference identity = createIdentity( "Identity-" + i );
+            file.remember( identity, pos[ i ] );
+        }
+
+        for( int i = 0; i < 150; i++ )
+        {
+            EntityReference identity = createIdentity( "Identity-" + i );
+            file.drop( identity );
+        }
+
+        for( int i = 0; i < 150; i++ )
+        {
+            EntityReference identity = createIdentity( "Identity-" + i );
+            Assert.assertEquals( "Identity entry not gone.", -1, file.find( identity ) );
+        }
+    }
+
+    @After
+    public void cleanUp()
+    {
+        try
+        {
+            file.close();
+        }
+        catch( IOException e )
+        {
+            e.printStackTrace();
+        }
+        delete( idFile );
+    }
+
+    private void delete( File file )
+    {
+        if( file.isDirectory() )
+        {
+            for( File child : file.listFiles() )
+            {
+                delete( child );
+            }
+        }
+        file.delete();
+    }
+
+    private EntityReference createIdentity( String identity )
+    {
+        return new EntityReference( identity );
+    }
+
+    private void populateRandom( IdentityFile file )
+        throws IOException
+    {
+        Random rnd = new Random();
+        int max = (int) ( file.entries() * 0.5 );
+        for( int i = 0; i < max; i++ )
+        {
+            String id = String.valueOf( rnd.nextInt(16) );
+            EntityReference ref = new EntityReference( id );
+            file.remember(ref, i);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/zest-sandbox/blob/d4dd9c17/qi4j/extensions/entitystore-swift/src/test/java/org/qi4j/entitystore/swift/SwiftEntityStoreTest.java
----------------------------------------------------------------------
diff --git a/qi4j/extensions/entitystore-swift/src/test/java/org/qi4j/entitystore/swift/SwiftEntityStoreTest.java b/qi4j/extensions/entitystore-swift/src/test/java/org/qi4j/entitystore/swift/SwiftEntityStoreTest.java
new file mode 100644
index 0000000..e039475
--- /dev/null
+++ b/qi4j/extensions/entitystore-swift/src/test/java/org/qi4j/entitystore/swift/SwiftEntityStoreTest.java
@@ -0,0 +1,43 @@
+/*
+ * 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.api.value.ValueSerialization;
+import org.qi4j.bootstrap.AssemblyException;
+import org.qi4j.bootstrap.ModuleAssembly;
+import org.qi4j.test.entity.AbstractEntityStoreTest;
+import org.qi4j.spi.uuid.UuidIdentityGeneratorService;
+import org.qi4j.test.EntityTestAssembler;
+import org.qi4j.valueserialization.orgjson.OrgJsonValueSerializationService;
+
+public class SwiftEntityStoreTest extends AbstractEntityStoreTest
+{
+    public void assemble( ModuleAssembly module )
+        throws AssemblyException
+    {
+        super.assemble( module );
+        module.services( SwiftEntityStoreService.class, UuidIdentityGeneratorService.class );
+        module.services( OrgJsonValueSerializationService.class ).taggedWith( ValueSerialization.Formats.JSON );
+
+        ModuleAssembly config = module.layer().module( "config" );
+        config.entities( SwiftConfiguration.class ).visibleIn( Visibility.layer );
+        new EntityTestAssembler().assemble( config );
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/zest-sandbox/blob/d4dd9c17/qi4j/extensions/entitystore-swift/src/test/java/org/qi4j/entitystore/swift/SwiftStorePerformanceTest.java
----------------------------------------------------------------------
diff --git a/qi4j/extensions/entitystore-swift/src/test/java/org/qi4j/entitystore/swift/SwiftStorePerformanceTest.java b/qi4j/extensions/entitystore-swift/src/test/java/org/qi4j/entitystore/swift/SwiftStorePerformanceTest.java
new file mode 100644
index 0000000..affae56
--- /dev/null
+++ b/qi4j/extensions/entitystore-swift/src/test/java/org/qi4j/entitystore/swift/SwiftStorePerformanceTest.java
@@ -0,0 +1,37 @@
+/*
+ * 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 org.qi4j.test.entity.performance.AbstractEntityStorePerformanceTest;
+import org.junit.Ignore;
+import org.qi4j.bootstrap.Assembler;
+
+@Ignore( "Needs org.qi4j.test.performance dependency" )
+public class SwiftStorePerformanceTest // extends AbstractEntityStorePerformanceTest
+{
+    public SwiftStorePerformanceTest()
+    {
+        // super( "SwiftEntityStore", createAssembler() );
+    }
+
+    private static Assembler createAssembler()
+    {
+        return new SwiftEntityStoreAssembler( "config" );
+    }
+}

http://git-wip-us.apache.org/repos/asf/zest-sandbox/blob/d4dd9c17/qi4j/extensions/entitystore-swift/src/test/resources/org/qi4j/entitystore/swift/SwiftEntityStoreService.properties
----------------------------------------------------------------------
diff --git a/qi4j/extensions/entitystore-swift/src/test/resources/org/qi4j/entitystore/swift/SwiftEntityStoreService.properties b/qi4j/extensions/entitystore-swift/src/test/resources/org/qi4j/entitystore/swift/SwiftEntityStoreService.properties
new file mode 100644
index 0000000..56cc119
--- /dev/null
+++ b/qi4j/extensions/entitystore-swift/src/test/resources/org/qi4j/entitystore/swift/SwiftEntityStoreService.properties
@@ -0,0 +1 @@
+storageDirectory=target/swiftdb

http://git-wip-us.apache.org/repos/asf/zest-sandbox/blob/d4dd9c17/qi4j/extensions/pom.xml
----------------------------------------------------------------------
diff --git a/qi4j/extensions/pom.xml b/qi4j/extensions/pom.xml
new file mode 100644
index 0000000..7093373
--- /dev/null
+++ b/qi4j/extensions/pom.xml
@@ -0,0 +1,25 @@
+<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/xsd/maven-4.0.0.xsd">
+  <parent>
+    <groupId>org.qi4j.sandbox</groupId>
+    <artifactId>qi4j-sandbox</artifactId>
+    <version>0-SNAPSHOT</version>
+  </parent>
+  <modelVersion>4.0.0</modelVersion>
+  <artifactId>qi4j-sandbox-extensions</artifactId>
+  <name>Qi4j Sandbox Extensions - Build POM</name>
+  <packaging>pom</packaging>
+
+  <modules>
+    <module>entitystore-jndi</module>
+    <module>entitystore-cassandra</module>
+    <module>entitystore-jgroups</module>
+    <module>entitystore-rmi</module>
+    <module>entitystore-s3</module>
+    <module>entitystore-swift</module>
+
+    <!-- Coherence needs a manual installation
+    <module>entitystore-coherence</module> -->
+
+  </modules>
+</project>

http://git-wip-us.apache.org/repos/asf/zest-sandbox/blob/d4dd9c17/qi4j/libraries/beans/dev-status.xml
----------------------------------------------------------------------
diff --git a/qi4j/libraries/beans/dev-status.xml b/qi4j/libraries/beans/dev-status.xml
new file mode 100644
index 0000000..0c93156
--- /dev/null
+++ b/qi4j/libraries/beans/dev-status.xml
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<module xmlns="http://www.qi4j.org/schemas/2008/dev-status/1">
+  <status>
+    <codebase>early</codebase>
+    <!--none,early,beta,stable,mature-->
+    <documentation>none</documentation>
+    <!-- none, brief, good, complete -->
+    <unittests>some</unittests>
+    <!-- none, some, good, complete -->
+  </status>
+  <licenses>
+    <license>ALv2</license>
+  </licenses>
+</module>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/zest-sandbox/blob/d4dd9c17/qi4j/libraries/beans/pom.xml
----------------------------------------------------------------------
diff --git a/qi4j/libraries/beans/pom.xml b/qi4j/libraries/beans/pom.xml
new file mode 100644
index 0000000..c8c36c7
--- /dev/null
+++ b/qi4j/libraries/beans/pom.xml
@@ -0,0 +1,41 @@
+<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">
+  <modelVersion>4.0.0</modelVersion>
+  <parent>
+    <groupId>org.qi4j.sandbox</groupId>
+    <artifactId>qi4j-sandbox-libraries</artifactId>
+    <version>0-SNAPSHOT</version>
+  </parent>
+  <groupId>org.qi4j.library</groupId>
+  <artifactId>org.qi4j.library.beans</artifactId>
+  <name>Qi4j Library - Beans</name>
+  <dependencies>
+    <dependency>
+      <groupId>org.qi4j.core</groupId>
+      <artifactId>org.qi4j.core.api</artifactId>
+    </dependency>
+    <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.runtime</artifactId>
+      <scope>runtime</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.qi4j.core</groupId>
+      <artifactId>org.qi4j.core.testsupport</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>junit</groupId>
+      <artifactId>junit</artifactId>
+      <scope>test</scope>
+    </dependency>
+  </dependencies>
+</project>

http://git-wip-us.apache.org/repos/asf/zest-sandbox/blob/d4dd9c17/qi4j/libraries/beans/src/main/java/org/qi4j/library/beans/properties/Getters.java
----------------------------------------------------------------------
diff --git a/qi4j/libraries/beans/src/main/java/org/qi4j/library/beans/properties/Getters.java b/qi4j/libraries/beans/src/main/java/org/qi4j/library/beans/properties/Getters.java
new file mode 100644
index 0000000..14bca23
--- /dev/null
+++ b/qi4j/libraries/beans/src/main/java/org/qi4j/library/beans/properties/Getters.java
@@ -0,0 +1,21 @@
+package org.qi4j.library.beans.properties;
+
+import org.qi4j.api.common.AppliesToFilter;
+
+import java.lang.reflect.Method;
+
+/**
+ * Filter for getter methods. Method name must match "get*" or "is*" or "has*".
+ */
+public class Getters implements AppliesToFilter
+{
+    public static final MethodPrefixFilter GET = new MethodPrefixFilter( "get" );
+    public static final MethodPrefixFilter IS = new MethodPrefixFilter( "is" );
+    public static final MethodPrefixFilter HAS = new MethodPrefixFilter( "has" );
+    public static final AppliesToFilter GETTERS = new OrAppliesToFilter( GET, IS, HAS );
+
+    public boolean appliesTo( Method method, Class mixin, Class compositeType, Class modelClass )
+    {
+        return GETTERS.appliesTo( method, mixin, compositeType, modelClass );
+    }
+}

http://git-wip-us.apache.org/repos/asf/zest-sandbox/blob/d4dd9c17/qi4j/libraries/beans/src/main/java/org/qi4j/library/beans/properties/Iterables.java
----------------------------------------------------------------------
diff --git a/qi4j/libraries/beans/src/main/java/org/qi4j/library/beans/properties/Iterables.java b/qi4j/libraries/beans/src/main/java/org/qi4j/library/beans/properties/Iterables.java
new file mode 100644
index 0000000..fe82b96
--- /dev/null
+++ b/qi4j/libraries/beans/src/main/java/org/qi4j/library/beans/properties/Iterables.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2008 Wen Tao. All Rights Reserved.
+ *
+ * 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.library.beans.properties;
+
+import org.qi4j.api.common.AppliesToFilter;
+
+import java.beans.Introspector;
+import java.lang.reflect.Method;
+
+public class Iterables implements PropertyNameExtractor, AppliesToFilter
+{
+    public boolean appliesTo( Method method, Class<?> mixin, Class<?> compositeType, Class<?> fragmentClass )
+    {
+        return matches( method.getName() );
+    }
+
+    public boolean matches( String methodName )
+    {
+        return methodName.endsWith( "Iterator" ) || "iterator".equals( methodName );
+    }
+
+    public String extractPropertyName( String methodName )
+    {
+        if( !matches( methodName ) )
+        {
+            return null;
+        }
+        return Introspector.decapitalize( methodName.substring( 0, methodName.length() - 8 ) );
+    }
+}

http://git-wip-us.apache.org/repos/asf/zest-sandbox/blob/d4dd9c17/qi4j/libraries/beans/src/main/java/org/qi4j/library/beans/properties/MethodPrefixFilter.java
----------------------------------------------------------------------
diff --git a/qi4j/libraries/beans/src/main/java/org/qi4j/library/beans/properties/MethodPrefixFilter.java b/qi4j/libraries/beans/src/main/java/org/qi4j/library/beans/properties/MethodPrefixFilter.java
new file mode 100644
index 0000000..bcb7634
--- /dev/null
+++ b/qi4j/libraries/beans/src/main/java/org/qi4j/library/beans/properties/MethodPrefixFilter.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2008 Wen Tao. All Rights Reserved.
+ *
+ * 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.library.beans.properties;
+
+import org.qi4j.api.common.AppliesToFilter;
+
+import java.beans.Introspector;
+import java.lang.reflect.Method;
+
+public class MethodPrefixFilter implements PropertyNameExtractor, AppliesToFilter
+{
+    private String prefix;
+
+    public MethodPrefixFilter( String prefix )
+    {
+        this.prefix = prefix;
+    }
+
+    public boolean appliesTo( Method method, Class<?> mixin, Class<?> compositeType, Class<?> fragmentClass )
+    {
+        return matches( method.getName() );
+    }
+
+    public boolean matches( String methodName )
+    {
+        return methodName.startsWith( prefix );
+    }
+
+    public String extractPropertyName( String methodName )
+    {
+        if( !matches( methodName ) )
+        {
+            return null;
+        }
+        return Introspector.decapitalize( methodName.substring( prefix.length() ) );
+    }
+}

http://git-wip-us.apache.org/repos/asf/zest-sandbox/blob/d4dd9c17/qi4j/libraries/beans/src/main/java/org/qi4j/library/beans/properties/OrAppliesToFilter.java
----------------------------------------------------------------------
diff --git a/qi4j/libraries/beans/src/main/java/org/qi4j/library/beans/properties/OrAppliesToFilter.java b/qi4j/libraries/beans/src/main/java/org/qi4j/library/beans/properties/OrAppliesToFilter.java
new file mode 100644
index 0000000..87a4673
--- /dev/null
+++ b/qi4j/libraries/beans/src/main/java/org/qi4j/library/beans/properties/OrAppliesToFilter.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2008 Wen Tao. All Rights Reserved.
+ *
+ * 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.library.beans.properties;
+
+import org.qi4j.api.common.AppliesToFilter;
+
+import java.lang.reflect.Method;
+
+public final class OrAppliesToFilter
+    implements AppliesToFilter
+{
+    private final AppliesToFilter[] filters;
+
+    public OrAppliesToFilter( AppliesToFilter... filters )
+    {
+        this.filters = filters;
+    }
+
+    public final boolean appliesTo( Method method, Class<?> mixin, Class<?> compositeType, Class<?> fragmentClass )
+    {
+        for( AppliesToFilter filter : filters )
+        {
+            if( filter.appliesTo( method, mixin, compositeType, fragmentClass ) )
+            {
+                return true;
+            }
+        }
+        return false;
+    }
+}

http://git-wip-us.apache.org/repos/asf/zest-sandbox/blob/d4dd9c17/qi4j/libraries/beans/src/main/java/org/qi4j/library/beans/properties/Properties.java
----------------------------------------------------------------------
diff --git a/qi4j/libraries/beans/src/main/java/org/qi4j/library/beans/properties/Properties.java b/qi4j/libraries/beans/src/main/java/org/qi4j/library/beans/properties/Properties.java
new file mode 100644
index 0000000..920b349
--- /dev/null
+++ b/qi4j/libraries/beans/src/main/java/org/qi4j/library/beans/properties/Properties.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright 2008 Wen Tao. All Rights Reserved.
+ *
+ * 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.library.beans.properties;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+
+class Properties
+{
+    private Map<String, Object> properties = new HashMap<String, Object>();
+    private static final String PREFIX_LIST_NAME = "l:";
+    private static final String PREFIX_VALUE_NAME = "v:";
+
+    public Object get( String propertyName )
+    {
+        String name = encodeValueName( propertyName );
+        return properties.get( name );
+    }
+
+    public void set( String propertyName, Object value )
+    {
+        String name = encodeValueName( propertyName );
+        if( value == null )
+        {
+            properties.remove( name );
+        }
+        else
+        {
+            properties.put( name, value );
+        }
+    }
+
+    public void add( String propertyName, Object value )
+    {
+        String name = encodeListName( propertyName );
+        ArrayList list = (ArrayList) properties.get( name );
+        if( list == null )
+        {
+            list = new ArrayList();
+            properties.put( name, list );
+        }
+        list.add( value );
+    }
+
+    public void remove( String propertyName, Object value )
+    {
+        String name = encodeListName( propertyName );
+        ArrayList list = (ArrayList) properties.get( name );
+        if( list != null )
+        {
+            list.remove( value );
+            if( list.size() == 0 )
+            {
+                properties.remove( name );
+            }
+        }
+    }
+
+    public Iterator iterator( String propertyName )
+    {
+        String name = encodeListName( propertyName );
+        ArrayList list = (ArrayList) properties.get( name );
+        if( list != null )
+        {
+            return list.iterator();
+        }
+        return new Iterator()
+        {
+            public boolean hasNext()
+            {
+                return false;
+            }
+
+            public Object next()
+            {
+                return null;
+            }
+
+            public void remove()
+            {
+            }
+        };
+    }
+
+    private String encodeValueName( String propertyName )
+    {
+        return PREFIX_VALUE_NAME + propertyName;
+    }
+
+    private String encodeListName( String propertyName )
+    {
+        return PREFIX_LIST_NAME + propertyName;
+    }
+
+}
+

http://git-wip-us.apache.org/repos/asf/zest-sandbox/blob/d4dd9c17/qi4j/libraries/beans/src/main/java/org/qi4j/library/beans/properties/PropertiesMixin.java
----------------------------------------------------------------------
diff --git a/qi4j/libraries/beans/src/main/java/org/qi4j/library/beans/properties/PropertiesMixin.java b/qi4j/libraries/beans/src/main/java/org/qi4j/library/beans/properties/PropertiesMixin.java
new file mode 100644
index 0000000..4032870
--- /dev/null
+++ b/qi4j/libraries/beans/src/main/java/org/qi4j/library/beans/properties/PropertiesMixin.java
@@ -0,0 +1,142 @@
+/*
+ * Copyright 2007 Rickard Öberg. All Rights Reserved.
+ * Copyright 2007 Alin Dreghiciu. All Rights Reserved.
+ * Copyright 2007 Edward Yakop. All Rights Reserved.
+ *
+ * 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.library.beans.properties;
+
+import org.qi4j.api.common.AppliesTo;
+
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Method;
+
+/**
+ * Generic property mixin. Methods in interface
+ * can be of the following types:
+ * setFoo = set property named foo
+ * getFoo = get property named foo
+ * addFoo = add object to list named foo
+ * removeFoo = remove object from list named foo
+ * fooIterator - return an iterator over the list of Foos
+ */
+@AppliesTo( { Getters.class, Setters.class, Iterables.class } )
+public class PropertiesMixin implements InvocationHandler
+{
+    private static final PropertyHandler[] HANDLERS = new PropertyHandler[]
+        {
+            new AbstractPropertyHandler( Setters.SET )
+            {
+                protected Object handleProperty( Properties properties, String propertyName, Object[] args )
+                {
+                    properties.set( propertyName, args[ 0 ] );
+                    return null;
+                }
+            },
+            new GetPropertyHandler( Getters.GET ),
+            new GetPropertyHandler( Getters.IS ),
+            new GetPropertyHandler( Getters.HAS ),
+            new AbstractPropertyHandler( Setters.ADD )
+            {
+                public Object handleProperty( Properties properties, String propertyName, Object[] args )
+                {
+                    properties.add( propertyName, args[ 0 ] );
+                    return null;
+                }
+            },
+            new AbstractPropertyHandler( Setters.REMOVE )
+            {
+                protected Object handleProperty( Properties properties, String propertyName, Object[] args )
+                {
+                    properties.remove( propertyName, args[ 0 ] );
+                    return null;
+                }
+            },
+            new AbstractPropertyHandler( new Iterables() )
+            {
+                protected Object handleProperty( Properties properties, String propertyName, Object[] args )
+                {
+                    return properties.iterator( propertyName );
+                }
+            }
+        };
+
+    // Attributes ----------------------------------------------------
+    Properties properties;
+
+    /**
+     * Construct and empty properties mixins.
+     *
+     * @since 0.1.0
+     */
+    public PropertiesMixin()
+    {
+        properties = new Properties();
+    }
+
+    // InvocationHandler implementation ------------------------------
+    @SuppressWarnings( "unchecked" )
+    public Object invoke( Object proxy, Method method, Object[] args ) throws Throwable
+    {
+        String methodName = method.getName();
+        for( PropertyHandler handler : HANDLERS )
+        {
+            if( handler.shouldHandle( methodName ) )
+            {
+                return handler.handleInvocation( properties, methodName, args );
+            }
+        }
+        return null;
+    }
+
+    private interface PropertyHandler
+    {
+        boolean shouldHandle( String methodName );
+
+        Object handleInvocation( Properties properties, String methodName, Object[] args );
+    }
+
+    private static abstract class AbstractPropertyHandler implements PropertyHandler
+    {
+        private PropertyNameExtractor propertyNameExtractor;
+
+        public AbstractPropertyHandler( PropertyNameExtractor propertyNameExtractor )
+        {
+            this.propertyNameExtractor = propertyNameExtractor;
+        }
+
+        public Object handleInvocation( Properties properties, String methodName, Object[] args )
+        {
+            String propertyName = propertyNameExtractor.extractPropertyName( methodName );
+            return handleProperty( properties, propertyName, args );
+        }
+
+        protected abstract Object handleProperty( Properties properties, String propertyName, Object[] args );
+
+        public boolean shouldHandle( String methodName )
+        {
+            return propertyNameExtractor.extractPropertyName( methodName ) != null;
+        }
+    }
+
+    private static final class GetPropertyHandler extends AbstractPropertyHandler
+    {
+        public GetPropertyHandler( PropertyNameExtractor propertyNameExtractor )
+        {
+            super( propertyNameExtractor );
+        }
+
+        protected Object handleProperty( Properties properties, String propertyName, Object[] args )
+        {
+            return properties.get( propertyName );
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/zest-sandbox/blob/d4dd9c17/qi4j/libraries/beans/src/main/java/org/qi4j/library/beans/properties/PropertyNameExtractor.java
----------------------------------------------------------------------
diff --git a/qi4j/libraries/beans/src/main/java/org/qi4j/library/beans/properties/PropertyNameExtractor.java b/qi4j/libraries/beans/src/main/java/org/qi4j/library/beans/properties/PropertyNameExtractor.java
new file mode 100644
index 0000000..1ad2d53
--- /dev/null
+++ b/qi4j/libraries/beans/src/main/java/org/qi4j/library/beans/properties/PropertyNameExtractor.java
@@ -0,0 +1,18 @@
+/*
+ * Copyright 2008 Wen Tao. All Rights Reserved.
+ *
+ * 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.library.beans.properties;
+
+public interface PropertyNameExtractor
+{
+    String extractPropertyName( String methodName );
+}

http://git-wip-us.apache.org/repos/asf/zest-sandbox/blob/d4dd9c17/qi4j/libraries/beans/src/main/java/org/qi4j/library/beans/properties/Setters.java
----------------------------------------------------------------------
diff --git a/qi4j/libraries/beans/src/main/java/org/qi4j/library/beans/properties/Setters.java b/qi4j/libraries/beans/src/main/java/org/qi4j/library/beans/properties/Setters.java
new file mode 100644
index 0000000..19d1404
--- /dev/null
+++ b/qi4j/libraries/beans/src/main/java/org/qi4j/library/beans/properties/Setters.java
@@ -0,0 +1,21 @@
+package org.qi4j.library.beans.properties;
+
+import org.qi4j.api.common.AppliesToFilter;
+
+import java.lang.reflect.Method;
+
+/**
+ * Filter for setter methods. Method name must match "set*","add*" or "remove*".
+ */
+public class Setters implements AppliesToFilter
+{
+    public static final MethodPrefixFilter SET = new MethodPrefixFilter( "set" );
+    public static final MethodPrefixFilter ADD = new MethodPrefixFilter( "add" );
+    public static final MethodPrefixFilter REMOVE = new MethodPrefixFilter( "remove" );
+    public static final AppliesToFilter SETTERS = new OrAppliesToFilter( SET, ADD, REMOVE );
+
+    public boolean appliesTo( Method method, Class mixin, Class compositeType, Class modelClass )
+    {
+        return SETTERS.appliesTo( method, mixin, compositeType, modelClass );
+    }
+}

http://git-wip-us.apache.org/repos/asf/zest-sandbox/blob/d4dd9c17/qi4j/libraries/beans/src/main/java/org/qi4j/library/beans/support/DelegatingIterator.java
----------------------------------------------------------------------
diff --git a/qi4j/libraries/beans/src/main/java/org/qi4j/library/beans/support/DelegatingIterator.java b/qi4j/libraries/beans/src/main/java/org/qi4j/library/beans/support/DelegatingIterator.java
new file mode 100644
index 0000000..c8bb012
--- /dev/null
+++ b/qi4j/libraries/beans/src/main/java/org/qi4j/library/beans/support/DelegatingIterator.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2008 Niclas Hedhman. All rights Reserved.
+ *
+ * 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.library.beans.support;
+
+import org.qi4j.api.composite.TransientBuilderFactory;
+
+import java.util.Iterator;
+import org.qi4j.api.association.AssociationDescriptor;
+
+public class DelegatingIterator
+    implements Iterator
+{
+    private Iterator source;
+    private final AssociationDescriptor info;
+    private final TransientBuilderFactory cbf;
+
+    public DelegatingIterator( Iterator source, AssociationDescriptor info, TransientBuilderFactory cbf )
+    {
+        this.source = source;
+        this.info = info;
+        this.cbf = cbf;
+    }
+
+    public boolean hasNext()
+    {
+        return source.hasNext();
+    }
+
+    public Object next()
+    {
+        return Wrapper.wrap( source.next(), info, cbf );
+    }
+
+    public void remove()
+    {
+        source.remove();
+    }
+}

http://git-wip-us.apache.org/repos/asf/zest-sandbox/blob/d4dd9c17/qi4j/libraries/beans/src/main/java/org/qi4j/library/beans/support/DelegatingListIterator.java
----------------------------------------------------------------------
diff --git a/qi4j/libraries/beans/src/main/java/org/qi4j/library/beans/support/DelegatingListIterator.java b/qi4j/libraries/beans/src/main/java/org/qi4j/library/beans/support/DelegatingListIterator.java
new file mode 100644
index 0000000..4dc5fb5
--- /dev/null
+++ b/qi4j/libraries/beans/src/main/java/org/qi4j/library/beans/support/DelegatingListIterator.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright 2008 Niclas Hedhman. All rights Reserved.
+ *
+ * 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.library.beans.support;
+
+import org.qi4j.api.composite.TransientBuilderFactory;
+
+import java.util.ListIterator;
+import org.qi4j.api.association.AssociationDescriptor;
+
+public class DelegatingListIterator
+    implements ListIterator
+{
+    private ListIterator source;
+    private final AssociationDescriptor info;
+    private final TransientBuilderFactory cbf;
+
+    public DelegatingListIterator( ListIterator source, AssociationDescriptor info, TransientBuilderFactory cbf )
+    {
+        this.source = source;
+        this.info = info;
+        this.cbf = cbf;
+    }
+
+    public boolean hasNext()
+    {
+        return source.hasNext();
+    }
+
+    public Object next()
+    {
+        return Wrapper.wrap( source.next(), info, cbf );
+    }
+
+    public boolean hasPrevious()
+    {
+        return source.hasPrevious();
+    }
+
+    public Object previous()
+    {
+        return Wrapper.wrap( source.previous(), info, cbf );
+    }
+
+    public int nextIndex()
+    {
+        return source.nextIndex();
+    }
+
+    public int previousIndex()
+    {
+        return source.previousIndex();
+    }
+
+    public void remove()
+    {
+        source.remove();
+    }
+
+    public void set( Object o )
+    {
+        throw new UnsupportedOperationException( "Read Only." );
+    }
+
+    public void add( Object o )
+    {
+        throw new UnsupportedOperationException( "Read Only." );
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/zest-sandbox/blob/d4dd9c17/qi4j/libraries/beans/src/main/java/org/qi4j/library/beans/support/JavabeanAssociation.java
----------------------------------------------------------------------
diff --git a/qi4j/libraries/beans/src/main/java/org/qi4j/library/beans/support/JavabeanAssociation.java b/qi4j/libraries/beans/src/main/java/org/qi4j/library/beans/support/JavabeanAssociation.java
new file mode 100644
index 0000000..a30e495
--- /dev/null
+++ b/qi4j/libraries/beans/src/main/java/org/qi4j/library/beans/support/JavabeanAssociation.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright 2008 Niclas Hedhman. All rights Reserved.
+ *
+ * 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.library.beans.support;
+
+import org.qi4j.api.common.QualifiedName;
+import org.qi4j.api.association.Association;
+import org.qi4j.api.composite.TransientBuilder;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Type;
+import java.lang.reflect.UndeclaredThrowableException;
+import org.qi4j.api.association.AssociationDescriptor;
+
+public class JavabeanAssociation
+    implements Association
+{
+    private final JavabeanMixin javabeanMixin;
+    private final AssociationDescriptor descriptor;
+    public final Method pojoMethod;
+
+    public JavabeanAssociation( JavabeanMixin javabeanMixin, AssociationDescriptor descriptor, Method pojoMethod )
+    {
+        this.javabeanMixin = javabeanMixin;
+        this.descriptor = descriptor;
+        this.pojoMethod = pojoMethod;
+    }
+
+    public <T> T metaInfo( Class<T> infoType )
+    {
+        return descriptor.metaInfo( infoType );
+    }
+
+    public QualifiedName qualifiedName()
+    {
+        return descriptor.qualifiedName();
+    }
+
+    public Type type()
+    {
+        return descriptor.type();
+    }
+
+    public boolean isImmutable()
+    {
+        return descriptor.isImmutable();
+    }
+
+    public boolean isAggregated()
+    {
+        return descriptor.isAggregated();
+    }
+
+    public Object get()
+    {
+        try
+        {
+            Object resultObject = pojoMethod.invoke( javabeanMixin.pojo );
+            Class type = (Class) type();
+            if( type.isInterface() )
+            {
+                TransientBuilder<?> builder = javabeanMixin.cbf.newTransientBuilder( type );
+                builder.use( resultObject );
+                return builder.newInstance();
+            }
+            return resultObject;
+        }
+        catch( IllegalAccessException e )
+        {
+            throw new IllegalArgumentException( "POJO is not compatible with JavaBeans specification. Method must be public: " + pojoMethod );
+        }
+        catch( InvocationTargetException e )
+        {
+            throw new UndeclaredThrowableException( e.getTargetException() );
+        }
+    }
+
+    public void set( Object associated ) throws IllegalArgumentException
+    {
+        //TODO: Auto-generated, need attention.
+    }
+}

http://git-wip-us.apache.org/repos/asf/zest-sandbox/blob/d4dd9c17/qi4j/libraries/beans/src/main/java/org/qi4j/library/beans/support/JavabeanManyAssociation.java
----------------------------------------------------------------------
diff --git a/qi4j/libraries/beans/src/main/java/org/qi4j/library/beans/support/JavabeanManyAssociation.java b/qi4j/libraries/beans/src/main/java/org/qi4j/library/beans/support/JavabeanManyAssociation.java
new file mode 100644
index 0000000..c8a96b8
--- /dev/null
+++ b/qi4j/libraries/beans/src/main/java/org/qi4j/library/beans/support/JavabeanManyAssociation.java
@@ -0,0 +1,202 @@
+/*
+ * Copyright 2008 Niclas Hedhman. All rights Reserved.
+ *
+ * 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.library.beans.support;
+
+import org.qi4j.api.common.QualifiedName;
+import org.qi4j.api.association.AssociationDescriptor;
+import org.qi4j.api.association.ManyAssociation;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Type;
+import java.lang.reflect.UndeclaredThrowableException;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.Set;
+import java.util.List;
+
+public class JavabeanManyAssociation
+    implements ManyAssociation
+{
+    private final JavabeanMixin javabeanMixin;
+    private final AssociationDescriptor descriptor;
+    private final Method pojoMethod;
+
+    public JavabeanManyAssociation( JavabeanMixin javabeanMixin, AssociationDescriptor descriptor, Method pojoMethod )
+    {
+        this.javabeanMixin = javabeanMixin;
+        this.descriptor = descriptor;
+        this.pojoMethod = pojoMethod;
+    }
+
+    public <T> T metaInfo( Class<T> infoType )
+    {
+        return descriptor.metaInfo( infoType );
+    }
+
+    public QualifiedName qualifiedName()
+    {
+        return descriptor.qualifiedName();
+    }
+
+    public Type type()
+    {
+        return descriptor.type();
+    }
+
+    public boolean isImmutable()
+    {
+        return descriptor.isImmutable();
+    }
+
+    public boolean isAggregated()
+    {
+        return descriptor.isAggregated();
+    }
+
+    public int size()
+    {
+        return delegate().size();
+    }
+
+    public boolean isEmpty()
+    {
+        return delegate().isEmpty();
+    }
+
+    public int count()
+    {
+        return 0;  //To change body of implemented methods use File | Settings | File Templates.
+    }
+
+    public boolean contains( Object object )
+    {
+        return delegate().contains( Wrapper.unwrap( object ) );
+    }
+
+    public boolean add( int i, Object entity )
+    {
+        return false;  //To change body of implemented methods use File | Settings | File Templates.
+    }
+
+    public Iterator iterator()
+    {
+        return new DelegatingIterator( delegate().iterator(), descriptor, javabeanMixin.cbf );
+    }
+
+    public Object[] toArray()
+    {
+        Object[] objects = delegate().toArray();
+        Object[] wrapped = new Object[objects.length];
+        for( int i = 0; i < objects.length; i++ )
+        {
+            wrapped[ i ] = Wrapper.wrap( objects[ i ], descriptor, javabeanMixin.cbf );
+        }
+        return wrapped;
+    }
+
+    public boolean add( Object object )
+    {
+        throw new UnsupportedOperationException( "Read/only." );
+    }
+
+    public boolean remove( Object object )
+    {
+        throw new UnsupportedOperationException( "Read/only." );
+    }
+
+    public Object get( int i )
+    {
+        return null;  //To change body of implemented methods use File | Settings | File Templates.
+    }
+
+    public List toList()
+    {
+        return null;  //To change body of implemented methods use File | Settings | File Templates.
+    }
+
+    public Set toSet()
+    {
+        return null;  //To change body of implemented methods use File | Settings | File Templates.
+    }
+
+    public boolean addAll( Collection collection )
+    {
+        throw new UnsupportedOperationException( "Read/only." );
+    }
+
+    public void clear()
+    {
+        throw new UnsupportedOperationException( "Read/only." );
+    }
+
+    public boolean retainAll( Collection collection )
+    {
+        throw new UnsupportedOperationException( "Read/only." );
+    }
+
+    public boolean removeAll( Collection collection )
+    {
+        throw new UnsupportedOperationException( "Read/only." );
+    }
+
+    public boolean containsAll( Collection collection )
+    {
+        for( Object obj : collection )
+        {
+            if( !contains( Wrapper.unwrap( obj ) ) )
+            {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    public Object[] toArray( Object[] objects )
+    {
+        Object[] array = delegate().toArray( objects );
+        for( int i = 0; i < array.length; i++ )
+        {
+            array[ i ] = Wrapper.wrap( array[ i ], descriptor, javabeanMixin.cbf );
+        }
+        return array;
+    }
+
+    private Set delegate()
+    {
+        try
+        {
+            Object resultObject = pojoMethod.invoke( javabeanMixin.pojo );
+            return (Set) resultObject;
+        }
+        catch( IllegalAccessException e )
+        {
+            throw new IllegalArgumentException( "Javabean is not compatible with JavaBeans specification. Method must be public: " + pojoMethod );
+        }
+        catch( ClassCastException e )
+        {
+            throw new IllegalArgumentException( "Javabean and Qi4j models are not compatible. Expected a java.util.Set in return type of " + pojoMethod );
+        }
+        catch( InvocationTargetException e )
+        {
+            throw new UndeclaredThrowableException( e.getTargetException() );
+        }
+    }
+
+
+}

http://git-wip-us.apache.org/repos/asf/zest-sandbox/blob/d4dd9c17/qi4j/libraries/beans/src/main/java/org/qi4j/library/beans/support/JavabeanMixin.java
----------------------------------------------------------------------
diff --git a/qi4j/libraries/beans/src/main/java/org/qi4j/library/beans/support/JavabeanMixin.java b/qi4j/libraries/beans/src/main/java/org/qi4j/library/beans/support/JavabeanMixin.java
new file mode 100644
index 0000000..b6b6772
--- /dev/null
+++ b/qi4j/libraries/beans/src/main/java/org/qi4j/library/beans/support/JavabeanMixin.java
@@ -0,0 +1,139 @@
+/*
+ * Copyright 2008 Niclas Hedhman. All rights Reserved.
+ *
+ * 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.library.beans.support;
+
+import java.lang.reflect.AccessibleObject;
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Method;
+import java.util.HashMap;
+import org.qi4j.api.Qi4j;
+import org.qi4j.api.common.AppliesTo;
+import org.qi4j.api.common.AppliesToFilter;
+import org.qi4j.api.composite.Composite;
+import org.qi4j.api.composite.TransientBuilderFactory;
+import org.qi4j.api.association.Association;
+import org.qi4j.api.association.AssociationDescriptor;
+import org.qi4j.api.association.AssociationStateDescriptor;
+import org.qi4j.api.association.ManyAssociation;
+import org.qi4j.api.composite.CompositeDescriptor;
+import org.qi4j.api.composite.StateDescriptor;
+import org.qi4j.api.composite.StatefulCompositeDescriptor;
+import org.qi4j.api.injection.scope.Structure;
+import org.qi4j.api.injection.scope.This;
+import org.qi4j.api.injection.scope.Uses;
+import org.qi4j.api.property.Property;
+import org.qi4j.api.property.PropertyDescriptor;
+import org.qi4j.api.structure.Module;
+
+@AppliesTo( { JavabeanMixin.JavabeanSupportFilter.class } )
+public class JavabeanMixin
+    implements JavabeanSupport, InvocationHandler
+{
+    private HashMap<AccessibleObject, Object> handlers;
+
+    @Structure TransientBuilderFactory cbf;
+    Object pojo;
+
+    public JavabeanMixin( @Structure Module module, @This Composite thisComposite, @Uses Object pojo )
+    {
+        this.pojo = pojo;
+        this.handlers = new HashMap<AccessibleObject, Object>();
+        CompositeDescriptor thisDescriptor = Qi4j.FUNCTION_DESCRIPTOR_FOR.map( thisComposite );
+        if( thisDescriptor instanceof StatefulCompositeDescriptor )
+        {
+            StateDescriptor stateDescriptor = ( (StatefulCompositeDescriptor) thisDescriptor ).state();
+            for( PropertyDescriptor propDesc : stateDescriptor.properties() )
+            {
+                Method pojoMethod = findMethod( pojo, propDesc.qualifiedName().name() );
+                handlers.put( propDesc.accessor(), new JavabeanProperty( this, propDesc, pojoMethod ) );
+            }
+            if( stateDescriptor instanceof AssociationStateDescriptor )
+            {
+                AssociationStateDescriptor assocStateDesc = (AssociationStateDescriptor) stateDescriptor;
+                for( AssociationDescriptor assocDesc : assocStateDesc.associations() )
+                {
+                    Method pojoMethod = findMethod( pojo, assocDesc.qualifiedName().name() );
+                    handlers.put( assocDesc.accessor(), new JavabeanAssociation( this, assocDesc, pojoMethod ) );
+                }
+                for( AssociationDescriptor assocDesc : assocStateDesc.manyAssociations() )
+                {
+                    Method pojoMethod = findMethod( pojo, assocDesc.qualifiedName().name() );
+                    handlers.put( assocDesc.accessor(), new JavabeanManyAssociation( this, assocDesc, pojoMethod ) );
+                }
+            }
+        }
+    }
+
+    private Method findMethod( Object pojo, String name )
+    {
+        String methodName = "get" + Character.toUpperCase( name.charAt( 0 ) ) + name.substring( 1 );
+        Method pojoMethod;
+        try
+        {
+            pojoMethod = pojo.getClass().getMethod( methodName );
+        }
+        catch( NoSuchMethodException e )
+        {
+            methodName = "is" + Character.toUpperCase( name.charAt( 0 ) ) + name.substring( 1 );
+            try
+            {
+                pojoMethod = pojo.getClass().getMethod( methodName );
+            }
+            catch( NoSuchMethodException e1 )
+            {
+                throw new IllegalArgumentException( methodName + " is not present in " + pojo.getClass() );
+            }
+        }
+        return pojoMethod;
+    }
+
+    public Object getJavabean()
+    {
+        return pojo;
+    }
+
+    public void setJavabean( Object data )
+    {
+        pojo = data;
+    }
+
+    public Object invoke( Object proxy, Method method, Object[] args )
+        throws Throwable
+    {
+        synchronized( this )
+        {
+            return handlers.get( method );
+        }
+    }
+
+    public static class JavabeanSupportFilter
+        implements AppliesToFilter
+    {
+        public boolean appliesTo( Method method, Class<?> mixin, Class<?> compositeType, Class<?> modifierClass )
+        {
+            String methodName = method.getName();
+            Class<?> retType = method.getReturnType();
+            return Property.class.isAssignableFrom( retType ) ||
+                   Association.class.isAssignableFrom( retType ) ||
+                   ManyAssociation.class.isAssignableFrom( retType ) ||
+                   "getJavabean".equals( methodName ) || "setJavabean".equals( methodName );
+        }
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/zest-sandbox/blob/d4dd9c17/qi4j/libraries/beans/src/main/java/org/qi4j/library/beans/support/JavabeanProperty.java
----------------------------------------------------------------------
diff --git a/qi4j/libraries/beans/src/main/java/org/qi4j/library/beans/support/JavabeanProperty.java b/qi4j/libraries/beans/src/main/java/org/qi4j/library/beans/support/JavabeanProperty.java
new file mode 100644
index 0000000..6ca01f7
--- /dev/null
+++ b/qi4j/libraries/beans/src/main/java/org/qi4j/library/beans/support/JavabeanProperty.java
@@ -0,0 +1,123 @@
+/*
+ * Copyright 2008 Niclas Hedhman. All rights Reserved.
+ *
+ * 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.library.beans.support;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+import java.lang.reflect.UndeclaredThrowableException;
+import java.util.Arrays;
+import java.util.List;
+import org.qi4j.api.composite.TransientBuilder;
+import org.qi4j.api.composite.TransientBuilderFactory;
+import org.qi4j.api.property.Property;
+import org.qi4j.api.property.PropertyDescriptor;
+
+public class JavabeanProperty implements Property
+{
+    private final JavabeanMixin javabeanMixin;
+    private final PropertyDescriptor descriptor;
+    private final Method pojoMethod;
+
+    public JavabeanProperty( JavabeanMixin javabeanMixin, PropertyDescriptor descriptor, Method pojoMethod )
+    {
+        this.javabeanMixin = javabeanMixin;
+        this.descriptor = descriptor;
+        this.pojoMethod = pojoMethod;
+    }
+
+    public Object get()
+    {
+        try
+        {
+            Object resultObject = pojoMethod.invoke( javabeanMixin.pojo );
+            return wrap( javabeanMixin.cbf, resultObject );
+        }
+        catch( IllegalAccessException e )
+        {
+            throw new IllegalArgumentException( "POJO is not compatible with JavaBeans specification. Method must be public: " + pojoMethod );
+        }
+        catch( InvocationTargetException e )
+        {
+            throw new UndeclaredThrowableException( e.getTargetException() );
+        }
+    }
+
+    private Object wrap( TransientBuilderFactory factory, Object resultObject )
+    {
+        if( resultObject == null )
+        {
+            return null;
+        }
+        Type type = descriptor.type();
+        if( type instanceof Class )
+        {
+            Class clazz = (Class) type;
+            if( clazz.isInterface() )
+            {
+                if( clazz.equals( List.class ) )
+                {
+                    if( resultObject.getClass().isArray() )
+                    {
+                        resultObject = Arrays.asList( (Object[]) resultObject );
+                    }
+                }
+                if( clazz.isArray() )
+                {
+                    if( List.class.isAssignableFrom( resultObject.getClass() ) )
+                    {
+                        resultObject = ( (List) resultObject ).toArray();
+                    }
+                }
+                TransientBuilder<?> builder = factory.newTransientBuilder( clazz );
+                builder.use( resultObject );
+                return builder.newInstance();
+            }
+        }
+        if( type instanceof ParameterizedType )
+        {
+            if( !resultObject.getClass().equals( type ) )
+            {
+                ParameterizedType paramtype = (ParameterizedType) type;
+                Type rawType = paramtype.getRawType();
+                Type actType = paramtype.getActualTypeArguments()[ 0 ];
+                if( List.class.isAssignableFrom( (Class<?>) rawType ) )
+                {
+                    if( !( actType instanceof Class ) ||
+                        ( (Class) actType ).isInstance( resultObject ) )
+                    {
+                        String message = "The type " + paramtype + " is not compatible with " + resultObject.getClass();
+                        throw new IllegalArgumentException( message );
+                    }
+                    if( resultObject.getClass().isArray() )
+                    {
+                        resultObject = Arrays.asList( (Object[]) resultObject );
+                    }
+                }
+            }
+        }
+        return resultObject;
+    }
+
+    public void set( Object newValue )
+        throws IllegalArgumentException, IllegalStateException
+    {
+        //TODO: Auto-generated, need attention.
+    }
+}

http://git-wip-us.apache.org/repos/asf/zest-sandbox/blob/d4dd9c17/qi4j/libraries/beans/src/main/java/org/qi4j/library/beans/support/JavabeanSupport.java
----------------------------------------------------------------------
diff --git a/qi4j/libraries/beans/src/main/java/org/qi4j/library/beans/support/JavabeanSupport.java b/qi4j/libraries/beans/src/main/java/org/qi4j/library/beans/support/JavabeanSupport.java
new file mode 100644
index 0000000..faa9e96
--- /dev/null
+++ b/qi4j/libraries/beans/src/main/java/org/qi4j/library/beans/support/JavabeanSupport.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2008 Niclas Hedhman. All rights Reserved.
+ *
+ * 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.library.beans.support;
+
+import org.qi4j.api.mixin.Mixins;
+
+/**
+ * This mixin type is used to have a POJO (Spring's definition) as the backing implementation
+ * of the mixin state.
+ */
+@Mixins( JavabeanMixin.class )
+public interface JavabeanSupport<T>
+{
+    T getJavabean();
+
+    void setJavabean( T data );
+}

http://git-wip-us.apache.org/repos/asf/zest-sandbox/blob/d4dd9c17/qi4j/libraries/beans/src/main/java/org/qi4j/library/beans/support/Wrapper.java
----------------------------------------------------------------------
diff --git a/qi4j/libraries/beans/src/main/java/org/qi4j/library/beans/support/Wrapper.java b/qi4j/libraries/beans/src/main/java/org/qi4j/library/beans/support/Wrapper.java
new file mode 100644
index 0000000..d717f75
--- /dev/null
+++ b/qi4j/libraries/beans/src/main/java/org/qi4j/library/beans/support/Wrapper.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2008 Niclas Hedhman. All rights Reserved.
+ *
+ * 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.library.beans.support;
+
+import org.qi4j.api.composite.TransientBuilderFactory;
+import org.qi4j.api.composite.TransientBuilder;
+import org.qi4j.api.association.AssociationDescriptor;
+
+public class Wrapper
+{
+    static Object wrap( Object resultObject, AssociationDescriptor info, TransientBuilderFactory cbf )
+    {
+        Class type = (Class) info.type();
+        if( type.isInterface() )
+        {
+            TransientBuilder<?> builder = cbf.newTransientBuilder( type );
+            builder.use( resultObject );
+            return builder.newInstance();
+        }
+        return resultObject;
+    }
+
+    static Object unwrap( Object object )
+    {
+        if( object instanceof JavabeanSupport )
+        {
+            return ( (JavabeanSupport) object ).getJavabean();
+        }
+        return object;
+    }
+
+}


Mime
View raw message