polygene-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From nic...@apache.org
Subject [5/9] polygene-java git commit: Trying to unify the naming of all the extensions.
Date Sat, 27 May 2017 02:12:00 GMT
http://git-wip-us.apache.org/repos/asf/polygene-java/blob/ea3f84a5/extensions/entitystore-mongodb/src/main/java/org/apache/polygene/entitystore/mongodb/MongoDBEntityStoreService.java
----------------------------------------------------------------------
diff --git a/extensions/entitystore-mongodb/src/main/java/org/apache/polygene/entitystore/mongodb/MongoDBEntityStoreService.java b/extensions/entitystore-mongodb/src/main/java/org/apache/polygene/entitystore/mongodb/MongoDBEntityStoreService.java
new file mode 100644
index 0000000..df2c510
--- /dev/null
+++ b/extensions/entitystore-mongodb/src/main/java/org/apache/polygene/entitystore/mongodb/MongoDBEntityStoreService.java
@@ -0,0 +1,49 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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.apache.polygene.entitystore.mongodb;
+
+import org.apache.polygene.api.concern.Concerns;
+import org.apache.polygene.api.configuration.Configuration;
+import org.apache.polygene.api.mixin.Mixins;
+import org.apache.polygene.api.service.ServiceActivation;
+import org.apache.polygene.library.locking.LockingAbstractComposite;
+import org.apache.polygene.spi.entitystore.ConcurrentModificationCheckConcern;
+import org.apache.polygene.spi.entitystore.EntityStateVersions;
+import org.apache.polygene.spi.entitystore.EntityStore;
+import org.apache.polygene.spi.entitystore.StateChangeNotificationConcern;
+import org.apache.polygene.spi.entitystore.helpers.JSONMapEntityStoreActivation;
+import org.apache.polygene.spi.entitystore.helpers.JSONMapEntityStoreMixin;
+
+/**
+ * MongoDB EntityStore service.
+ * <p>Based on @{@link JSONMapEntityStoreMixin}.</p>
+ */
+@Concerns( { StateChangeNotificationConcern.class, ConcurrentModificationCheckConcern.class } )
+@Mixins( { JSONMapEntityStoreMixin.class, MongoDBEntityStoreMixin.class } )
+public interface MongoDBEntityStoreService
+    extends EntityStore,
+            EntityStateVersions,
+            ServiceActivation,
+            JSONMapEntityStoreActivation,
+            LockingAbstractComposite,
+            Configuration,
+            MongoDBAccessors
+{
+}

http://git-wip-us.apache.org/repos/asf/polygene-java/blob/ea3f84a5/extensions/entitystore-mongodb/src/main/java/org/apache/polygene/entitystore/mongodb/MongoDBMapEntityStoreMixin.java
----------------------------------------------------------------------
diff --git a/extensions/entitystore-mongodb/src/main/java/org/apache/polygene/entitystore/mongodb/MongoDBMapEntityStoreMixin.java b/extensions/entitystore-mongodb/src/main/java/org/apache/polygene/entitystore/mongodb/MongoDBMapEntityStoreMixin.java
deleted file mode 100644
index 3294910..0000000
--- a/extensions/entitystore-mongodb/src/main/java/org/apache/polygene/entitystore/mongodb/MongoDBMapEntityStoreMixin.java
+++ /dev/null
@@ -1,322 +0,0 @@
-/*
- *  Licensed to the Apache Software Foundation (ASF) under one
- *  or more contributor license agreements.  See the NOTICE file
- *  distributed with this work for additional information
- *  regarding copyright ownership.  The ASF licenses this file
- *  to you 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.apache.polygene.entitystore.mongodb;
-
-import com.mongodb.BasicDBObject;
-import com.mongodb.MongoClient;
-import com.mongodb.MongoClientOptions;
-import com.mongodb.MongoCredential;
-import com.mongodb.ServerAddress;
-import com.mongodb.WriteConcern;
-import com.mongodb.client.MongoCollection;
-import com.mongodb.client.MongoCursor;
-import com.mongodb.client.MongoDatabase;
-import com.mongodb.util.JSON;
-import java.io.IOException;
-import java.io.Reader;
-import java.io.StringReader;
-import java.io.StringWriter;
-import java.io.Writer;
-import java.net.UnknownHostException;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.List;
-import java.util.stream.Stream;
-import java.util.stream.StreamSupport;
-import org.apache.polygene.api.configuration.Configuration;
-import org.apache.polygene.api.entity.EntityDescriptor;
-import org.apache.polygene.api.entity.EntityReference;
-import org.apache.polygene.api.injection.scope.This;
-import org.apache.polygene.api.service.ServiceActivation;
-import org.apache.polygene.spi.entitystore.EntityNotFoundException;
-import org.apache.polygene.spi.entitystore.EntityStoreException;
-import org.apache.polygene.spi.entitystore.helpers.MapEntityStore;
-import org.bson.Document;
-import org.bson.conversions.Bson;
-
-import static com.mongodb.client.model.Filters.eq;
-import static java.util.stream.Collectors.toList;
-
-/**
- * MongoDB implementation of MapEntityStore.
- */
-public class MongoDBMapEntityStoreMixin
-    implements ServiceActivation, MapEntityStore, MongoDBAccessors
-{
-    private static final String DEFAULT_DATABASE_NAME = "polygene:entitystore";
-    private static final String DEFAULT_COLLECTION_NAME = "polygene:entitystore:entities";
-    public static final String IDENTITY_COLUMN = "_id";
-    public static final String STATE_COLUMN = "state";
-    @This
-    private Configuration<MongoDBEntityStoreConfiguration> configuration;
-    private List<ServerAddress> serverAddresses;
-    private String databaseName;
-    private String collectionName;
-    private WriteConcern writeConcern;
-    private String username;
-    private char[] password;
-    private MongoClient mongo;
-    private MongoDatabase db;
-
-    @Override
-    public void activateService()
-        throws Exception
-    {
-        loadConfiguration();
-
-        // Create Mongo driver and open the database
-        MongoClientOptions options = MongoClientOptions.builder().writeConcern( writeConcern ).build();
-        if( username.isEmpty() )
-        {
-            mongo = new MongoClient( serverAddresses, options );
-        }
-        else
-        {
-            MongoCredential credential = MongoCredential.createMongoCRCredential( username, databaseName, password );
-            mongo = new MongoClient( serverAddresses, Collections.singletonList( credential ), options );
-        }
-        db = mongo.getDatabase( databaseName );
-
-        // Create index if needed
-        MongoCollection<Document> entities = db.getCollection( collectionName );
-        if( !entities.listIndexes().iterator().hasNext() )
-        {
-            entities.createIndex( new BasicDBObject( IDENTITY_COLUMN, 1 ) );
-        }
-    }
-
-    private void loadConfiguration()
-        throws UnknownHostException
-    {
-        configuration.refresh();
-        MongoDBEntityStoreConfiguration config = configuration.get();
-
-        // Combine hostname, port and nodes configuration properties
-        // If no configuration, use 127.0.0.1:27017
-        serverAddresses = new ArrayList<>();
-        int port = config.port().get() == null ? 27017 : config.port().get();
-        List<String> nodes = config.nodes().get();
-        if( nodes.isEmpty() )
-        {
-            String hostname = config.hostname().get() == null ? "127.0.0.1" : config.hostname().get();
-            serverAddresses.add( new ServerAddress( hostname, port ) );
-        }
-        else
-        {
-            if( config.hostname().get() != null && !config.hostname().get().isEmpty() )
-            {
-                serverAddresses.add( new ServerAddress( config.hostname().get(), port ) );
-            }
-            serverAddresses.addAll( nodes.stream()
-                                         .map( this::parseNode )
-                                         .collect( toList() )
-                                  );
-        }
-
-        // If database name not configured, set it to polygene:entitystore
-        databaseName = config.database().get();
-        if( databaseName == null )
-        {
-            databaseName = DEFAULT_DATABASE_NAME;
-        }
-
-        // If collection name not configured, set it to polygene:entitystore:entities
-        collectionName = config.collection().get();
-        if( collectionName == null )
-        {
-            collectionName = DEFAULT_COLLECTION_NAME;
-        }
-
-        // If write concern not configured, set it to normal
-        switch( config.writeConcern().get() )
-        {
-        case W1:
-            writeConcern = WriteConcern.W1;
-            break;
-        case W2:
-            writeConcern = WriteConcern.W2;
-            break;
-        case W3:
-            writeConcern = WriteConcern.W3;
-            break;
-        case UNACKNOWLEDGED:
-            writeConcern = WriteConcern.UNACKNOWLEDGED;
-            break;
-        case JOURNALED:
-            writeConcern = WriteConcern.JOURNALED;
-            break;
-        case MAJORITY:
-            writeConcern = WriteConcern.MAJORITY;
-            break;
-        case ACKNOWLEDGED:
-        default:
-            writeConcern = WriteConcern.ACKNOWLEDGED;
-        }
-
-        // Username and password are defaulted to empty strings
-        username = config.username().get();
-        password = config.password().get().toCharArray();
-    }
-
-    private <R> ServerAddress parseNode( String nodeString )
-    {
-        String[] parts = nodeString.split( ":" );
-        String host = parts[ 0 ];
-        if( parts.length == 2 )
-        {
-            int port = Integer.parseInt( parts[ 1 ] );
-            return new ServerAddress( host, port );
-        }
-        return new ServerAddress( host );
-    }
-
-    @Override
-    public void passivateService()
-        throws Exception
-    {
-        mongo.close();
-        mongo = null;
-        databaseName = null;
-        collectionName = null;
-        writeConcern = null;
-        username = null;
-        Arrays.fill( password, ' ' );
-        password = null;
-        db = null;
-    }
-
-    @Override
-    public MongoClient mongoInstanceUsed()
-    {
-        return mongo;
-    }
-
-    @Override
-    public MongoDatabase dbInstanceUsed()
-    {
-        return db;
-    }
-
-    @Override
-    public String collectionUsed()
-    {
-        return collectionName;
-    }
-
-    @Override
-    public Reader get( EntityReference entityReference )
-        throws EntityStoreException
-    {
-        MongoCursor<Document> cursor = db.getCollection( collectionName )
-                                         .find( byIdentity( entityReference ) )
-                                         .limit( 1 ).iterator();
-        if( !cursor.hasNext() )
-        {
-            throw new EntityNotFoundException( entityReference );
-        }
-        Document bsonState = (Document) cursor.next().get( STATE_COLUMN );
-        String jsonState = JSON.serialize( bsonState );
-        return new StringReader( jsonState );
-    }
-
-    @Override
-    public void applyChanges( MapChanges changes )
-        throws Exception
-    {
-        final MongoCollection<Document> entities = db.getCollection( collectionName );
-
-        changes.visitMap( new MapChanger()
-        {
-            @Override
-            public Writer newEntity( EntityReference ref, EntityDescriptor entityDescriptor )
-                throws IOException
-            {
-                return new StringWriter( 1000 )
-                {
-                    @Override
-                    public void close()
-                        throws IOException
-                    {
-                        super.close();
-                        Document bsonState = Document.parse( toString() );
-                        Document entity = new Document();
-                        entity.put( IDENTITY_COLUMN, ref.identity().toString() );
-                        entity.put( STATE_COLUMN, bsonState );
-                        entities.insertOne( entity );
-                    }
-                };
-            }
-
-            @Override
-            public Writer updateEntity( MapChange mapChange )
-                throws IOException
-            {
-                return new StringWriter( 1000 )
-                {
-                    @Override
-                    public void close()
-                        throws IOException
-                    {
-                        super.close();
-                        Document bsonState = Document.parse( toString() );
-                        Document entity = new Document();
-                        entity.put( IDENTITY_COLUMN, mapChange.reference().identity().toString() );
-                        entity.put( STATE_COLUMN, bsonState );
-                        entities.replaceOne( byIdentity( mapChange.reference() ), entity );
-                    }
-                };
-            }
-
-            @Override
-            public void removeEntity( EntityReference ref, EntityDescriptor entityDescriptor )
-                throws EntityNotFoundException
-            {
-                Bson byIdFilter = byIdentity( ref );
-                MongoCursor<Document> cursor = db.getCollection( collectionName )
-                                                 .find( byIdFilter )
-                                                 .limit( 1 ).iterator();
-                if( !cursor.hasNext() )
-                {
-                    throw new EntityNotFoundException( ref );
-                }
-                entities.deleteOne( byIdFilter );
-            }
-        } );
-    }
-
-    @Override
-    public Stream<Reader> entityStates()
-    {
-        return StreamSupport
-            .stream( db.getCollection( collectionName ).find().spliterator(), false )
-            .map( eachEntity ->
-                  {
-                      Document bsonState = (Document) eachEntity.get( STATE_COLUMN );
-                      String jsonState = JSON.serialize( bsonState );
-                      return new StringReader( jsonState );
-                  } );
-    }
-
-    private Bson byIdentity( EntityReference entityReference )
-    {
-        return eq( IDENTITY_COLUMN, entityReference.identity().toString() );
-    }
-}

