Initial Zookeeper Entity Store done.
Project: http://git-wip-us.apache.org/repos/asf/polygene-java/repo Commit: http://git-wip-us.apache.org/repos/asf/polygene-java/commit/68b55fa9 Tree: http://git-wip-us.apache.org/repos/asf/polygene-java/tree/68b55fa9 Diff: http://git-wip-us.apache.org/repos/asf/polygene-java/diff/68b55fa9 Branch: refs/heads/develop Commit: 68b55fa9b6950275a7ec2397cd89c40414526014 Parents: c68ad9a Author: niclas <nic...@hedhman.org> Authored: Thu Apr 26 08:30:44 2018 +0800 Committer: niclas <nic...@hedhman.org> Committed: Thu Apr 26 08:30:44 2018 +0800 ---------------------------------------------------------------------- dependencies.gradle | 3 + .../entitystore/riak/RiakEntityStoreMixin.java | 3 +- extensions/entitystore-zookeeper/build.gradle | 41 +++ extensions/entitystore-zookeeper/dev-status.xml | 38 +++ .../src/docs/es-zookeeper.txt | 53 +++ .../ZookeeperEntityStoreConfiguration.java | 89 +++++ .../zookeeper/ZookeeperEntityStoreMixin.java | 336 +++++++++++++++++++ .../zookeeper/ZookeeperEntityStoreService.java | 52 +++ .../assembly/ZookeeperEntityStoreAssembler.java | 50 +++ .../polygene/entitystore/zookeeper/package.html | 24 ++ .../polygene/entitystore/zookeeper/ZkUtil.java | 33 ++ .../zookeeper/ZookeeperEntityStoreTest.java | 67 ++++ .../ZookeeperEntityStoreTestSuite.java | 62 ++++ .../ZookeeperEntityStoreWithCacheTest.java | 60 ++++ settings.gradle | 1 + 15 files changed, 911 insertions(+), 1 deletion(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/polygene-java/blob/68b55fa9/dependencies.gradle ---------------------------------------------------------------------- diff --git a/dependencies.gradle b/dependencies.gradle index e2aad56..6bf9405 100644 --- a/dependencies.gradle +++ b/dependencies.gradle @@ -83,6 +83,8 @@ def solrVersion = "1.4.1" // 4.x Fails to compile! def springVersion = '5.0.5.RELEASE' def spymemcachedVersion = '2.12.3' def velocityVersion = '1.7' +def zookeeperVersion = '3.4.10' + dependencies.libraries << [ bdb_je : "com.sleepycat:je:$bdbjeVersion", bonecp : "com.jolbox:bonecp:$bonecpVersion", @@ -171,6 +173,7 @@ dependencies.libraries << [ "org.springframework:spring-context:$springVersion"], spymemcached : "net.spy:spymemcached:$spymemcachedVersion", velocity : "org.apache.velocity:velocity:$velocityVersion", + zookeeper : "org.apache.zookeeper:zookeeper:$zookeeperVersion" ] // Tools dependencies http://git-wip-us.apache.org/repos/asf/polygene-java/blob/68b55fa9/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 index 39fd13a..7981318 100644 --- 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 @@ -282,7 +282,8 @@ public class RiakEntityStoreMixin implements ServiceActivation, MapEntityStore, { super.close(); EntityReference reference = mapChange.reference(); - Location location = new Location( namespace, reference.identity().toString() ); + String identity = reference.identity().toString(); + Location location = new Location( namespace, identity ); FetchValue fetch = new FetchValue.Builder( location ).build(); FetchValue.Response response = riakClient.execute( fetch ); if( response.isNotFound() ) http://git-wip-us.apache.org/repos/asf/polygene-java/blob/68b55fa9/extensions/entitystore-zookeeper/build.gradle ---------------------------------------------------------------------- diff --git a/extensions/entitystore-zookeeper/build.gradle b/extensions/entitystore-zookeeper/build.gradle new file mode 100644 index 0000000..b8c6430 --- /dev/null +++ b/extensions/entitystore-zookeeper/build.gradle @@ -0,0 +1,41 @@ +/* + * 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. + * + * + */ + +apply plugin: 'polygene-extension' + +description = "Apache Polygene⢠Zookeeper EntityStore Extension. NOTE: Meant for configuration and other very light loads." + +jar { manifest { name = "Apache Polygene⢠Extension - EntityStore - Zookeeper" } } + +dependencies { + api polygene.core.bootstrap + + implementation polygene.library( 'locking' ) + implementation polygene.library( 'constraints' ) + implementation libraries.zookeeper + + runtimeOnly polygene.core.runtime + + testImplementation polygene.core.testsupport + testImplementation libraries.awaitility + testImplementation libraries.docker_junit + + testRuntimeOnly libraries.logback +} http://git-wip-us.apache.org/repos/asf/polygene-java/blob/68b55fa9/extensions/entitystore-zookeeper/dev-status.xml ---------------------------------------------------------------------- diff --git a/extensions/entitystore-zookeeper/dev-status.xml b/extensions/entitystore-zookeeper/dev-status.xml new file mode 100644 index 0000000..c79727f --- /dev/null +++ b/extensions/entitystore-zookeeper/dev-status.xml @@ -0,0 +1,38 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<!-- + ~ 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. + ~ + ~ + --> +<module xmlns="http://polygene.apache.org/schemas/2008/dev-status/1" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://polygene.apache.org/schemas/2008/dev-status/1 + http://polygene.apache.org/schemas/2008/dev-status/1/dev-status.xsd"> + <status> + <!--none,early,beta,stable,mature--> + <codebase>beta</codebase> + + <!-- none, brief, good, complete --> + <documentation>brief</documentation> + + <!-- none, some, good, complete --> + <unittests>good</unittests> + </status> + <licenses> + <license>ALv2</license> + </licenses> +</module> http://git-wip-us.apache.org/repos/asf/polygene-java/blob/68b55fa9/extensions/entitystore-zookeeper/src/docs/es-zookeeper.txt ---------------------------------------------------------------------- diff --git a/extensions/entitystore-zookeeper/src/docs/es-zookeeper.txt b/extensions/entitystore-zookeeper/src/docs/es-zookeeper.txt new file mode 100644 index 0000000..18253b9 --- /dev/null +++ b/extensions/entitystore-zookeeper/src/docs/es-zookeeper.txt @@ -0,0 +1,53 @@ +/////////////////////////////////////////////////////////////// + * 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. +/////////////////////////////////////////////////////////////// + +[[extension-es-zookeeper, Zookeeper EntityStore]] += Zookeeper EntityStore = + +[devstatus] +-------------- +source=extensions/entitystore-sqlkv/dev-status.xml +-------------- + +EntityStore service backed by a Zookeeper cluster. All entities are stored under a configurable ZNode, by default +/polygene/store. + +Note that Zookeeper is not intended for heavy loads, and this implementation is primarily for Configurations and +reference data that is primarily read-only in nature. + +include::../../build/docs/buildinfo/artifact.txt[] + +== Configuration == + +Here are the available configuration properties: + +[snippet,java] +---- +source=extensions/entitystore-zookeeper/src/main/java/org/apache/polygene/entitystore/zookeeper/ZookeeperEntityStoreConfiguration.java +tag=config +---- + +Assembly is done using the provided Assembler: + +[snippet,java] +---- +source=extensions/entitystore-zookeeper/src/test/java/org/apache/polygene/entitystore/zookeeper/ZookeeperEntityStoreTest.java +tag=assembly +---- + http://git-wip-us.apache.org/repos/asf/polygene-java/blob/68b55fa9/extensions/entitystore-zookeeper/src/main/java/org/apache/polygene/entitystore/zookeeper/ZookeeperEntityStoreConfiguration.java ---------------------------------------------------------------------- diff --git a/extensions/entitystore-zookeeper/src/main/java/org/apache/polygene/entitystore/zookeeper/ZookeeperEntityStoreConfiguration.java b/extensions/entitystore-zookeeper/src/main/java/org/apache/polygene/entitystore/zookeeper/ZookeeperEntityStoreConfiguration.java new file mode 100644 index 0000000..f7ffdbf --- /dev/null +++ b/extensions/entitystore-zookeeper/src/main/java/org/apache/polygene/entitystore/zookeeper/ZookeeperEntityStoreConfiguration.java @@ -0,0 +1,89 @@ +/* + * 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.zookeeper; + +import java.util.List; +import org.apache.polygene.api.common.Optional; +import org.apache.polygene.api.common.UseDefaults; +import org.apache.polygene.api.property.Property; + +/** + * Configuration for ZookeeperEntityStoreService. + */ +// START SNIPPET: config +public interface ZookeeperEntityStoreConfiguration +{ + /** + * List of Zookeeper hosts + * <p> + * Each entry must contain an IP address / hostname followed by a column and the host's port. + * <p> + * Defaulted to 127.0.0.1:2181 if empty. + * + * @return List of Zookeeper hosts + */ + @UseDefaults + Property<List<String>> hosts(); + + /** + * Path/Node in Zookeeper's namespace where Entities state will be stored. + * <p> + * Defaulted to "/polygene/store". + * + * @return The path of the the node where the entities will be stored. + */ + @UseDefaults( "/polygene/store" ) + Property<String> storageNode(); + + /** Timeout of Session in milliseconds + */ + @UseDefaults( "10000" ) + Property<Integer> sessionTimeout(); + + /** + * ACL to be used for new znodes. + * <p> + * Each String is in the format of; PERM, SCHEME, ID + * </p> + * <p> + * where <strong>PERM</strong> is an integer by adding together + * <ul> + * <li>1 = <code>READ</code> </li> + * <li>2 = <code>WRITE</code> </li> + * <li>4 = <code>CREATE</code> </li> + * <li>8 = <code>DELETE</code> </li> + * <li>16 = <code>ADMIN</code> </li> + * </ul> + * or 31 for <code>ALL</code>, which is also the default value. + * + * </p> + *<p> + * <strong>SCHEME</strong> is the zookeeper ACL scheme, one of "world", "auth", ...(?)... + * <br/> + * Default: "world" + *</p> + * <p> + * ID is the identity within the SCHEME. For "world" SCHEME, "anyone" is wildcard as can be expected. + * + * Default: "anyone" + * </p> + */ + @Optional + Property<List<String>> acls(); +} +// END SNIPPET: config http://git-wip-us.apache.org/repos/asf/polygene-java/blob/68b55fa9/extensions/entitystore-zookeeper/src/main/java/org/apache/polygene/entitystore/zookeeper/ZookeeperEntityStoreMixin.java ---------------------------------------------------------------------- diff --git a/extensions/entitystore-zookeeper/src/main/java/org/apache/polygene/entitystore/zookeeper/ZookeeperEntityStoreMixin.java b/extensions/entitystore-zookeeper/src/main/java/org/apache/polygene/entitystore/zookeeper/ZookeeperEntityStoreMixin.java new file mode 100644 index 0000000..59a0b5a --- /dev/null +++ b/extensions/entitystore-zookeeper/src/main/java/org/apache/polygene/entitystore/zookeeper/ZookeeperEntityStoreMixin.java @@ -0,0 +1,336 @@ +/* + * 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.zookeeper; + +import java.io.IOException; +import java.io.Reader; +import java.io.StringReader; +import java.io.StringWriter; +import java.io.Writer; +import java.lang.reflect.UndeclaredThrowableException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; +import java.util.Spliterators; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.stream.Stream; +import java.util.stream.StreamSupport; +import org.apache.polygene.api.common.Optional; +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.Service; +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.apache.zookeeper.KeeperException; +import org.apache.zookeeper.WatchedEvent; +import org.apache.zookeeper.Watcher; +import org.apache.zookeeper.ZooKeeper; +import org.apache.zookeeper.data.ACL; +import org.apache.zookeeper.data.Id; +import org.apache.zookeeper.data.Stat; + +import static java.util.Spliterator.DISTINCT; +import static java.util.Spliterator.IMMUTABLE; +import static java.util.Spliterator.NONNULL; +import static org.apache.zookeeper.CreateMode.PERSISTENT; +import static org.apache.zookeeper.ZooDefs.Ids.ANYONE_ID_UNSAFE; +import static org.apache.zookeeper.ZooDefs.Perms.ALL; + +/** + * Zookeeper implementation of MapEntityStore. + */ +public class ZookeeperEntityStoreMixin + implements ServiceActivation, MapEntityStore +{ + private static final String DEFAULT_HOSTPORT = "localhost:2181"; + private static final byte[] EMPTY_DATA = new byte[ 0 ]; + + @Service + @Optional + private Watcher watcher; + + @This + private Configuration<ZookeeperEntityStoreConfiguration> configuration; + private ZooKeeper zkClient; + private String storageNode; + private List<ACL> acl; + + @Override + public void activateService() + throws Exception + { + // Load configuration + configuration.refresh(); + ZookeeperEntityStoreConfiguration config = configuration.get(); + setupStorageNode( config ); + + List<String> hosts = config.hosts().get(); + if( hosts == null || hosts.size() == 0 ) + { + hosts = Collections.singletonList( DEFAULT_HOSTPORT ); + } + String hostPort = hosts.get( (int) ( Math.random() * hosts.size() ) ); + + int sessionTimeout = config.sessionTimeout().get(); + + zkClient = new ZooKeeper( hostPort, sessionTimeout, watcher == null ? new DummyWatcher() : watcher ); + createStorageNodeIfNotExists( config ); + } + + private void setupStorageNode( ZookeeperEntityStoreConfiguration config ) + { + storageNode = config.storageNode().get(); + while( storageNode.startsWith( "//" ) ) + { + storageNode = storageNode.substring( 1 ); + } + while( storageNode.endsWith( "/" ) ) + { + storageNode = storageNode.substring( 0, storageNode.length() - 1 ); + } + if( !storageNode.startsWith( "/" ) ) + { + storageNode = "/" + storageNode; + } + } + + private void createStorageNodeIfNotExists( ZookeeperEntityStoreConfiguration config ) + throws KeeperException, InterruptedException + { + acl = parseAcls( config.acls().get() ); + String nodeName = config.storageNode().get(); + String[] parts = nodeName.split( "/" ); + String current = ""; + for( String part : parts ) + { + if( part.length() > 0 ) + { + current = current + "/" + part; + Stat stat = zkClient.exists( current, false ); + if( stat == null ) + { + zkClient.create( current, EMPTY_DATA, acl, PERSISTENT ); + } + } + } + } + + private List<ACL> parseAcls( List<String> acls ) + { + List<ACL> result = new ArrayList<>(); + if( acls == null || acls.size() == 0 ) + { + result.add( new ACL( ALL, ANYONE_ID_UNSAFE ) ); + return result; + } + acls.forEach( s -> { + String[] parts = s.split( "," ); + int perms = Integer.valueOf( parts[ 0 ] ); + String id = parts[ 2 ]; + String scheme = parts[ 1 ]; + result.add( new ACL( perms, new Id( scheme, id ) ) ); + } ); + return result; + } + + @Override + public void passivateService() + throws Exception + { + zkClient.close(); + } + + @Override + public Reader get( EntityReference reference ) + { + try + { + Stat stat = new Stat(); + byte[] data = zkClient.getData( znode( reference ), false, stat ); + if( data == null || stat.getDataLength() == 0 ) + { + throw new EntityNotFoundException( reference ); + } + return new StringReader( new String( data ) ); + } + catch( KeeperException.NoNodeException e ) + { + throw new EntityNotFoundException( reference ); + } + catch( InterruptedException | KeeperException e ) + { + throw new EntityStoreException( "Unable to get Entity " + reference.identity(), e ); + } + } + + @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(); + String znode = znode( ref ); + byte[] data = toString().getBytes(); + zkClient.create( znode, data, acl, PERSISTENT ); + } + catch( InterruptedException | KeeperException e ) + { + throw new EntityStoreException( "Unable to apply entity change: newEntity", e ); + } + } + }; + } + + @Override + public Writer updateEntity( MapChange mapChange ) + { + return new StringWriter( 1000 ) + { + @Override + public void close() + throws IOException + { + try + { + super.close(); + EntityReference ref = mapChange.reference(); + String znode = znode( ref ); + Stat stat = zkClient.exists( znode, false ); + if( stat == null ) + { + throw new EntityNotFoundException( ref ); + } + int version = stat.getVersion(); + zkClient.setData( znode, toString().getBytes(), version ); + } + catch( InterruptedException | KeeperException e ) + { + throw new EntityStoreException( "Unable to apply entity change: updateEntity", e ); + } + } + }; + } + + @Override + public void removeEntity( EntityReference ref, EntityDescriptor entityDescriptor ) + { + try + { + String znode = znode( ref ); + Stat stat = zkClient.exists( znode, false ); + int version = stat.getVersion(); + zkClient.delete( znode, version ); + } + catch( InterruptedException | KeeperException e ) + { + throw new EntityStoreException( "Unable to apply entity change: removeEntity", e ); + } + } + } ); + } + catch( Exception ex ) + { + throw new EntityStoreException( "Unable to apply entity changes.", ex ); + } + } + + private String znode( EntityReference ref ) + { + return storageNode + '/' + ref.identity().toString(); + } + + @Override + public Stream<Reader> entityStates() + { + try + { + List<String> children = zkClient.getChildren( storageNode, false ); + final ItemIterator iterator = new ItemIterator(); + for( String child : children ) + { + Stat stat = new Stat(); + String path = storageNode + "/" + child; + byte[] data = zkClient.getData( path, false, stat ); + if( data.length > 0 ) + { + String json = new String( data ); + iterator.queue.offer( new StringReader( json ) ); + } + } + return StreamSupport.stream( Spliterators.spliterator( iterator, DISTINCT | NONNULL | IMMUTABLE, 1 ), false ); + } + catch( KeeperException | InterruptedException e ) + { + throw new EntityStoreException( "Unable to get entity states.", e ); + } + } + + private class ItemIterator + implements Iterator<Reader> + { + private final BlockingQueue<Reader> queue = new LinkedBlockingQueue<>(); + + @Override + public boolean hasNext() + { + return queue.size() > 0; + } + + @Override + public Reader next() + { + try + { + return queue.take(); + } + catch( InterruptedException e ) + { + throw new UndeclaredThrowableException( e ); + } + } + } + + private class DummyWatcher + implements Watcher + { + @Override + public void process( WatchedEvent event ) + { + // ignore all + } + } +} http://git-wip-us.apache.org/repos/asf/polygene-java/blob/68b55fa9/extensions/entitystore-zookeeper/src/main/java/org/apache/polygene/entitystore/zookeeper/ZookeeperEntityStoreService.java ---------------------------------------------------------------------- diff --git a/extensions/entitystore-zookeeper/src/main/java/org/apache/polygene/entitystore/zookeeper/ZookeeperEntityStoreService.java b/extensions/entitystore-zookeeper/src/main/java/org/apache/polygene/entitystore/zookeeper/ZookeeperEntityStoreService.java new file mode 100644 index 0000000..80cb2a5 --- /dev/null +++ b/extensions/entitystore-zookeeper/src/main/java/org/apache/polygene/entitystore/zookeeper/ZookeeperEntityStoreService.java @@ -0,0 +1,52 @@ +/* + * 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.zookeeper; + +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; +import org.apache.polygene.spi.entitystore.helpers.MapEntityStoreActivation; +import org.apache.polygene.spi.entitystore.helpers.MapEntityStoreMixin; + +/** + * Riak EntityStore service. + * <p>Can be used with Riak implementations of MapEntityStore.</p> + * <p>Based on {@link JSONMapEntityStoreMixin}</p> + */ +@Concerns( { StateChangeNotificationConcern.class, ConcurrentModificationCheckConcern.class } ) +@Mixins( { JSONMapEntityStoreMixin.class, MapEntityStoreMixin.class } ) +public interface ZookeeperEntityStoreService + extends EntityStore, + EntityStateVersions, + MapEntityStoreActivation, + JSONMapEntityStoreActivation, + ServiceActivation, + LockingAbstractComposite, + Configuration +{ +} http://git-wip-us.apache.org/repos/asf/polygene-java/blob/68b55fa9/extensions/entitystore-zookeeper/src/main/java/org/apache/polygene/entitystore/zookeeper/assembly/ZookeeperEntityStoreAssembler.java ---------------------------------------------------------------------- diff --git a/extensions/entitystore-zookeeper/src/main/java/org/apache/polygene/entitystore/zookeeper/assembly/ZookeeperEntityStoreAssembler.java b/extensions/entitystore-zookeeper/src/main/java/org/apache/polygene/entitystore/zookeeper/assembly/ZookeeperEntityStoreAssembler.java new file mode 100644 index 0000000..664513c --- /dev/null +++ b/extensions/entitystore-zookeeper/src/main/java/org/apache/polygene/entitystore/zookeeper/assembly/ZookeeperEntityStoreAssembler.java @@ -0,0 +1,50 @@ +/* + * 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.zookeeper.assembly; + +import org.apache.polygene.bootstrap.Assemblers; +import org.apache.polygene.bootstrap.ModuleAssembly; +import org.apache.polygene.bootstrap.ServiceDeclaration; +import org.apache.polygene.entitystore.zookeeper.ZookeeperEntityStoreConfiguration; +import org.apache.polygene.entitystore.zookeeper.ZookeeperEntityStoreMixin; +import org.apache.polygene.entitystore.zookeeper.ZookeeperEntityStoreService; + +/** + * Zookeeper EntityStore assembly. + */ +public class ZookeeperEntityStoreAssembler + extends Assemblers.VisibilityIdentityConfig<ZookeeperEntityStoreAssembler> +{ + @Override + public void assemble( ModuleAssembly module ) + { + super.assemble( module ); + ServiceDeclaration service = module.services( ZookeeperEntityStoreService.class ) + .withMixins( ZookeeperEntityStoreMixin.class ) + .visibleIn( visibility() ); + if( hasIdentity() ) + { + service.identifiedBy( identity() ); + } + if( hasConfig() ) + { + configModule().entities( ZookeeperEntityStoreConfiguration.class ) + .visibleIn( configVisibility() ); + } + } +} http://git-wip-us.apache.org/repos/asf/polygene-java/blob/68b55fa9/extensions/entitystore-zookeeper/src/main/java/org/apache/polygene/entitystore/zookeeper/package.html ---------------------------------------------------------------------- diff --git a/extensions/entitystore-zookeeper/src/main/java/org/apache/polygene/entitystore/zookeeper/package.html b/extensions/entitystore-zookeeper/src/main/java/org/apache/polygene/entitystore/zookeeper/package.html new file mode 100644 index 0000000..142a770 --- /dev/null +++ b/extensions/entitystore-zookeeper/src/main/java/org/apache/polygene/entitystore/zookeeper/package.html @@ -0,0 +1,24 @@ +<!-- + ~ 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. + ~ + ~ + --> +<html> + <body> + <h2>Zookeeper EntityStore.</h2> + </body> +</html> http://git-wip-us.apache.org/repos/asf/polygene-java/blob/68b55fa9/extensions/entitystore-zookeeper/src/test/java/org/apache/polygene/entitystore/zookeeper/ZkUtil.java ---------------------------------------------------------------------- diff --git a/extensions/entitystore-zookeeper/src/test/java/org/apache/polygene/entitystore/zookeeper/ZkUtil.java b/extensions/entitystore-zookeeper/src/test/java/org/apache/polygene/entitystore/zookeeper/ZkUtil.java new file mode 100644 index 0000000..8b08892 --- /dev/null +++ b/extensions/entitystore-zookeeper/src/test/java/org/apache/polygene/entitystore/zookeeper/ZkUtil.java @@ -0,0 +1,33 @@ +package org.apache.polygene.entitystore.zookeeper; + +import java.io.IOException; +import java.util.List; +import org.apache.zookeeper.KeeperException; +import org.apache.zookeeper.ZooKeeper; +import org.apache.zookeeper.data.Stat; + +public class ZkUtil +{ + static void cleanUp( String host, String znodeName ) + throws KeeperException, InterruptedException, IOException + { + ZooKeeper zk = new ZooKeeper( host, 10000, event -> { + } ); + delete( zk, znodeName ); + } + + private static void delete( ZooKeeper zk, String znodeName ) + throws KeeperException, InterruptedException + { + Stat stat = zk.exists( znodeName, false ); + if( stat != null ) + { + List<String> children = zk.getChildren( znodeName, false ); + for( String child : children ) + { + delete( zk, znodeName + "/" + child ); + } + zk.delete( znodeName, stat.getVersion() ); + } + } +} http://git-wip-us.apache.org/repos/asf/polygene-java/blob/68b55fa9/extensions/entitystore-zookeeper/src/test/java/org/apache/polygene/entitystore/zookeeper/ZookeeperEntityStoreTest.java ---------------------------------------------------------------------- diff --git a/extensions/entitystore-zookeeper/src/test/java/org/apache/polygene/entitystore/zookeeper/ZookeeperEntityStoreTest.java b/extensions/entitystore-zookeeper/src/test/java/org/apache/polygene/entitystore/zookeeper/ZookeeperEntityStoreTest.java new file mode 100644 index 0000000..bf5bc99 --- /dev/null +++ b/extensions/entitystore-zookeeper/src/test/java/org/apache/polygene/entitystore/zookeeper/ZookeeperEntityStoreTest.java @@ -0,0 +1,67 @@ +/* + * 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.zookeeper; + +import org.apache.polygene.api.common.Visibility; +import org.apache.polygene.bootstrap.AssemblyException; +import org.apache.polygene.bootstrap.ModuleAssembly; +import org.apache.polygene.entitystore.zookeeper.assembly.ZookeeperEntityStoreAssembler; +import org.apache.polygene.test.EntityTestAssembler; +import org.apache.polygene.test.TemporaryFolder; +import org.apache.polygene.test.entity.AbstractEntityStoreTest; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.extension.ExtendWith; + +import static java.util.Collections.singletonList; + +@ExtendWith( TemporaryFolder.class ) +public class ZookeeperEntityStoreTest + extends AbstractEntityStoreTest +{ + + static final String TEST_ZNODE_NAME = "/polygene/entitystore-test"; + + @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().defaultServicesVisibleIn( Visibility.layer ).assemble( config ); + // START SNIPPET: assembly + ZookeeperEntityStoreAssembler zkAssembler = new ZookeeperEntityStoreAssembler(); + zkAssembler.withConfig( config, Visibility.layer ).assemble( module ); + // END SNIPPET: assembly + ZookeeperEntityStoreConfiguration defaults = zkAssembler.configModule().forMixin( ZookeeperEntityStoreConfiguration.class ).declareDefaults(); + defaults.hosts().set( singletonList( "localhost:2181" ) ); + defaults.storageNode().set( TEST_ZNODE_NAME ); + // START SNIPPET: assembly + } + // END SNIPPET: assembly + + @AfterEach + void cleanUp() + throws Exception + { + ZkUtil.cleanUp( "localhost:2181", TEST_ZNODE_NAME ); + } +} http://git-wip-us.apache.org/repos/asf/polygene-java/blob/68b55fa9/extensions/entitystore-zookeeper/src/test/java/org/apache/polygene/entitystore/zookeeper/ZookeeperEntityStoreTestSuite.java ---------------------------------------------------------------------- diff --git a/extensions/entitystore-zookeeper/src/test/java/org/apache/polygene/entitystore/zookeeper/ZookeeperEntityStoreTestSuite.java b/extensions/entitystore-zookeeper/src/test/java/org/apache/polygene/entitystore/zookeeper/ZookeeperEntityStoreTestSuite.java new file mode 100644 index 0000000..bf83f26 --- /dev/null +++ b/extensions/entitystore-zookeeper/src/test/java/org/apache/polygene/entitystore/zookeeper/ZookeeperEntityStoreTestSuite.java @@ -0,0 +1,62 @@ +/* + * 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.zookeeper; + +import org.apache.polygene.api.common.Visibility; +import org.apache.polygene.bootstrap.ModuleAssembly; +import org.apache.polygene.entitystore.zookeeper.assembly.ZookeeperEntityStoreAssembler; +import org.apache.polygene.test.TemporaryFolder; +import org.apache.polygene.test.entity.model.EntityStoreTestSuite; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.extension.ExtendWith; + +import static java.util.Collections.singletonList; +import static org.apache.polygene.entitystore.zookeeper.ZookeeperEntityStoreTest.TEST_ZNODE_NAME; + +@ExtendWith( TemporaryFolder.class ) +public class ZookeeperEntityStoreTestSuite + extends EntityStoreTestSuite +{ + @Override + protected void defineStorageModule( ModuleAssembly module ) + { + module.defaultServices(); + new ZookeeperEntityStoreAssembler() + .withConfig( configModule, Visibility.application ) + .visibleIn( Visibility.application ) + .assemble( module ); + } + + @Override + protected void defineConfigModule( ModuleAssembly module ) + { + super.defineConfigModule( module ); + ZookeeperEntityStoreConfiguration defaults = module.forMixin( ZookeeperEntityStoreConfiguration.class ).declareDefaults(); + defaults.hosts().set( singletonList( "localhost:2181" ) ); + defaults.storageNode().set( TEST_ZNODE_NAME ); + } + + @AfterEach + void cleanUp() + throws Exception + { + ZkUtil.cleanUp( "localhost:2181", TEST_ZNODE_NAME ); + } +} http://git-wip-us.apache.org/repos/asf/polygene-java/blob/68b55fa9/extensions/entitystore-zookeeper/src/test/java/org/apache/polygene/entitystore/zookeeper/ZookeeperEntityStoreWithCacheTest.java ---------------------------------------------------------------------- diff --git a/extensions/entitystore-zookeeper/src/test/java/org/apache/polygene/entitystore/zookeeper/ZookeeperEntityStoreWithCacheTest.java b/extensions/entitystore-zookeeper/src/test/java/org/apache/polygene/entitystore/zookeeper/ZookeeperEntityStoreWithCacheTest.java new file mode 100644 index 0000000..1efaf05 --- /dev/null +++ b/extensions/entitystore-zookeeper/src/test/java/org/apache/polygene/entitystore/zookeeper/ZookeeperEntityStoreWithCacheTest.java @@ -0,0 +1,60 @@ +/* + * 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.zookeeper; + +import org.apache.polygene.api.common.Visibility; +import org.apache.polygene.bootstrap.AssemblyException; +import org.apache.polygene.bootstrap.ModuleAssembly; +import org.apache.polygene.entitystore.zookeeper.assembly.ZookeeperEntityStoreAssembler; +import org.apache.polygene.test.EntityTestAssembler; +import org.apache.polygene.test.TemporaryFolder; +import org.apache.polygene.test.cache.AbstractEntityStoreWithCacheTest; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.extension.ExtendWith; + +import static java.util.Collections.singletonList; +import static org.apache.polygene.entitystore.zookeeper.ZookeeperEntityStoreTest.TEST_ZNODE_NAME; + +public class ZookeeperEntityStoreWithCacheTest + extends AbstractEntityStoreWithCacheTest +{ + @Override + public void assemble( ModuleAssembly module ) + throws AssemblyException + { + super.assemble( module ); + ModuleAssembly config = module.layer().module( "config" ); + new EntityTestAssembler().defaultServicesVisibleIn( Visibility.layer ).assemble( config ); + ZookeeperEntityStoreAssembler zkAssembler = new ZookeeperEntityStoreAssembler(); + zkAssembler.withConfig( config, Visibility.layer ).assemble( module ); + + ZookeeperEntityStoreConfiguration defaults = zkAssembler.configModule().forMixin( ZookeeperEntityStoreConfiguration.class ).declareDefaults(); + defaults.hosts().set( singletonList( "localhost:2181" ) ); + defaults.storageNode().set( TEST_ZNODE_NAME ); + } + + @AfterEach + void cleanUp() + throws Exception + { + ZkUtil.cleanUp( "localhost:2181", TEST_ZNODE_NAME ); + } + +} http://git-wip-us.apache.org/repos/asf/polygene-java/blob/68b55fa9/settings.gradle ---------------------------------------------------------------------- diff --git a/settings.gradle b/settings.gradle index e410c6a..0f503e1 100644 --- a/settings.gradle +++ b/settings.gradle @@ -69,6 +69,7 @@ include 'core:api', 'extensions:entitystore-riak', 'extensions:entitystore-sql', 'extensions:entitystore-sqlkv', + 'extensions:entitystore-zookeeper', 'extensions:indexing-elasticsearch', 'extensions:indexing-rdf', 'extensions:indexing-solr',