You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@polygene.apache.org by pa...@apache.org on 2015/04/21 13:50:17 UTC
[05/18] zest-sandbox git commit: Move Qi4j related projects in a
`qi4j/` subfolder
http://git-wip-us.apache.org/repos/asf/zest-sandbox/blob/d4dd9c17/qi4j/extensions/entitystore-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;
+ }
+
+}