http://git-wip-us.apache.org/repos/asf/polygene-java/blob/ea3f84a5/extensions/entitystore-mongodb/src/main/java/org/apache/polygene/entitystore/mongodb/MongoDBMapEntityStoreService.java
----------------------------------------------------------------------
diff --git a/extensions/entitystore-mongodb/src/main/java/org/apache/polygene/entitystore/mongodb/MongoDBMapEntityStoreService.java b/extensions/entitystore-mongodb/src/main/java/org/apache/polygene/entitystore/mongodb/MongoDBMapEntityStoreService.java
deleted file mode 100644
index 4f34214..0000000
--- a/extensions/entitystore-mongodb/src/main/java/org/apache/polygene/entitystore/mongodb/MongoDBMapEntityStoreService.java
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- *  Licensed to the Apache Software Foundation (ASF) under one
- *  or more contributor license agreements.  See the NOTICE file
- *  distributed with this work for additional information
- *  regarding copyright ownership.  The ASF licenses this file
- *  to you 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.apache.polygene.entitystore.mongodb;
-
-import org.apache.polygene.api.concern.Concerns;
-import org.apache.polygene.api.configuration.Configuration;
-import org.apache.polygene.api.mixin.Mixins;
-import org.apache.polygene.api.service.ServiceActivation;
-import org.apache.polygene.library.locking.LockingAbstractComposite;
-import org.apache.polygene.spi.entitystore.ConcurrentModificationCheckConcern;
-import org.apache.polygene.spi.entitystore.EntityStateVersions;
-import org.apache.polygene.spi.entitystore.EntityStore;
-import org.apache.polygene.spi.entitystore.StateChangeNotificationConcern;
-import org.apache.polygene.spi.entitystore.helpers.JSONMapEntityStoreActivation;
-import org.apache.polygene.spi.entitystore.helpers.JSONMapEntityStoreMixin;
-
-/**
- * MongoDB EntityStore service.
- * <p>Based on @{@link JSONMapEntityStoreMixin}.</p>
- */
-@Concerns( { StateChangeNotificationConcern.class, ConcurrentModificationCheckConcern.class } )
-@Mixins( { JSONMapEntityStoreMixin.class, MongoDBMapEntityStoreMixin.class } )
-public interface MongoDBMapEntityStoreService
-    extends EntityStore,
-            EntityStateVersions,
-            ServiceActivation,
-            JSONMapEntityStoreActivation,
-            LockingAbstractComposite,
-            Configuration,
-            MongoDBAccessors
-{
-}

http://git-wip-us.apache.org/repos/asf/polygene-java/blob/ea3f84a5/extensions/entitystore-mongodb/src/main/java/org/apache/polygene/entitystore/mongodb/assembly/MongoDBEntityStoreAssembler.java
----------------------------------------------------------------------
diff --git a/extensions/entitystore-mongodb/src/main/java/org/apache/polygene/entitystore/mongodb/assembly/MongoDBEntityStoreAssembler.java b/extensions/entitystore-mongodb/src/main/java/org/apache/polygene/entitystore/mongodb/assembly/MongoDBEntityStoreAssembler.java
index eff0463..1c3b780 100644
--- a/extensions/entitystore-mongodb/src/main/java/org/apache/polygene/entitystore/mongodb/assembly/MongoDBEntityStoreAssembler.java
+++ b/extensions/entitystore-mongodb/src/main/java/org/apache/polygene/entitystore/mongodb/assembly/MongoDBEntityStoreAssembler.java
@@ -24,7 +24,7 @@ import org.apache.polygene.bootstrap.AssemblyException;
 import org.apache.polygene.bootstrap.ModuleAssembly;
 import org.apache.polygene.bootstrap.ServiceDeclaration;
 import org.apache.polygene.entitystore.mongodb.MongoDBEntityStoreConfiguration;
-import org.apache.polygene.entitystore.mongodb.MongoDBMapEntityStoreService;
+import org.apache.polygene.entitystore.mongodb.MongoDBEntityStoreService;
 
 public class MongoDBEntityStoreAssembler
     extends Assemblers.VisibilityIdentityConfig<MongoDBEntityStoreAssembler>
