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