@@ -33,7 +33,7 @@ public class MongoDBEntityStoreAssembler
     public void assemble( ModuleAssembly module )
         throws AssemblyException
     {
-        ServiceDeclaration service = module.services( MongoDBMapEntityStoreService.class ).visibleIn( visibility() );
+        ServiceDeclaration service = module.services( MongoDBEntityStoreService.class ).visibleIn( visibility() );
         if( hasIdentity() )
         {
             service.identifiedBy( identity() );

http://git-wip-us.apache.org/repos/asf/polygene-java/blob/ea3f84a5/extensions/entitystore-mongodb/src/test/java/org/apache/polygene/entitystore/mongodb/MongoDBEntityStoreTest.java
----------------------------------------------------------------------
diff --git a/extensions/entitystore-mongodb/src/test/java/org/apache/polygene/entitystore/mongodb/MongoDBEntityStoreTest.java b/extensions/entitystore-mongodb/src/test/java/org/apache/polygene/entitystore/mongodb/MongoDBEntityStoreTest.java
new file mode 100644
index 0000000..131e76e
--- /dev/null
+++ b/extensions/entitystore-mongodb/src/test/java/org/apache/polygene/entitystore/mongodb/MongoDBEntityStoreTest.java
@@ -0,0 +1,88 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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.apache.polygene.entitystore.mongodb;
+
+import com.mongodb.Mongo;
+import org.apache.polygene.api.common.Visibility;
+import org.apache.polygene.bootstrap.AssemblyException;
+import org.apache.polygene.bootstrap.ModuleAssembly;
+import org.apache.polygene.entitystore.mongodb.assembly.MongoDBEntityStoreAssembler;
+import org.apache.polygene.test.EntityTestAssembler;
+import org.apache.polygene.test.entity.AbstractEntityStoreTest;
+import org.junit.BeforeClass;
+
+import static org.apache.polygene.test.util.Assume.assumeConnectivity;
+
+/**
+ * Test the MongoDBEntityStoreService.
+ * <p>Installing mongodb and starting it should suffice as the test use mongodb defaults: 127.0.0.1:27017</p>
+ */
+public class MongoDBEntityStoreTest extends AbstractEntityStoreTest
+{
+    @BeforeClass
+    public static void beforeRedisMapEntityStoreTests()
+    {
+        assumeConnectivity( "localhost", 27017 );
+    }
+
+    @Override
+    // START SNIPPET: assembly
+    public void assemble( ModuleAssembly module )
+        throws AssemblyException
+    {
+        // END SNIPPET: assembly
+        super.assemble( module );
+
+        ModuleAssembly config = module.layer().module( "config" );
+        new EntityTestAssembler().assemble( config );
+
+        // START SNIPPET: assembly
+        new MongoDBEntityStoreAssembler().withConfig( config, Visibility.layer ).assemble( module );
+        // END SNIPPET: assembly
+
+        MongoDBEntityStoreConfiguration mongoConfig = config.forMixin( MongoDBEntityStoreConfiguration.class ).declareDefaults();
+        mongoConfig.writeConcern().set( MongoDBEntityStoreConfiguration.WriteConcern.MAJORITY );
+        mongoConfig.database().set( "polygene:test" );
+        mongoConfig.collection().set( "polygene:test:entities" );
+        // START SNIPPET: assembly
+    }
+
+    // END SNIPPET: assembly
+    private Mongo mongo;
+    private String dbName;
+
+    @Override
+    public void setUp()
+        throws Exception
+    {
+        super.setUp();
+        MongoDBEntityStoreService es = serviceFinder.findService( MongoDBEntityStoreService.class ).get();
+        mongo = es.mongoInstanceUsed();
+        dbName = es.dbInstanceUsed().getName();
+    }
+
+    @Override
+    public void tearDown()
+        throws Exception
+    {
+        mongo.dropDatabase( dbName );
+        super.tearDown();
+    }
+}

http://git-wip-us.apache.org/repos/asf/polygene-java/blob/ea3f84a5/extensions/entitystore-mongodb/src/test/java/org/apache/polygene/entitystore/mongodb/MongoDBEntityStoreWithCacheTest.java
----------------------------------------------------------------------
diff --git a/extensions/entitystore-mongodb/src/test/java/org/apache/polygene/entitystore/mongodb/MongoDBEntityStoreWithCacheTest.java b/extensions/entitystore-mongodb/src/test/java/org/apache/polygene/entitystore/mongodb/MongoDBEntityStoreWithCacheTest.java
new file mode 100644
index 0000000..bb077cb
--- /dev/null
+++ b/extensions/entitystore-mongodb/src/test/java/org/apache/polygene/entitystore/mongodb/MongoDBEntityStoreWithCacheTest.java
@@ -0,0 +1,84 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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.apache.polygene.entitystore.mongodb;
+
+import com.mongodb.Mongo;
+import org.apache.polygene.api.common.Visibility;
+import org.apache.polygene.bootstrap.AssemblyException;
+import org.apache.polygene.bootstrap.ModuleAssembly;
+import org.apache.polygene.entitystore.mongodb.assembly.MongoDBEntityStoreAssembler;
+import org.apache.polygene.test.EntityTestAssembler;
+import org.apache.polygene.test.cache.AbstractEntityStoreWithCacheTest;
+import org.junit.BeforeClass;
+
+import static org.apache.polygene.test.util.Assume.assumeConnectivity;
+
+/**
+ * Test the MongoDBEntityStoreService usage with a CachePool.
+ * <p>Installing mongodb and starting it should suffice as the test use mongodb defaults: 127.0.0.1:27017</p>
+ */
+public class MongoDBEntityStoreWithCacheTest
+    extends AbstractEntityStoreWithCacheTest
+{
+    @BeforeClass
+    public static void beforeRedisMapEntityStoreTests()
+    {
+        assumeConnectivity( "localhost", 27017 );
+    }
+
+    @Override
+    public void assemble( ModuleAssembly module )
+        throws AssemblyException
+    {
+        super.assemble( module );
+
+        ModuleAssembly config = module.layer().module( "config" );
+        new EntityTestAssembler().assemble( config );
+
+        new MongoDBEntityStoreAssembler().withConfig( config, Visibility.layer ).assemble( module );
+
+        MongoDBEntityStoreConfiguration mongoConfig = config.forMixin( MongoDBEntityStoreConfiguration.class ).declareDefaults();
+        mongoConfig.writeConcern().set( MongoDBEntityStoreConfiguration.WriteConcern.MAJORITY );
+        mongoConfig.database().set( "polygene:test" );
+        mongoConfig.collection().set( "polygene:test:entities" );
+    }
+
+    private Mongo mongo;
+    private String dbName;
+
+    @Override
+    public void setUp()
+        throws Exception
+    {
+        super.setUp();
+        MongoDBEntityStoreService es = serviceFinder.findService( MongoDBEntityStoreService.class ).get();
+        mongo = es.mongoInstanceUsed();
+        dbName = es.dbInstanceUsed().getName();
+
+    }
+
+    @Override
+    public void tearDown()
+        throws Exception
+    {
+        mongo.dropDatabase( dbName );
+        super.tearDown();
+    }
+}

http://git-wip-us.apache.org/repos/asf/polygene-java/blob/ea3f84a5/extensions/entitystore-mongodb/src/test/java/org/apache/polygene/entitystore/mongodb/MongoDBMapEntityStoreTest.java
----------------------------------------------------------------------
diff --git a/extensions/entitystore-mongodb/src/test/java/org/apache/polygene/entitystore/mongodb/MongoDBMapEntityStoreTest.java b/extensions/entitystore-mongodb/src/test/java/org/apache/polygene/entitystore/mongodb/MongoDBMapEntityStoreTest.java
deleted file mode 100644
index f98fdc3..0000000
--- a/extensions/entitystore-mongodb/src/test/java/org/apache/polygene/entitystore/mongodb/MongoDBMapEntityStoreTest.java
+++ /dev/null
@@ -1,88 +0,0 @@
-/*
- *  Licensed to the Apache Software Foundation (ASF) under one
- *  or more contributor license agreements.  See the NOTICE file
- *  distributed with this work for additional information
- *  regarding copyright ownership.  The ASF licenses this file
- *  to you 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.apache.polygene.entitystore.mongodb;
-
-import com.mongodb.Mongo;
-import org.apache.polygene.api.common.Visibility;
-import org.apache.polygene.bootstrap.AssemblyException;
-import org.apache.polygene.bootstrap.ModuleAssembly;
-import org.apache.polygene.entitystore.mongodb.assembly.MongoDBEntityStoreAssembler;
-import org.apache.polygene.test.EntityTestAssembler;
-import org.apache.polygene.test.entity.AbstractEntityStoreTest;
-import org.junit.BeforeClass;
-
-import static org.apache.polygene.test.util.Assume.assumeConnectivity;
-
-/**
- * Test the MongoDBMapEntityStoreService.
- * <p>Installing mongodb and starting it should suffice as the test use mongodb defaults: 127.0.0.1:27017</p>
- */
-public class MongoDBMapEntityStoreTest extends AbstractEntityStoreTest
-{
-    @BeforeClass
-    public static void beforeRedisMapEntityStoreTests()
-    {
-        assumeConnectivity( "localhost", 27017 );
-    }
-
-    @Override
-    // START SNIPPET: assembly
-    public void assemble( ModuleAssembly module )
-        throws AssemblyException
-    {
-        // END SNIPPET: assembly
-        super.assemble( module );
-
-        ModuleAssembly config = module.layer().module( "config" );
-        new EntityTestAssembler().assemble( config );
-
-        // START SNIPPET: assembly
-        new MongoDBEntityStoreAssembler().withConfig( config, Visibility.layer ).assemble( module );
-        // END SNIPPET: assembly
-
-        MongoDBEntityStoreConfiguration mongoConfig = config.forMixin( MongoDBEntityStoreConfiguration.class ).declareDefaults();
-        mongoConfig.writeConcern().set( MongoDBEntityStoreConfiguration.WriteConcern.MAJORITY );
-        mongoConfig.database().set( "polygene:test" );
-        mongoConfig.collection().set( "polygene:test:entities" );
-        // START SNIPPET: assembly
-    }
-
-    // END SNIPPET: assembly
-    private Mongo mongo;
-    private String dbName;
-
-    @Override
-    public void setUp()
-        throws Exception
-    {
-        super.setUp();
-        MongoDBMapEntityStoreService es = serviceFinder.findService( MongoDBMapEntityStoreService.class ).get();
-        mongo = es.mongoInstanceUsed();
-        dbName = es.dbInstanceUsed().getName();
-    }
-
-    @Override
-    public void tearDown()
-        throws Exception
-    {
-        mongo.dropDatabase( dbName );
-        super.tearDown();
-    }
-}

http://git-wip-us.apache.org/repos/asf/polygene-java/blob/ea3f84a5/extensions/entitystore-mongodb/src/test/java/org/apache/polygene/entitystore/mongodb/MongoDBMapEntityStoreWithCacheTest.java
----------------------------------------------------------------------
diff --git a/extensions/entitystore-mongodb/src/test/java/org/apache/polygene/entitystore/mongodb/MongoDBMapEntityStoreWithCacheTest.java b/extensions/entitystore-mongodb/src/test/java/org/apache/polygene/entitystore/mongodb/MongoDBMapEntityStoreWithCacheTest.java
deleted file mode 100644
index 65e9a13..0000000
--- a/extensions/entitystore-mongodb/src/test/java/org/apache/polygene/entitystore/mongodb/MongoDBMapEntityStoreWithCacheTest.java
+++ /dev/null
@@ -1,84 +0,0 @@
-/*
- *  Licensed to the Apache Software Foundation (ASF) under one
- *  or more contributor license agreements.  See the NOTICE file
- *  distributed with this work for additional information
- *  regarding copyright ownership.  The ASF licenses this file
- *  to you 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.apache.polygene.entitystore.mongodb;
-
-import com.mongodb.Mongo;
-import org.apache.polygene.api.common.Visibility;
-import org.apache.polygene.bootstrap.AssemblyException;
-import org.apache.polygene.bootstrap.ModuleAssembly;
-import org.apache.polygene.entitystore.mongodb.assembly.MongoDBEntityStoreAssembler;
-import org.apache.polygene.test.EntityTestAssembler;
-import org.apache.polygene.test.cache.AbstractEntityStoreWithCacheTest;
-import org.junit.BeforeClass;
-
-import static org.apache.polygene.test.util.Assume.assumeConnectivity;
-
-/**
- * Test the MongoDBMapEntityStoreService usage with a CachePool.
- * <p>Installing mongodb and starting it should suffice as the test use mongodb defaults: 127.0.0.1:27017</p>
- */
-public class MongoDBMapEntityStoreWithCacheTest
-    extends AbstractEntityStoreWithCacheTest
-{
-    @BeforeClass
-    public static void beforeRedisMapEntityStoreTests()
-    {
-        assumeConnectivity( "localhost", 27017 );
-    }
-
-    @Override
-    public void assemble( ModuleAssembly module )
-        throws AssemblyException
-    {
-        super.assemble( module );
-
-        ModuleAssembly config = module.layer().module( "config" );
-        new EntityTestAssembler().assemble( config );
-
-        new MongoDBEntityStoreAssembler().withConfig( config, Visibility.layer ).assemble( module );
-
-        MongoDBEntityStoreConfiguration mongoConfig = config.forMixin( MongoDBEntityStoreConfiguration.class ).declareDefaults();
-        mongoConfig.writeConcern().set( MongoDBEntityStoreConfiguration.WriteConcern.MAJORITY );
-        mongoConfig.database().set( "polygene:test" );
-        mongoConfig.collection().set( "polygene:test:entities" );
-    }
-
-    private Mongo mongo;
-    private String dbName;
-
-    @Override
-    public void setUp()
-        throws Exception
-    {
-        super.setUp();
-        MongoDBMapEntityStoreService es = serviceFinder.findService( MongoDBMapEntityStoreService.class ).get();
-        mongo = es.mongoInstanceUsed();
-        dbName = es.dbInstanceUsed().getName();
-
-    }
-
-    @Override
-    public void tearDown()
-        throws Exception
-    {
-        mongo.dropDatabase( dbName );
-        super.tearDown();
-    }
-}

http://git-wip-us.apache.org/repos/asf/polygene-java/blob/ea3f84a5/extensions/entitystore-redis/src/main/java/org/apache/polygene/entitystore/redis/RedisEntityStoreConfiguration.java
----------------------------------------------------------------------
diff --git a/extensions/entitystore-redis/src/main/java/org/apache/polygene/entitystore/redis/RedisEntityStoreConfiguration.java b/extensions/entitystore-redis/src/main/java/org/apache/polygene/entitystore/redis/RedisEntityStoreConfiguration.java
index c3ba3f1..8b001bc 100644
--- a/extensions/entitystore-redis/src/main/java/org/apache/polygene/entitystore/redis/RedisEntityStoreConfiguration.java
+++ b/extensions/entitystore-redis/src/main/java/org/apache/polygene/entitystore/redis/RedisEntityStoreConfiguration.java
@@ -23,7 +23,7 @@ import org.apache.polygene.api.common.Optional;
 import org.apache.polygene.api.property.Property;
 
 /**
- * Configuration for RedisMapEntityStoreService.
+ * Configuration for RedisEntityStoreService.
  */
 // START SNIPPET: config
 public interface RedisEntityStoreConfiguration

http://git-wip-us.apache.org/repos/asf/polygene-java/blob/ea3f84a5/extensions/entitystore-redis/src/main/java/org/apache/polygene/entitystore/redis/RedisEntityStoreMixin.java
----------------------------------------------------------------------
diff --git a/extensions/entitystore-redis/src/main/java/org/apache/polygene/entitystore/redis/RedisEntityStoreMixin.java b/extensions/entitystore-redis/src/main/java/org/apache/polygene/entitystore/redis/RedisEntityStoreMixin.java
new file mode 100644
index 0000000..f3a3b11
--- /dev/null
+++ b/extensions/entitystore-redis/src/main/java/org/apache/polygene/entitystore/redis/RedisEntityStoreMixin.java
@@ -0,0 +1,177 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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.apache.polygene.entitystore.redis;
+
+import java.io.IOException;
+import java.io.Reader;
+import java.io.StringReader;
+import java.io.StringWriter;
+import java.io.Writer;
+import java.util.stream.Stream;
+import org.apache.polygene.api.configuration.Configuration;
+import org.apache.polygene.api.entity.EntityDescriptor;
+import org.apache.polygene.api.entity.EntityReference;
+import org.apache.polygene.api.injection.scope.This;
+import org.apache.polygene.api.service.ServiceActivation;
+import org.apache.polygene.spi.entitystore.EntityAlreadyExistsException;
+import org.apache.polygene.spi.entitystore.EntityNotFoundException;
+import org.apache.polygene.spi.entitystore.EntityStoreException;
+import org.apache.polygene.spi.entitystore.helpers.MapEntityStore;
+import redis.clients.jedis.Jedis;
+import redis.clients.jedis.JedisPool;
+import redis.clients.jedis.JedisPoolConfig;
+import redis.clients.jedis.Protocol;
+
+/**
+ * Redis implementation of MapEntityStore.
+ */
+public class RedisEntityStoreMixin
+    implements ServiceActivation, RedisAccessors, MapEntityStore
+{
+    private static final String DEFAULT_HOST = "127.0.0.1";
+    private static final String NIL = "nil";
+    @This
+    private Configuration<RedisEntityStoreConfiguration> configuration;
+    private JedisPool pool;
+
+    @Override
+    public void activateService()
+        throws Exception
+    {
+        configuration.refresh();
+        RedisEntityStoreConfiguration config = configuration.get();
+
+        String host = config.host().get() == null ? DEFAULT_HOST : config.host().get();
+        int port = config.port().get() == null ? Protocol.DEFAULT_PORT : config.port().get();
+        int timeout = config.timeout().get() == null ? Protocol.DEFAULT_TIMEOUT : config.timeout().get();
+        String password = config.password().get();
+        int database = config.database().get() == null ? Protocol.DEFAULT_DATABASE : config.database().get();
+
+        pool = new JedisPool( new JedisPoolConfig(), host, port, timeout, password, database );
+    }
+
+    @Override
+    public void passivateService()
+        throws Exception
+    {
+        pool.destroy();
+        pool = null;
+    }
+
+    @Override
+    public JedisPool jedisPool()
+    {
+        return pool;
+    }
+
+    @Override
+    public Reader get( EntityReference entityReference )
+        throws EntityStoreException
+    {
+        try( Jedis jedis = pool.getResource() )
+        {
+            String jsonState = jedis.get( entityReference.identity().toString() );
+            if( notFound( jsonState ) )
+            {
+                throw new EntityNotFoundException( entityReference );
+            }
+            return new StringReader( jsonState );
+        }
+    }
+
+    @Override
+    public void applyChanges( MapChanges changes )
+        throws Exception
+    {
+        try( Jedis jedis = pool.getResource() )
+        {
+            changes.visitMap( new MapChanger()
+            {
+                @Override
+                public Writer newEntity( EntityReference ref, EntityDescriptor entityDescriptor )
+                    throws IOException
+                {
+                    return new StringWriter( 1000 )
+                    {
+                        @Override
+                        public void close()
+                            throws IOException
+                        {
+                            super.close();
+                            String statusCode = jedis.set( ref.identity().toString(), toString(), "NX" );
+                            if( !"OK".equals( statusCode ) )
+                            {
+                                throw new EntityAlreadyExistsException( ref );
+                            }
+                        }
+                    };
+                }
+
+                @Override
+                public Writer updateEntity( MapChange mapChange )
+                    throws IOException
+                {
+                    return new StringWriter( 1000 )
+                    {
+                        @Override
+                        public void close()
+                            throws IOException
+                        {
+                            super.close();
+                            String statusCode = jedis.set( mapChange.reference().identity().toString(),
+                                                           toString(),
+                                                           "XX" );
+                            if( !"OK".equals( statusCode ) )
+                            {
+                                throw new EntityNotFoundException( mapChange.reference() );
+                            }
+                        }
+                    };
+                }
+
+                @Override
+                public void removeEntity( EntityReference ref, EntityDescriptor entityDescriptor )
+                    throws EntityNotFoundException
+                {
+                    String jsonState = jedis.get( ref.identity().toString() );
+                    if( notFound( jsonState ) )
+                    {
+                        throw new EntityNotFoundException( ref );
+                    }
+                    jedis.del( ref.identity().toString() );
+                }
+            } );
+        }
+    }
+
+    @Override
+    public Stream<Reader> entityStates()
+    {
+        Jedis jedis = pool.getResource();
+        return jedis.keys( "*" ).stream()
+                    .map( key -> (Reader) new StringReader( jedis.get( key ) ) )
+                    .onClose( jedis::close );
+    }
+
+    private static boolean notFound( String jsonState )
+    {
+        return jsonState == null || NIL.equals( jsonState );
+    }
+}

http://git-wip-us.apache.org/repos/asf/polygene-java/blob/ea3f84a5/extensions/entitystore-redis/src/main/java/org/apache/polygene/entitystore/redis/RedisEntityStoreService.java
----------------------------------------------------------------------
diff --git a/extensions/entitystore-redis/src/main/java/org/apache/polygene/entitystore/redis/RedisEntityStoreService.java b/extensions/entitystore-redis/src/main/java/org/apache/polygene/entitystore/redis/RedisEntityStoreService.java
new file mode 100644
index 0000000..8e2ed6b
--- /dev/null
+++ b/extensions/entitystore-redis/src/main/java/org/apache/polygene/entitystore/redis/RedisEntityStoreService.java
@@ -0,0 +1,49 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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.apache.polygene.entitystore.redis;
+
+import org.apache.polygene.api.concern.Concerns;
+import org.apache.polygene.api.configuration.Configuration;
+import org.apache.polygene.api.mixin.Mixins;
+import org.apache.polygene.api.service.ServiceActivation;
+import org.apache.polygene.library.locking.LockingAbstractComposite;
+import org.apache.polygene.spi.entitystore.ConcurrentModificationCheckConcern;
+import org.apache.polygene.spi.entitystore.EntityStateVersions;
+import org.apache.polygene.spi.entitystore.EntityStore;
+import org.apache.polygene.spi.entitystore.StateChangeNotificationConcern;
+import org.apache.polygene.spi.entitystore.helpers.JSONMapEntityStoreActivation;
+import org.apache.polygene.spi.entitystore.helpers.JSONMapEntityStoreMixin;
+
+/**
+ * Redis EntityStore service.
+ * <p>Based on @{@link JSONMapEntityStoreMixin}.</p>
+ */
+@Concerns( { StateChangeNotificationConcern.class, ConcurrentModificationCheckConcern.class } )
+@Mixins( { JSONMapEntityStoreMixin.class, RedisEntityStoreMixin.class } )
+public interface RedisEntityStoreService
+        extends EntityStore,
+                EntityStateVersions,
+                ServiceActivation,
+                JSONMapEntityStoreActivation,
+                LockingAbstractComposite,
+                Configuration,
+                RedisAccessors
+{
+}

http://git-wip-us.apache.org/repos/asf/polygene-java/blob/ea3f84a5/extensions/entitystore-redis/src/main/java/org/apache/polygene/entitystore/redis/RedisMapEntityStoreMixin.java
----------------------------------------------------------------------
diff --git a/extensions/entitystore-redis/src/main/java/org/apache/polygene/entitystore/redis/RedisMapEntityStoreMixin.java b/extensions/entitystore-redis/src/main/java/org/apache/polygene/entitystore/redis/RedisMapEntityStoreMixin.java
deleted file mode 100644
index 0fcb2c5..0000000
--- a/extensions/entitystore-redis/src/main/java/org/apache/polygene/entitystore/redis/RedisMapEntityStoreMixin.java
+++ /dev/null
@@ -1,177 +0,0 @@
-/*
- *  Licensed to the Apache Software Foundation (ASF) under one
- *  or more contributor license agreements.  See the NOTICE file
- *  distributed with this work for additional information
- *  regarding copyright ownership.  The ASF licenses this file
- *  to you 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.apache.polygene.entitystore.redis;
-
-import java.io.IOException;
-import java.io.Reader;
-import java.io.StringReader;
-import java.io.StringWriter;
-import java.io.Writer;
-import java.util.stream.Stream;
-import org.apache.polygene.api.configuration.Configuration;
-import org.apache.polygene.api.entity.EntityDescriptor;
-import org.apache.polygene.api.entity.EntityReference;
-import org.apache.polygene.api.injection.scope.This;
-import org.apache.polygene.api.service.ServiceActivation;
-import org.apache.polygene.spi.entitystore.EntityAlreadyExistsException;
-import org.apache.polygene.spi.entitystore.EntityNotFoundException;
-import org.apache.polygene.spi.entitystore.EntityStoreException;
-import org.apache.polygene.spi.entitystore.helpers.MapEntityStore;
-import redis.clients.jedis.Jedis;
-import redis.clients.jedis.JedisPool;
-import redis.clients.jedis.JedisPoolConfig;
-import redis.clients.jedis.Protocol;
-
-/**
- * Redis implementation of MapEntityStore.
- */
-public class RedisMapEntityStoreMixin
-    implements ServiceActivation, RedisAccessors, MapEntityStore
-{
-    private static final String DEFAULT_HOST = "127.0.0.1";
-    private static final String NIL = "nil";
-    @This
-    private Configuration<RedisEntityStoreConfiguration> configuration;
-    private JedisPool pool;
-
-    @Override
-    public void activateService()
-        throws Exception
-    {
-        configuration.refresh();
-        RedisEntityStoreConfiguration config = configuration.get();
-
-        String host = config.host().get() == null ? DEFAULT_HOST : config.host().get();
-        int port = config.port().get() == null ? Protocol.DEFAULT_PORT : config.port().get();
-        int timeout = config.timeout().get() == null ? Protocol.DEFAULT_TIMEOUT : config.timeout().get();
-        String password = config.password().get();
-        int database = config.database().get() == null ? Protocol.DEFAULT_DATABASE : config.database().get();
-
-        pool = new JedisPool( new JedisPoolConfig(), host, port, timeout, password, database );
-    }
-
-    @Override
-    public void passivateService()
-        throws Exception
-    {
-        pool.destroy();
-        pool = null;
-    }
-
-    @Override
-    public JedisPool jedisPool()
-    {
-        return pool;
-    }
-
-    @Override
-    public Reader get( EntityReference entityReference )
-        throws EntityStoreException
-    {
-        try( Jedis jedis = pool.getResource() )
-        {
-            String jsonState = jedis.get( entityReference.identity().toString() );
-            if( notFound( jsonState ) )
-            {
-                throw new EntityNotFoundException( entityReference );
-            }
-            return new StringReader( jsonState );
-        }
-    }
-
-    @Override
-    public void applyChanges( MapChanges changes )
-        throws Exception
-    {
-        try( Jedis jedis = pool.getResource() )
-        {
-            changes.visitMap( new MapChanger()
-            {
-                @Override
-                public Writer newEntity( EntityReference ref, EntityDescriptor entityDescriptor )
-                    throws IOException
-                {
-                    return new StringWriter( 1000 )
-                    {
-                        @Override
-                        public void close()
-                            throws IOException
-                        {
-                            super.close();
-                            String statusCode = jedis.set( ref.identity().toString(), toString(), "NX" );
-                            if( !"OK".equals( statusCode ) )
-                            {
-                                throw new EntityAlreadyExistsException( ref );
-                            }
-                        }
-                    };
-                }
-
-                @Override
-                public Writer updateEntity( MapChange mapChange )
-                    throws IOException
-                {
-                    return new StringWriter( 1000 )
-                    {
-                        @Override
-                        public void close()
-                            throws IOException
-                        {
-                            super.close();
-                            String statusCode = jedis.set( mapChange.reference().identity().toString(),
-                                                           toString(),
-                                                           "XX" );
-                            if( !"OK".equals( statusCode ) )
-                            {
-                                throw new EntityNotFoundException( mapChange.reference() );
-                            }
-                        }
-                    };
-                }
-
-                @Override
-                public void removeEntity( EntityReference ref, EntityDescriptor entityDescriptor )
-                    throws EntityNotFoundException
-                {
-                    String jsonState = jedis.get( ref.identity().toString() );
-                    if( notFound( jsonState ) )
-                    {
-                        throw new EntityNotFoundException( ref );
-                    }
-                    jedis.del( ref.identity().toString() );
-                }
-            } );
-        }
-    }
-
-    @Override
-    public Stream<Reader> entityStates()
-    {
-        Jedis jedis = pool.getResource();
-        return jedis.keys( "*" ).stream()
-                    .map( key -> (Reader) new StringReader( jedis.get( key ) ) )
-                    .onClose( jedis::close );
-    }
-
-    private static boolean notFound( String jsonState )
-    {
-        return jsonState == null || NIL.equals( jsonState );
-    }
-}

http://git-wip-us.apache.org/repos/asf/polygene-java/blob/ea3f84a5/extensions/entitystore-redis/src/main/java/org/apache/polygene/entitystore/redis/RedisMapEntityStoreService.java
----------------------------------------------------------------------
diff --git a/extensions/entitystore-redis/src/main/java/org/apache/polygene/entitystore/redis/RedisMapEntityStoreService.java b/extensions/entitystore-redis/src/main/java/org/apache/polygene/entitystore/redis/RedisMapEntityStoreService.java
deleted file mode 100644
index 0ebaa52..0000000
--- a/extensions/entitystore-redis/src/main/java/org/apache/polygene/entitystore/redis/RedisMapEntityStoreService.java
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- *  Licensed to the Apache Software Foundation (ASF) under one
- *  or more contributor license agreements.  See the NOTICE file
- *  distributed with this work for additional information
- *  regarding copyright ownership.  The ASF licenses this file
- *  to you 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.apache.polygene.entitystore.redis;
-
-import org.apache.polygene.api.concern.Concerns;
-import org.apache.polygene.api.configuration.Configuration;
-import org.apache.polygene.api.mixin.Mixins;
-import org.apache.polygene.api.service.ServiceActivation;
-import org.apache.polygene.library.locking.LockingAbstractComposite;
-import org.apache.polygene.spi.entitystore.ConcurrentModificationCheckConcern;
-import org.apache.polygene.spi.entitystore.EntityStateVersions;
-import org.apache.polygene.spi.entitystore.EntityStore;
-import org.apache.polygene.spi.entitystore.StateChangeNotificationConcern;
-import org.apache.polygene.spi.entitystore.helpers.JSONMapEntityStoreActivation;
-import org.apache.polygene.spi.entitystore.helpers.JSONMapEntityStoreMixin;
-
-/**
- * Redis EntityStore service.
- * <p>Based on @{@link JSONMapEntityStoreMixin}.</p>
- */
-@Concerns( { StateChangeNotificationConcern.class, ConcurrentModificationCheckConcern.class } )
-@Mixins( { JSONMapEntityStoreMixin.class, RedisMapEntityStoreMixin.class } )
-public interface RedisMapEntityStoreService
-        extends EntityStore,
-                EntityStateVersions,
-                ServiceActivation,
-                JSONMapEntityStoreActivation,
-                LockingAbstractComposite,
-                Configuration,
-                RedisAccessors
-{
-}

http://git-wip-us.apache.org/repos/asf/polygene-java/blob/ea3f84a5/extensions/entitystore-redis/src/main/java/org/apache/polygene/entitystore/redis/assembly/RedisEntityStoreAssembler.java
----------------------------------------------------------------------
diff --git a/extensions/entitystore-redis/src/main/java/org/apache/polygene/entitystore/redis/assembly/RedisEntityStoreAssembler.java b/extensions/entitystore-redis/src/main/java/org/apache/polygene/entitystore/redis/assembly/RedisEntityStoreAssembler.java
index a0d8f04..1262e70 100644
--- a/extensions/entitystore-redis/src/main/java/org/apache/polygene/entitystore/redis/assembly/RedisEntityStoreAssembler.java
+++ b/extensions/entitystore-redis/src/main/java/org/apache/polygene/entitystore/redis/assembly/RedisEntityStoreAssembler.java
@@ -24,7 +24,7 @@ import org.apache.polygene.bootstrap.AssemblyException;
 import org.apache.polygene.bootstrap.ModuleAssembly;
 import org.apache.polygene.bootstrap.ServiceDeclaration;
 import org.apache.polygene.entitystore.redis.RedisEntityStoreConfiguration;
-import org.apache.polygene.entitystore.redis.RedisMapEntityStoreService;
+import org.apache.polygene.entitystore.redis.RedisEntityStoreService;
 
 /**
  * Redis EntityStore assembly.
@@ -36,7 +36,7 @@ public class RedisEntityStoreAssembler
     public void assemble( ModuleAssembly module )
         throws AssemblyException
     {
-        ServiceDeclaration service = module.services( RedisMapEntityStoreService.class ).visibleIn( visibility() );
+        ServiceDeclaration service = module.services( RedisEntityStoreService.class ).visibleIn( visibility() );
         if( hasIdentity() )
         {
             service.identifiedBy( identity() );

http://git-wip-us.apache.org/repos/asf/polygene-java/blob/ea3f84a5/extensions/entitystore-redis/src/test/java/org/apache/polygene/entitystore/redis/RedisEntityStoreTest.java
----------------------------------------------------------------------
diff --git a/extensions/entitystore-redis/src/test/java/org/apache/polygene/entitystore/redis/RedisEntityStoreTest.java b/extensions/entitystore-redis/src/test/java/org/apache/polygene/entitystore/redis/RedisEntityStoreTest.java
new file mode 100644
index 0000000..671bffa
--- /dev/null
+++ b/extensions/entitystore-redis/src/test/java/org/apache/polygene/entitystore/redis/RedisEntityStoreTest.java
@@ -0,0 +1,80 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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.apache.polygene.entitystore.redis;
+
+import org.apache.polygene.api.common.Visibility;
+import org.apache.polygene.bootstrap.AssemblyException;
+import org.apache.polygene.bootstrap.ModuleAssembly;
+import org.apache.polygene.entitystore.redis.assembly.RedisEntityStoreAssembler;
+import org.apache.polygene.test.EntityTestAssembler;
+import org.apache.polygene.test.entity.AbstractEntityStoreTest;
+import org.apache.polygene.test.internal.DockerRule;
+import org.junit.ClassRule;
+import redis.clients.jedis.Jedis;
+import redis.clients.jedis.JedisPool;
+
+public class RedisEntityStoreTest
+    extends AbstractEntityStoreTest
+{
+    @ClassRule
+    public static final DockerRule DOCKER = new DockerRule( "redis", 6379 );
+
+    @Override
+    // START SNIPPET: assembly
+    public void assemble( ModuleAssembly module )
+        throws AssemblyException
+    {
+        // END SNIPPET: assembly
+        super.assemble( module );
+        ModuleAssembly config = module.layer().module( "config" );
+        new EntityTestAssembler().assemble( config );
+        // START SNIPPET: assembly
+        new RedisEntityStoreAssembler().withConfig( config, Visibility.layer ).assemble( module );
+        // END SNIPPET: assembly
+        RedisEntityStoreConfiguration redisConfig = config.forMixin( RedisEntityStoreConfiguration.class )
+                                                          .declareDefaults();
+        redisConfig.host().set( DOCKER.getDockerHost() );
+        redisConfig.port().set( DOCKER.getExposedContainerPort( "6379/tcp" ) );
+        // START SNIPPET: assembly
+    }
+    // END SNIPPET: assembly
+
+    private JedisPool jedisPool;
+
+    @Override
+    public void setUp()
+        throws Exception
+    {
+        super.setUp();
+        RedisEntityStoreService es = serviceFinder.findService( RedisEntityStoreService.class ).get();
+        jedisPool = es.jedisPool();
+    }
+
+    @Override
+    public void tearDown()
+        throws Exception
+    {
+        try( Jedis jedis = jedisPool.getResource() )
+        {
+            jedis.flushDB();
+        }
+        super.tearDown();
+    }
+}

http://git-wip-us.apache.org/repos/asf/polygene-java/blob/ea3f84a5/extensions/entitystore-redis/src/test/java/org/apache/polygene/entitystore/redis/RedisEntityStoreWithCacheTest.java
----------------------------------------------------------------------
diff --git a/extensions/entitystore-redis/src/test/java/org/apache/polygene/entitystore/redis/RedisEntityStoreWithCacheTest.java b/extensions/entitystore-redis/src/test/java/org/apache/polygene/entitystore/redis/RedisEntityStoreWithCacheTest.java
new file mode 100644
index 0000000..7cd9cbe
--- /dev/null
+++ b/extensions/entitystore-redis/src/test/java/org/apache/polygene/entitystore/redis/RedisEntityStoreWithCacheTest.java
@@ -0,0 +1,74 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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.apache.polygene.entitystore.redis;
+
+import org.apache.polygene.api.common.Visibility;
+import org.apache.polygene.bootstrap.AssemblyException;
+import org.apache.polygene.bootstrap.ModuleAssembly;
+import org.apache.polygene.entitystore.redis.assembly.RedisEntityStoreAssembler;
+import org.apache.polygene.test.EntityTestAssembler;
+import org.apache.polygene.test.cache.AbstractEntityStoreWithCacheTest;
+import org.apache.polygene.test.internal.DockerRule;
+import org.junit.ClassRule;
+import redis.clients.jedis.Jedis;
+import redis.clients.jedis.JedisPool;
+
+public class RedisEntityStoreWithCacheTest
+    extends AbstractEntityStoreWithCacheTest
+{
+    @ClassRule
+    public static final DockerRule DOCKER = new DockerRule( "redis", 6379 );
+
+    @Override
+    public void assemble( ModuleAssembly module )
+        throws AssemblyException
+    {
+        super.assemble( module );
+        ModuleAssembly config = module.layer().module( "config" );
+        new EntityTestAssembler().assemble( config );
+        new RedisEntityStoreAssembler().withConfig( config, Visibility.layer ).assemble( module );
+        RedisEntityStoreConfiguration redisConfig = config.forMixin( RedisEntityStoreConfiguration.class )
+                                                          .declareDefaults();
+        redisConfig.host().set( DOCKER.getDockerHost() );
+        redisConfig.port().set( DOCKER.getExposedContainerPort( "6379/tcp" ) );
+    }
+
+    private JedisPool jedisPool;
+
+    @Override
+    public void setUp()
+        throws Exception
+    {
+        super.setUp();
+        RedisEntityStoreService es = serviceFinder.findService( RedisEntityStoreService.class ).get();
+        jedisPool = es.jedisPool();
+    }
+
+    @Override
+    public void tearDown()
+        throws Exception
+    {
+        try( Jedis jedis = jedisPool.getResource() )
+        {
+            jedis.flushDB();
+        }
+        super.tearDown();
+    }
+}

http://git-wip-us.apache.org/repos/asf/polygene-java/blob/ea3f84a5/extensions/entitystore-redis/src/test/java/org/apache/polygene/entitystore/redis/RedisMapEntityStoreTest.java
----------------------------------------------------------------------
diff --git a/extensions/entitystore-redis/src/test/java/org/apache/polygene/entitystore/redis/RedisMapEntityStoreTest.java b/extensions/entitystore-redis/src/test/java/org/apache/polygene/entitystore/redis/RedisMapEntityStoreTest.java
deleted file mode 100644
index e654afc..0000000
--- a/extensions/entitystore-redis/src/test/java/org/apache/polygene/entitystore/redis/RedisMapEntityStoreTest.java
+++ /dev/null
@@ -1,80 +0,0 @@
-/*
- *  Licensed to the Apache Software Foundation (ASF) under one
- *  or more contributor license agreements.  See the NOTICE file
- *  distributed with this work for additional information
- *  regarding copyright ownership.  The ASF licenses this file
- *  to you 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.apache.polygene.entitystore.redis;
-
-import org.apache.polygene.api.common.Visibility;
-import org.apache.polygene.bootstrap.AssemblyException;
-import org.apache.polygene.bootstrap.ModuleAssembly;
-import org.apache.polygene.entitystore.redis.assembly.RedisEntityStoreAssembler;
-import org.apache.polygene.test.EntityTestAssembler;
-import org.apache.polygene.test.entity.AbstractEntityStoreTest;
-import org.apache.polygene.test.internal.DockerRule;
-import org.junit.ClassRule;
-import redis.clients.jedis.Jedis;
-import redis.clients.jedis.JedisPool;
-
-public class RedisMapEntityStoreTest
-    extends AbstractEntityStoreTest
-{
-    @ClassRule
-    public static final DockerRule DOCKER = new DockerRule( "redis", 6379 );
-
-    @Override
-    // START SNIPPET: assembly
-    public void assemble( ModuleAssembly module )
-        throws AssemblyException
-    {
-        // END SNIPPET: assembly
-        super.assemble( module );
-        ModuleAssembly config = module.layer().module( "config" );
-        new EntityTestAssembler().assemble( config );
-        // START SNIPPET: assembly
-        new RedisEntityStoreAssembler().withConfig( config, Visibility.layer ).assemble( module );
-        // END SNIPPET: assembly
-        RedisEntityStoreConfiguration redisConfig = config.forMixin( RedisEntityStoreConfiguration.class )
-                                                          .declareDefaults();
-        redisConfig.host().set( DOCKER.getDockerHost() );
-        redisConfig.port().set( DOCKER.getExposedContainerPort( "6379/tcp" ) );
-        // START SNIPPET: assembly
-    }
-    // END SNIPPET: assembly
-
-    private JedisPool jedisPool;
-
-    @Override
-    public void setUp()
-        throws Exception
-    {
-        super.setUp();
-        RedisMapEntityStoreService es = serviceFinder.findService( RedisMapEntityStoreService.class ).get();
-        jedisPool = es.jedisPool();
-    }
-
-    @Override
-    public void tearDown()
-        throws Exception
-    {
-        try( Jedis jedis = jedisPool.getResource() )
-        {
-            jedis.flushDB();
-        }
-        super.tearDown();
-    }
-}

http://git-wip-us.apache.org/repos/asf/polygene-java/blob/ea3f84a5/extensions/entitystore-redis/src/test/java/org/apache/polygene/entitystore/redis/RedisMapEntityStoreWithCacheTest.java
----------------------------------------------------------------------
diff --git a/extensions/entitystore-redis/src/test/java/org/apache/polygene/entitystore/redis/RedisMapEntityStoreWithCacheTest.java b/extensions/entitystore-redis/src/test/java/org/apache/polygene/entitystore/redis/RedisMapEntityStoreWithCacheTest.java
deleted file mode 100644
index 1dba76a..0000000
--- a/extensions/entitystore-redis/src/test/java/org/apache/polygene/entitystore/redis/RedisMapEntityStoreWithCacheTest.java
+++ /dev/null
@@ -1,74 +0,0 @@
-/*
- *  Licensed to the Apache Software Foundation (ASF) under one
- *  or more contributor license agreements.  See the NOTICE file
- *  distributed with this work for additional information
- *  regarding copyright ownership.  The ASF licenses this file
- *  to you 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.apache.polygene.entitystore.redis;
-
-import org.apache.polygene.api.common.Visibility;
-import org.apache.polygene.bootstrap.AssemblyException;
-import org.apache.polygene.bootstrap.ModuleAssembly;
-import org.apache.polygene.entitystore.redis.assembly.RedisEntityStoreAssembler;
-import org.apache.polygene.test.EntityTestAssembler;
-import org.apache.polygene.test.cache.AbstractEntityStoreWithCacheTest;
-import org.apache.polygene.test.internal.DockerRule;
-import org.junit.ClassRule;
-import redis.clients.jedis.Jedis;
-import redis.clients.jedis.JedisPool;
-
-public class RedisMapEntityStoreWithCacheTest
-    extends AbstractEntityStoreWithCacheTest
-{
-    @ClassRule
-    public static final DockerRule DOCKER = new DockerRule( "redis", 6379 );
-
-    @Override
-    public void assemble( ModuleAssembly module )
-        throws AssemblyException
-    {
-        super.assemble( module );
-        ModuleAssembly config = module.layer().module( "config" );
-        new EntityTestAssembler().assemble( config );
-        new RedisEntityStoreAssembler().withConfig( config, Visibility.layer ).assemble( module );
-        RedisEntityStoreConfiguration redisConfig = config.forMixin( RedisEntityStoreConfiguration.class )
-                                                          .declareDefaults();
-        redisConfig.host().set( DOCKER.getDockerHost() );
-        redisConfig.port().set( DOCKER.getExposedContainerPort( "6379/tcp" ) );
-    }
-
-    private JedisPool jedisPool;
-
-    @Override
-    public void setUp()
-        throws Exception
-    {
-        super.setUp();
-        RedisMapEntityStoreService es = serviceFinder.findService( RedisMapEntityStoreService.class ).get();
-        jedisPool = es.jedisPool();
-    }
-
-    @Override
-    public void tearDown()
-        throws Exception
-    {
-        try( Jedis jedis = jedisPool.getResource() )
-        {
-            jedis.flushDB();
-        }
-        super.tearDown();
-    }
-}

http://git-wip-us.apache.org/repos/asf/polygene-java/blob/ea3f84a5/extensions/entitystore-riak/src/main/java/org/apache/polygene/entitystore/riak/RiakEntityStoreMixin.java
----------------------------------------------------------------------
diff --git a/extensions/entitystore-riak/src/main/java/org/apache/polygene/entitystore/riak/RiakEntityStoreMixin.java b/extensions/entitystore-riak/src/main/java/org/apache/polygene/entitystore/riak/RiakEntityStoreMixin.java
new file mode 100644
index 0000000..39fd13a
--- /dev/null
+++ b/extensions/entitystore-riak/src/main/java/org/apache/polygene/entitystore/riak/RiakEntityStoreMixin.java
@@ -0,0 +1,384 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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.apache.polygene.entitystore.riak;
+
+import com.basho.riak.client.api.RiakClient;
+import com.basho.riak.client.api.commands.buckets.StoreBucketProperties;
+import com.basho.riak.client.api.commands.kv.DeleteValue;
+import com.basho.riak.client.api.commands.kv.FetchValue;
+import com.basho.riak.client.api.commands.kv.ListKeys;
+import com.basho.riak.client.api.commands.kv.StoreValue;
+import com.basho.riak.client.core.RiakCluster;
+import com.basho.riak.client.core.RiakNode;
+import com.basho.riak.client.core.query.Location;
+import com.basho.riak.client.core.query.Namespace;
+import com.basho.riak.client.core.util.HostAndPort;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.Reader;
+import java.io.StringReader;
+import java.io.StringWriter;
+import java.io.Writer;
+import java.security.GeneralSecurityException;
+import java.security.KeyStore;
+import java.security.Provider;
+import java.security.Security;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+import java.util.concurrent.ExecutionException;
+import java.util.stream.Stream;
+import java.util.stream.StreamSupport;
+import org.apache.polygene.api.common.InvalidApplicationException;
+import org.apache.polygene.api.configuration.Configuration;
+import org.apache.polygene.api.entity.EntityDescriptor;
+import org.apache.polygene.api.entity.EntityReference;
+import org.apache.polygene.api.injection.scope.This;
+import org.apache.polygene.api.service.ServiceActivation;
+import org.apache.polygene.spi.entitystore.EntityNotFoundException;
+import org.apache.polygene.spi.entitystore.EntityStoreException;
+import org.apache.polygene.spi.entitystore.helpers.MapEntityStore;
+
+/**
+ * Riak Protobuf implementation of MapEntityStore.
+ */
+public class RiakEntityStoreMixin implements ServiceActivation, MapEntityStore, RiakAccessors
+{
+    private static final String DEFAULT_HOST = "127.0.0.1";
+    private static final int DEFAULT_PORT = 8087;
+
+    @This
+    private Configuration<RiakEntityStoreConfiguration> configuration;
+
+    private RiakClient riakClient;
+    private Namespace namespace;
+
+    @Override
+    public void activateService() throws Exception
+    {
+        // Load configuration
+        configuration.refresh();
+        RiakEntityStoreConfiguration config = configuration.get();
+        String bucketName = config.bucket().get();
+        List<String> hosts = config.hosts().get();
+
+        // Setup Riak Cluster Client
+        List<HostAndPort> hostsAndPorts = parseHosts( hosts );
+        RiakNode.Builder nodeBuilder = new RiakNode.Builder();
+        nodeBuilder = configureNodes( config, nodeBuilder );
+        nodeBuilder = configureAuthentication( config, nodeBuilder );
+        List<RiakNode> nodes = new ArrayList<>();
+        for( HostAndPort host : hostsAndPorts )
+        {
+            nodes.add( nodeBuilder.withRemoteAddress( host ).build() );
+        }
+        RiakCluster.Builder clusterBuilder = RiakCluster.builder( nodes );
+        clusterBuilder = configureCluster( config, clusterBuilder );
+
+        // Start Riak Cluster
+        RiakCluster cluster = clusterBuilder.build();
+        cluster.start();
+        namespace = new Namespace( bucketName );
+        riakClient = new RiakClient( cluster );
+
+        // Initialize Bucket
+        riakClient.execute( new StoreBucketProperties.Builder( namespace ).build() );
+    }
+
+    private RiakNode.Builder configureNodes( RiakEntityStoreConfiguration config, RiakNode.Builder nodeBuilder )
+    {
+        Integer minConnections = config.minConnections().get();
+        Integer maxConnections = config.maxConnections().get();
+        Boolean blockOnMaxConnections = config.blockOnMaxConnections().get();
+        Integer connectionTimeout = config.connectionTimeout().get();
+        Integer idleTimeout = config.idleTimeout().get();
+        if( minConnections != null )
+        {
+            nodeBuilder = nodeBuilder.withMinConnections( minConnections );
+        }
+        if( maxConnections != null )
+        {
+            nodeBuilder = nodeBuilder.withMaxConnections( maxConnections );
+        }
+        nodeBuilder = nodeBuilder.withBlockOnMaxConnections( blockOnMaxConnections );
+        if( connectionTimeout != null )
+        {
+            nodeBuilder = nodeBuilder.withConnectionTimeout( connectionTimeout );
+        }
+        if( idleTimeout != null )
+        {
+            nodeBuilder = nodeBuilder.withIdleTimeout( idleTimeout );
+        }
+        return nodeBuilder;
+    }
+
+    private RiakNode.Builder configureAuthentication( RiakEntityStoreConfiguration config,
+                                                      RiakNode.Builder nodeBuilder )
+        throws IOException, GeneralSecurityException
+    {
+        String username = config.username().get();
+        String password = config.password().get();
+        String truststoreType = config.truststoreType().get();
+        String truststorePath = config.truststorePath().get();
+        String truststorePassword = config.truststorePassword().get();
+        String keystoreType = config.keystoreType().get();
+        String keystorePath = config.keystorePath().get();
+        String keystorePassword = config.keystorePassword().get();
+        String keyPassword = config.keyPassword().get();
+        if( username != null )
+        {
+            // Eventually load BouncyCastle to support PKCS12
+            if( "PKCS12".equals( keystoreType ) || "PKCS12".equals( truststoreType ) )
+            {
+                Provider bc = Security.getProvider( "BC" );
+                if( bc == null )
+                {
+                    try
+                    {
+                        Class<?> bcType = Class.forName( "org.bouncycastle.jce.provider.BouncyCastleProvider" );
+                        Security.addProvider( (Provider) bcType.newInstance() );
+                    }
+                    catch( Exception ex )
+                    {
+                        throw new InvalidApplicationException(
+                            "Need to open a PKCS#12 but unable to register BouncyCastle, check your classpath", ex );
+                    }
+                }
+            }
+            KeyStore truststore = loadStore( truststoreType, truststorePath, truststorePassword );
+            if( keystorePath != null )
+            {
+                KeyStore keyStore = loadStore( keystoreType, keystorePath, keystorePassword );
+                nodeBuilder = nodeBuilder.withAuth( username, password, truststore, keyStore, keyPassword );
+            }
+            else
+            {
+                nodeBuilder = nodeBuilder.withAuth( username, password, truststore );
+            }
+        }
+        return nodeBuilder;
+    }
+
+    private KeyStore loadStore( String type, String path, String password )
+        throws IOException, GeneralSecurityException
+    {
+        try( InputStream keystoreInput = new FileInputStream( new File( path ) ) )
+        {
+            KeyStore keyStore = KeyStore.getInstance( type );
+            keyStore.load( keystoreInput, password.toCharArray() );
+            return keyStore;
+        }
+    }
+
+    private RiakCluster.Builder configureCluster( RiakEntityStoreConfiguration config,
+                                                  RiakCluster.Builder clusterBuilder )
+    {
+        Integer clusterExecutionAttempts = config.clusterExecutionAttempts().get();
+        if( clusterExecutionAttempts != null )
+        {
+            clusterBuilder = clusterBuilder.withExecutionAttempts( clusterExecutionAttempts );
+        }
+        return clusterBuilder;
+    }
+
+    @Override
+    public void passivateService() throws Exception
+    {
+        riakClient.shutdown();
+        riakClient = null;
+        namespace = null;
+    }
+
+    @Override
+    public RiakClient riakClient()
+    {
+        return riakClient;
+    }
+
+    @Override
+    public Namespace riakNamespace()
+    {
+        return namespace;
+    }
+
+    @Override
+    public Reader get( EntityReference entityReference )
+    {
+        try
+        {
+            Location location = new Location( namespace, entityReference.identity().toString() );
+            FetchValue fetch = new FetchValue.Builder( location ).build();
+            FetchValue.Response response = riakClient.execute( fetch );
+            if( response.isNotFound() )
+            {
+                throw new EntityNotFoundException( entityReference );
+            }
+            String jsonState = response.getValue( String.class );
+            return new StringReader( jsonState );
+        }
+        catch( InterruptedException | ExecutionException ex )
+        {
+            throw new EntityStoreException( "Unable to get Entity " + entityReference.identity(), ex );
+        }
+    }
+
+    @Override
+    public void applyChanges( MapChanges changes )
+    {
+        try
+        {
+            changes.visitMap( new MapChanger()
+            {
+                @Override
+                public Writer newEntity( EntityReference ref, EntityDescriptor entityDescriptor )
+                {
+                    return new StringWriter( 1000 )
+                    {
+                        @Override
+                        public void close() throws IOException
+                        {
+                            try
+                            {
+                                super.close();
+                                StoreValue store = new StoreValue.Builder( toString() )
+                                    .withLocation( new Location( namespace, ref.identity().toString() ) )
+                                    .build();
+                                riakClient.execute( store );
+                            }
+                            catch( InterruptedException | ExecutionException ex )
+                            {
+                                throw new EntityStoreException( "Unable to apply entity change: newEntity", ex );
+                            }
+                        }
+                    };
+                }
+
+                @Override
+                public Writer updateEntity( MapChange mapChange )
+                {
+                    return new StringWriter( 1000 )
+                    {
+                        @Override
+                        public void close() throws IOException
+                        {
+                            try
+                            {
+                                super.close();
+                                EntityReference reference = mapChange.reference();
+                                Location location = new Location( namespace, reference.identity().toString() );
+                                FetchValue fetch = new FetchValue.Builder( location ).build();
+                                FetchValue.Response response = riakClient.execute( fetch );
+                                if( response.isNotFound() )
+                                {
+                                    throw new EntityNotFoundException( reference );
+                                }
+                                StoreValue store = new StoreValue.Builder( toString() )
+                                    .withLocation( location )
+                                    .build();
+                                riakClient.execute( store );
+                            }
+                            catch( InterruptedException | ExecutionException ex )
+                            {
+                                throw new EntityStoreException( "Unable to apply entity change: updateEntity", ex );
+                            }
+                        }
+                    };
+                }
+
+                @Override
+                public void removeEntity( EntityReference ref, EntityDescriptor entityDescriptor )
+                {
+                    try
+                    {
+                        Location location = new Location( namespace, ref.identity().toString() );
+                        FetchValue fetch = new FetchValue.Builder( location ).build();
+                        FetchValue.Response response = riakClient.execute( fetch );
+                        if( response.isNotFound() )
+                        {
+                            throw new EntityNotFoundException( ref );
+                        }
+                        DeleteValue delete = new DeleteValue.Builder( location ).build();
+                        riakClient.execute( delete );
+                    }
+                    catch( InterruptedException | ExecutionException ex )
+                    {
+                        throw new EntityStoreException( "Unable to apply entity change: removeEntity", ex );
+                    }
+                }
+            } );
+        }
+        catch( Exception ex )
+        {
+            throw new EntityStoreException( "Unable to apply entity changes.", ex );
+        }
+    }
+
+    @Override
+    public Stream<Reader> entityStates()
+    {
+        try
+        {
+            ListKeys listKeys = new ListKeys.Builder( namespace ).build();
+            ListKeys.Response listKeysResponse = riakClient.execute( listKeys );
+            return StreamSupport
+                .stream( listKeysResponse.spliterator(), false )
+                .map( location ->
+                      {
+                          try
+                          {
+                              FetchValue fetch = new FetchValue.Builder( location ).build();
+                              FetchValue.Response response = riakClient.execute( fetch );
+                              return response.getValue( String.class );
+                          }
+                          catch( InterruptedException | ExecutionException ex )
+                          {
+                              throw new EntityStoreException( "Unable to get entity states.", ex );
+                          }
+                      } )
+                .filter( Objects::nonNull )
+                .map( StringReader::new );
+        }
+        catch( InterruptedException | ExecutionException ex )
+        {
+            throw new EntityStoreException( "Unable to get entity states.", ex );
+        }
+    }
+
+    private List<HostAndPort> parseHosts( List<String> hosts )
+    {
+        if( hosts.isEmpty() )
+        {
+            hosts.add( DEFAULT_HOST );
+        }
+        List<HostAndPort> addresses = new ArrayList<>( hosts.size() );
+        for( String host : hosts )
+        {
+            String[] splitted = host.split( ":" );
+            int port = DEFAULT_PORT;
+            if( splitted.length > 1 )
+            {
+                host = splitted[ 0 ];
+                port = Integer.valueOf( splitted[ 1 ] );
+            }
+            addresses.add( HostAndPort.fromParts( host, port ) );
+        }
+        return addresses;
+    }
+}


Mime
View raw message