Repository: curator Updated Branches: refs/heads/CURATOR-397 [created] 8237fde0a
WIP - strongly type DSL for Curator Project: http://git-wip-us.apache.org/repos/asf/curator/repo Commit: http://git-wip-us.apache.org/repos/asf/curator/commit/8237fde0 Tree: http://git-wip-us.apache.org/repos/asf/curator/tree/8237fde0 Diff: http://git-wip-us.apache.org/repos/asf/curator/diff/8237fde0 Branch: refs/heads/CURATOR-397 Commit: 8237fde0aabf7088b6ddcaf56a7fba1f8b1d3441 Parents: 191aca2 Author: randgalt <[email protected]> Authored: Sat Apr 8 13:52:06 2017 -0500 Committer: randgalt <[email protected]> Committed: Sat Apr 8 13:52:06 2017 -0500 ---------------------------------------------------------------------- curator-x-async/pom.xml | 6 + .../org/apache/curator/x/async/AsyncStage.java | 15 +- .../x/async/modeled/JacksonModelSerializer.java | 99 ++++++++++ .../x/async/modeled/ModelSerializer.java | 26 +++ .../modeled/ModeledAsyncCuratorFramework.java | 66 +++++++ .../ModeledAsyncCuratorFrameworkBuilder.java | 129 +++++++++++++ .../apache/curator/x/async/modeled/ZPath.java | 35 ++++ .../x/async/modeled/details/ModelStage.java | 26 +++ .../ModeledAsyncCuratorFrameworkImpl.java | 186 +++++++++++++++++++ .../x/async/modeled/details/ZPathImpl.java | 132 +++++++++++++ .../x/async/CompletableBaseClassForTests.java | 65 +++++++ .../curator/x/async/TestBasicOperations.java | 35 +--- .../TestModeledAsyncCuratorFramework.java | 102 ++++++++++ .../curator/x/async/modeled/TestZPath.java | 39 ++++ .../x/async/modeled/models/TestModel.java | 115 ++++++++++++ .../x/async/modeled/models/TestNewerModel.java | 137 ++++++++++++++ pom.xml | 2 +- 17 files changed, 1177 insertions(+), 38 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/curator/blob/8237fde0/curator-x-async/pom.xml ---------------------------------------------------------------------- diff --git a/curator-x-async/pom.xml b/curator-x-async/pom.xml index a7fdcd5..fa73942 100644 --- a/curator-x-async/pom.xml +++ b/curator-x-async/pom.xml @@ -20,6 +20,12 @@ </dependency> <dependency> + <groupId>com.fasterxml.jackson.core</groupId> + <artifactId>jackson-databind</artifactId> + <scope>provided</scope> + </dependency> + + <dependency> <groupId>org.apache.curator</groupId> <artifactId>curator-test</artifactId> <scope>test</scope> http://git-wip-us.apache.org/repos/asf/curator/blob/8237fde0/curator-x-async/src/main/java/org/apache/curator/x/async/AsyncStage.java ---------------------------------------------------------------------- diff --git a/curator-x-async/src/main/java/org/apache/curator/x/async/AsyncStage.java b/curator-x-async/src/main/java/org/apache/curator/x/async/AsyncStage.java index 30ed234..9b63309 100644 --- a/curator-x-async/src/main/java/org/apache/curator/x/async/AsyncStage.java +++ b/curator-x-async/src/main/java/org/apache/curator/x/async/AsyncStage.java @@ -27,9 +27,18 @@ import java.util.concurrent.CompletionStage; public interface AsyncStage<T> extends CompletionStage<T> { /** - * If the {@link org.apache.curator.x.async.api.WatchableAsyncCuratorFramework} facade is - * used (via {@link AsyncCuratorFramework#watched()}), this returns the completion - * stage used when the watcher is triggered + * <p> + * If the {@link org.apache.curator.x.async.api.WatchableAsyncCuratorFramework} facade is + * used (via {@link AsyncCuratorFramework#watched()}), this returns the completion + * stage used when the watcher is triggered + * </p> + * + * <p> + * Also, applies to {@link org.apache.curator.x.async.modeled.ModeledAsyncCuratorFramework} + * when {@link org.apache.curator.x.async.modeled.ModeledAsyncCuratorFrameworkBuilder#watched(WatchMode)} + * or {@link org.apache.curator.x.async.modeled.ModeledAsyncCuratorFrameworkBuilder#watched(WatchMode, java.util.function.UnaryOperator)} + * is used. + * </p> * * @return CompletionStage for the set watcher or <code>null</code> */ http://git-wip-us.apache.org/repos/asf/curator/blob/8237fde0/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/JacksonModelSerializer.java ---------------------------------------------------------------------- diff --git a/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/JacksonModelSerializer.java b/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/JacksonModelSerializer.java new file mode 100644 index 0000000..5fb6e07 --- /dev/null +++ b/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/JacksonModelSerializer.java @@ -0,0 +1,99 @@ +/** + * 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.curator.x.async.modeled; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.JavaType; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.ObjectReader; +import com.fasterxml.jackson.databind.ObjectWriter; +import java.io.IOException; +import java.util.Arrays; + +public class JacksonModelSerializer<T> implements ModelSerializer<T> +{ + private static final ObjectMapper mapper = new ObjectMapper(); + static + { + mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); + } + + private final ObjectReader reader; + private final ObjectWriter writer; + + public static <T> JacksonModelSerializer<T> build(Class<T> modelClass) + { + return new JacksonModelSerializer<>(modelClass); + } + + public static <T> JacksonModelSerializer<T> build(JavaType type) + { + return new JacksonModelSerializer<>(type); + } + + public static <T> JacksonModelSerializer<T> build(TypeReference type) + { + return new JacksonModelSerializer<>(type); + } + + public JacksonModelSerializer(Class<T> modelClass) + { + this(mapper.getTypeFactory().constructType(modelClass)); + } + + public JacksonModelSerializer(JavaType type) + { + reader = mapper.readerFor(type); + writer = mapper.writerFor(type); + } + + public JacksonModelSerializer(TypeReference type) + { + reader = mapper.readerFor(type); + writer = mapper.writerFor(type); + } + + @Override + public byte[] serialize(T model) + { + try + { + return writer.writeValueAsBytes(model); + } + catch ( JsonProcessingException e ) + { + throw new RuntimeException(String.format("Could not serialize value: %s", model), e); + } + } + + @Override + public T deserialize(byte[] bytes) + { + try + { + return reader.readValue(bytes); + } + catch ( IOException e ) + { + throw new RuntimeException(String.format("Could not deserialize value: %s", Arrays.toString(bytes)), e); + } + } +} http://git-wip-us.apache.org/repos/asf/curator/blob/8237fde0/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/ModelSerializer.java ---------------------------------------------------------------------- diff --git a/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/ModelSerializer.java b/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/ModelSerializer.java new file mode 100644 index 0000000..64defdf --- /dev/null +++ b/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/ModelSerializer.java @@ -0,0 +1,26 @@ +/** + * 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.curator.x.async.modeled; + +public interface ModelSerializer<T> +{ + byte[] serialize(T model); + + T deserialize(byte[] bytes); +} http://git-wip-us.apache.org/repos/asf/curator/blob/8237fde0/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/ModeledAsyncCuratorFramework.java ---------------------------------------------------------------------- diff --git a/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/ModeledAsyncCuratorFramework.java b/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/ModeledAsyncCuratorFramework.java new file mode 100644 index 0000000..fdcc259 --- /dev/null +++ b/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/ModeledAsyncCuratorFramework.java @@ -0,0 +1,66 @@ +/** + * 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.curator.x.async.modeled; + +import com.google.common.collect.ImmutableSet; +import org.apache.curator.framework.CuratorFramework; +import org.apache.curator.x.async.AsyncStage; +import org.apache.curator.x.async.api.CreateOption; +import org.apache.curator.x.async.api.DeleteOption; +import org.apache.zookeeper.data.Stat; + +public interface ModeledAsyncCuratorFramework<T> +{ + ImmutableSet<CreateOption> defaultCreateOptions = ImmutableSet.of(CreateOption.createParentsAsContainers, CreateOption.setDataIfExists); + ImmutableSet<DeleteOption> defaultDeleteOptions = ImmutableSet.of(DeleteOption.guaranteed); + + static <T> ModeledAsyncCuratorFramework<T> wrap(CuratorFramework client, ZPath path, ModelSerializer<T> serializer) + { + return builder(client, path, serializer).build(); + } + + static <T> ModeledAsyncCuratorFrameworkBuilder<T> builder(CuratorFramework client, ZPath path, ModelSerializer<T> serializer) + { + return new ModeledAsyncCuratorFrameworkBuilder<>(client, path, serializer) + .withCreateOptions(defaultCreateOptions) + .withDeleteOptions(defaultDeleteOptions); + } + + CuratorFramework unwrap(); + + ModeledAsyncCuratorFramework<T> at(String child); + + AsyncStage<String> create(T model); + + AsyncStage<String> create(T model, Stat storingStatIn); + + AsyncStage<T> read(); + + AsyncStage<T> read(Stat storingStatIn); + + AsyncStage<Stat> update(T model); + + AsyncStage<Stat> update(T model, int version); + + AsyncStage<Stat> checkExists(); + + AsyncStage<Void> delete(); + + AsyncStage<Void> delete(int version); +} http://git-wip-us.apache.org/repos/asf/curator/blob/8237fde0/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/ModeledAsyncCuratorFrameworkBuilder.java ---------------------------------------------------------------------- diff --git a/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/ModeledAsyncCuratorFrameworkBuilder.java b/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/ModeledAsyncCuratorFrameworkBuilder.java new file mode 100644 index 0000000..b328220 --- /dev/null +++ b/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/ModeledAsyncCuratorFrameworkBuilder.java @@ -0,0 +1,129 @@ +/** + * 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.curator.x.async.modeled; + +import com.google.common.collect.ImmutableSet; +import org.apache.curator.framework.CuratorFramework; +import org.apache.curator.framework.api.CuratorEvent; +import org.apache.curator.framework.api.UnhandledErrorListener; +import org.apache.curator.x.async.WatchMode; +import org.apache.curator.x.async.api.CreateOption; +import org.apache.curator.x.async.api.DeleteOption; +import org.apache.curator.x.async.modeled.details.ModeledAsyncCuratorFrameworkImpl; +import org.apache.zookeeper.CreateMode; +import org.apache.zookeeper.WatchedEvent; +import org.apache.zookeeper.data.ACL; +import java.util.List; +import java.util.Objects; +import java.util.Set; +import java.util.function.UnaryOperator; + +public class ModeledAsyncCuratorFrameworkBuilder<T> +{ + private final CuratorFramework client; + private final ZPath path; + private final ModelSerializer<T> serializer; + private WatchMode watchMode; + private UnaryOperator<WatchedEvent> watcherFilter; + private UnhandledErrorListener unhandledErrorListener; + private UnaryOperator<CuratorEvent> resultFilter; + private CreateMode createMode; + private List<ACL> aclList; + private Set<CreateOption> createOptions; + private Set<DeleteOption> deleteOptions; + + public ModeledAsyncCuratorFramework<T> build() + { + return new ModeledAsyncCuratorFrameworkImpl<>( + client, + path.fullPath(), + serializer, + watchMode, + watcherFilter, + unhandledErrorListener, + resultFilter, + createMode, + aclList, + createOptions, + deleteOptions + ); + } + + public ModeledAsyncCuratorFrameworkBuilder<T> watched() + { + this.watchMode = WatchMode.stateChangeAndSuccess; + return this; + } + + public ModeledAsyncCuratorFrameworkBuilder<T> watched(WatchMode watchMode) + { + this.watchMode = watchMode; + return this; + } + + public ModeledAsyncCuratorFrameworkBuilder<T> watched(WatchMode watchMode, UnaryOperator<WatchedEvent> watcherFilter) + { + this.watchMode = watchMode; + this.watcherFilter = watcherFilter; + return this; + } + + public ModeledAsyncCuratorFrameworkBuilder<T> withUnhandledErrorListener(UnhandledErrorListener unhandledErrorListener) + { + this.unhandledErrorListener = unhandledErrorListener; + return this; + } + + public ModeledAsyncCuratorFrameworkBuilder<T> withResultFilter(UnaryOperator<CuratorEvent> resultFilter) + { + this.resultFilter = resultFilter; + return this; + } + + public ModeledAsyncCuratorFrameworkBuilder<T> withCreateMode(CreateMode createMode) + { + this.createMode = createMode; + return this; + } + + public ModeledAsyncCuratorFrameworkBuilder<T> withAclList(List<ACL> aclList) + { + this.aclList = aclList; + return this; + } + + public ModeledAsyncCuratorFrameworkBuilder<T> withCreateOptions(Set<CreateOption> createOptions) + { + this.createOptions = (createOptions != null) ? ImmutableSet.copyOf(createOptions) : null; + return this; + } + + public ModeledAsyncCuratorFrameworkBuilder<T> withDeleteOptions(Set<DeleteOption> deleteOptions) + { + this.deleteOptions = (deleteOptions != null) ? ImmutableSet.copyOf(deleteOptions) : null; + return this; + } + + ModeledAsyncCuratorFrameworkBuilder(CuratorFramework client, ZPath path, ModelSerializer<T> serializer) + { + this.client = Objects.requireNonNull(client, "client cannot be null"); + this.path = Objects.requireNonNull(path, "path cannot be null"); + this.serializer = Objects.requireNonNull(serializer, "serializer cannot be null"); + } +} http://git-wip-us.apache.org/repos/asf/curator/blob/8237fde0/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/ZPath.java ---------------------------------------------------------------------- diff --git a/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/ZPath.java b/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/ZPath.java new file mode 100644 index 0000000..7e24341 --- /dev/null +++ b/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/ZPath.java @@ -0,0 +1,35 @@ +package org.apache.curator.x.async.modeled; + +import org.apache.curator.x.async.modeled.details.ZPathImpl; + +public interface ZPath +{ + ZPath root = ZPathImpl.root; + + static ZPath parse(String fullPath) + { + return ZPathImpl.parse(fullPath); + } + + static ZPath from(String... names) + { + ZPath path = root; + for ( String n : names ) + { + path = path.at(n); + } + return path; + } + + ZPath at(String child); + + ZPath parent(); + + boolean isRoot(); + + String fullPath(); + + String parentPath(); + + String nodeName(); +} http://git-wip-us.apache.org/repos/asf/curator/blob/8237fde0/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/details/ModelStage.java ---------------------------------------------------------------------- diff --git a/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/details/ModelStage.java b/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/details/ModelStage.java new file mode 100644 index 0000000..077bbb0 --- /dev/null +++ b/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/details/ModelStage.java @@ -0,0 +1,26 @@ +/** + * 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.curator.x.async.modeled.details; + +import org.apache.curator.x.async.AsyncStage; +import java.util.concurrent.CompletableFuture; + +abstract class ModelStage<T> extends CompletableFuture<T> implements AsyncStage<T> +{ +} http://git-wip-us.apache.org/repos/asf/curator/blob/8237fde0/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/details/ModeledAsyncCuratorFrameworkImpl.java ---------------------------------------------------------------------- diff --git a/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/details/ModeledAsyncCuratorFrameworkImpl.java b/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/details/ModeledAsyncCuratorFrameworkImpl.java new file mode 100644 index 0000000..40a3be8 --- /dev/null +++ b/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/details/ModeledAsyncCuratorFrameworkImpl.java @@ -0,0 +1,186 @@ +/** + * 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.curator.x.async.modeled.details; + +import com.google.common.collect.ImmutableSet; +import org.apache.curator.framework.CuratorFramework; +import org.apache.curator.framework.api.CuratorEvent; +import org.apache.curator.framework.api.UnhandledErrorListener; +import org.apache.curator.utils.ZKPaths; +import org.apache.curator.x.async.AsyncCuratorFramework; +import org.apache.curator.x.async.AsyncStage; +import org.apache.curator.x.async.WatchMode; +import org.apache.curator.x.async.api.AsyncCuratorFrameworkDsl; +import org.apache.curator.x.async.api.AsyncPathAndBytesable; +import org.apache.curator.x.async.api.AsyncPathable; +import org.apache.curator.x.async.api.CreateOption; +import org.apache.curator.x.async.api.DeleteOption; +import org.apache.curator.x.async.api.WatchableAsyncCuratorFramework; +import org.apache.curator.x.async.modeled.ModelSerializer; +import org.apache.curator.x.async.modeled.ModeledAsyncCuratorFramework; +import org.apache.zookeeper.CreateMode; +import org.apache.zookeeper.WatchedEvent; +import org.apache.zookeeper.data.ACL; +import org.apache.zookeeper.data.Stat; +import java.util.Collections; +import java.util.List; +import java.util.Objects; +import java.util.Set; +import java.util.concurrent.CompletionStage; +import java.util.function.UnaryOperator; + +public class ModeledAsyncCuratorFrameworkImpl<T> implements ModeledAsyncCuratorFramework<T> +{ + private final AsyncCuratorFramework client; + private final WatchableAsyncCuratorFramework watchableClient; + private final String path; + private final ModelSerializer<T> serializer; + private final WatchMode watchMode; + private final UnaryOperator<WatchedEvent> watcherFilter; + private final UnhandledErrorListener unhandledErrorListener; + private final UnaryOperator<CuratorEvent> resultFilter; + private final CreateMode createMode; + private final List<ACL> aclList; + private final Set<CreateOption> createOptions; + private final Set<DeleteOption> deleteOptions; + private final AsyncCuratorFrameworkDsl dslClient; + + public ModeledAsyncCuratorFrameworkImpl(CuratorFramework client, String path, ModelSerializer<T> serializer, WatchMode watchMode, UnaryOperator<WatchedEvent> watcherFilter, UnhandledErrorListener unhandledErrorListener, UnaryOperator<CuratorEvent> resultFilter, CreateMode createMode, List<ACL> aclList, Set<CreateOption> createOptions, Set<DeleteOption> deleteOptions) + { + boolean localIsWatched = (watchMode != null); + + this.client = AsyncCuratorFramework.wrap(client); + this.path = Objects.requireNonNull(path, "path cannot be null"); + this.serializer = Objects.requireNonNull(serializer, "serializer cannot be null"); + this.watchMode = (watchMode != null) ? watchMode : WatchMode.stateChangeAndSuccess; + this.watcherFilter = watcherFilter; + this.unhandledErrorListener = unhandledErrorListener; + this.resultFilter = resultFilter; + this.createMode = (createMode != null) ? createMode : CreateMode.PERSISTENT; + this.aclList = aclList; + this.createOptions = (createOptions != null) ? ImmutableSet.copyOf(createOptions) : Collections.emptySet(); + this.deleteOptions = (deleteOptions != null) ? ImmutableSet.copyOf(deleteOptions) : Collections.emptySet(); + + dslClient = this.client.with(this.watchMode, unhandledErrorListener, resultFilter, watcherFilter); + watchableClient = localIsWatched ? dslClient.watched() : dslClient; + } + + @Override + public CuratorFramework unwrap() + { + return client.unwrap(); + } + + @Override + public AsyncStage<String> create(T model) + { + return create(model, null); + } + + @Override + public AsyncStage<String> create(T model, Stat storingStatIn) + { + byte[] bytes = serializer.serialize(model); + return dslClient.create().withOptions(createOptions, createMode, aclList, storingStatIn).forPath(path, bytes); + } + + @Override + public AsyncStage<T> read() + { + return read(null); + } + + @Override + public AsyncStage<T> read(Stat storingStatIn) + { + AsyncPathable<AsyncStage<byte[]>> next; + if ( isCompressed() ) + { + next = (storingStatIn != null) ? watchableClient.getData().decompressedStoringStatIn(storingStatIn) : watchableClient.getData().decompressed(); + } + else + { + next = (storingStatIn != null) ? watchableClient.getData().storingStatIn(storingStatIn) : watchableClient.getData(); + } + AsyncStage<byte[]> asyncStage = next.forPath(path); + ModelStage<T> modelStage = new ModelStage<T>() + { + @Override + public CompletionStage<WatchedEvent> event() + { + return asyncStage.event(); + } + }; + asyncStage.whenComplete((value, e) -> { + if ( e != null ) + { + modelStage.completeExceptionally(e); + } + else + { + modelStage.complete(serializer.deserialize(value)); + } + }); + return modelStage; + } + + @Override + public AsyncStage<Stat> update(T model) + { + return update(model, -1); + } + + @Override + public AsyncStage<Stat> update(T model, int version) + { + byte[] bytes = serializer.serialize(model); + AsyncPathAndBytesable<AsyncStage<Stat>> next = isCompressed() ? dslClient.setData().compressedWithVersion(version) : dslClient.setData(); + return next.forPath(path, bytes); + } + + @Override + public AsyncStage<Stat> checkExists() + { + return watchableClient.checkExists().forPath(path); + } + + @Override + public AsyncStage<Void> delete() + { + return delete(-1); + } + + @Override + public AsyncStage<Void> delete(int version) + { + return dslClient.delete().withVersion(-1).forPath(path); + } + + @Override + public ModeledAsyncCuratorFramework<T> at(String child) + { + String childPath = ZKPaths.makePath(path, child); + return new ModeledAsyncCuratorFrameworkImpl<>(client.unwrap(), childPath, serializer, watchMode, watcherFilter, unhandledErrorListener, resultFilter, createMode, aclList, createOptions, deleteOptions); + } + + private boolean isCompressed() + { + return createOptions.contains(CreateOption.compress); + } +} http://git-wip-us.apache.org/repos/asf/curator/blob/8237fde0/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/details/ZPathImpl.java ---------------------------------------------------------------------- diff --git a/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/details/ZPathImpl.java b/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/details/ZPathImpl.java new file mode 100644 index 0000000..06a15ad --- /dev/null +++ b/curator-x-async/src/main/java/org/apache/curator/x/async/modeled/details/ZPathImpl.java @@ -0,0 +1,132 @@ +package org.apache.curator.x.async.modeled.details; + +import com.google.common.base.Splitter; +import com.google.common.collect.ImmutableList; +import org.apache.curator.utils.ZKPaths; +import org.apache.curator.x.async.modeled.ZPath; +import org.apache.zookeeper.common.PathUtils; +import java.util.Collections; +import java.util.List; +import java.util.NoSuchElementException; +import java.util.Objects; + +public class ZPathImpl implements ZPath +{ + public static final ZPath root = new ZPathImpl(Collections.singletonList(ZKPaths.PATH_SEPARATOR)); + + private final List<String> nodes; + + public static ZPath parse(String fullPath) + { + PathUtils.validatePath(fullPath); + List<String> nodes = ImmutableList.<String>builder() + .add(ZKPaths.PATH_SEPARATOR) + .addAll(Splitter.on(ZKPaths.PATH_SEPARATOR).omitEmptyStrings().splitToList(fullPath)) + .build(); + return new ZPathImpl(nodes); + } + + @Override + public ZPath at(String child) + { + return new ZPathImpl(nodes, child); + } + + @Override + public ZPath parent() + { + checkRootAccess(); + return new ZPathImpl(nodes.subList(0, nodes.size() - 1)); + } + + @Override + public boolean isRoot() + { + return nodes.size() == 1; + } + + @Override + public String fullPath() + { + return buildFullPath(nodes.size()); + } + + @Override + public String parentPath() + { + checkRootAccess(); + return buildFullPath(nodes.size() - 1); + } + + @Override + public String nodeName() + { + return nodes.get(nodes.size() - 1); + } + + @Override + public boolean equals(Object o) + { + if ( this == o ) + { + return true; + } + if ( o == null || getClass() != o.getClass() ) + { + return false; + } + + ZPathImpl zPaths = (ZPathImpl)o; + + return nodes.equals(zPaths.nodes); + } + + @Override + public int hashCode() + { + return nodes.hashCode(); + } + + @Override + public String toString() + { + return "ZPathImpl{" + "nodes=" + nodes + '}'; + } + + private ZPathImpl(List<String> nodes) + { + this.nodes = Objects.requireNonNull(nodes, "nodes cannot be null"); + } + + private ZPathImpl(List<String> nodes, String child) + { + PathUtils.validatePath(ZKPaths.PATH_SEPARATOR + child); + this.nodes = ImmutableList.<String>builder() + .addAll(nodes) + .add(child) + .build(); + } + + private void checkRootAccess() + { + if ( isRoot() ) + { + throw new NoSuchElementException("The root has no parent"); + } + } + + private String buildFullPath(int size) + { + boolean addSeparator = false; + StringBuilder str = new StringBuilder(); + for ( int i = 0; i < size; ++i ) + { + if ( i > 1 ) + { + str.append(ZKPaths.PATH_SEPARATOR); + } + str.append(nodes.get(i)); + } + return str.toString(); + } +} http://git-wip-us.apache.org/repos/asf/curator/blob/8237fde0/curator-x-async/src/test/java/org/apache/curator/x/async/CompletableBaseClassForTests.java ---------------------------------------------------------------------- diff --git a/curator-x-async/src/test/java/org/apache/curator/x/async/CompletableBaseClassForTests.java b/curator-x-async/src/test/java/org/apache/curator/x/async/CompletableBaseClassForTests.java new file mode 100644 index 0000000..232d301 --- /dev/null +++ b/curator-x-async/src/test/java/org/apache/curator/x/async/CompletableBaseClassForTests.java @@ -0,0 +1,65 @@ +/** + * 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.curator.x.async; + +import org.apache.curator.test.BaseClassForTests; +import org.apache.curator.test.Timing; +import org.testng.Assert; +import java.util.concurrent.CompletionStage; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; +import java.util.function.BiConsumer; + +public abstract class CompletableBaseClassForTests extends BaseClassForTests +{ + protected static final Timing timing = new Timing(); + + protected <T, U> void complete(CompletionStage<T> stage) + { + complete(stage, (v, e) -> {}); + } + + protected <T, U> void complete(CompletionStage<T> stage, BiConsumer<? super T, Throwable> handler) + { + try + { + stage.handle((v, e) -> { + handler.accept(v, e); + return null; + }).toCompletableFuture().get(timing.forWaiting().milliseconds(), TimeUnit.MILLISECONDS); + } + catch ( InterruptedException e ) + { + Thread.interrupted(); + } + catch ( ExecutionException e ) + { + if ( e.getCause() instanceof AssertionError ) + { + throw (AssertionError)e.getCause(); + } + Assert.fail("get() failed", e); + } + catch ( TimeoutException e ) + { + Assert.fail("get() timed out"); + } + } +} http://git-wip-us.apache.org/repos/asf/curator/blob/8237fde0/curator-x-async/src/test/java/org/apache/curator/x/async/TestBasicOperations.java ---------------------------------------------------------------------- diff --git a/curator-x-async/src/test/java/org/apache/curator/x/async/TestBasicOperations.java b/curator-x-async/src/test/java/org/apache/curator/x/async/TestBasicOperations.java index 1c4f241..0274413 100644 --- a/curator-x-async/src/test/java/org/apache/curator/x/async/TestBasicOperations.java +++ b/curator-x-async/src/test/java/org/apache/curator/x/async/TestBasicOperations.java @@ -44,9 +44,8 @@ import static org.apache.curator.x.async.api.CreateOption.compress; import static org.apache.curator.x.async.api.CreateOption.setDataIfExists; import static org.apache.zookeeper.CreateMode.EPHEMERAL_SEQUENTIAL; -public class TestBasicOperations extends BaseClassForTests +public class TestBasicOperations extends CompletableBaseClassForTests { - private static final Timing timing = new Timing(); private AsyncCuratorFramework client; @BeforeMethod @@ -181,36 +180,4 @@ public class TestBasicOperations extends BaseClassForTests Assert.assertEquals(v.getCode(), KeeperException.Code.CONNECTIONLOSS); }); } - - private <T, U> void complete(CompletionStage<T> stage) - { - complete(stage, (v, e) -> {}); - } - - private <T, U> void complete(CompletionStage<T> stage, BiConsumer<? super T, Throwable> handler) - { - try - { - stage.handle((v, e) -> { - handler.accept(v, e); - return null; - }).toCompletableFuture().get(timing.forWaiting().milliseconds(), TimeUnit.MILLISECONDS); - } - catch ( InterruptedException e ) - { - Thread.interrupted(); - } - catch ( ExecutionException e ) - { - if ( e.getCause() instanceof AssertionError ) - { - throw (AssertionError)e.getCause(); - } - Assert.fail("get() failed", e); - } - catch ( TimeoutException e ) - { - Assert.fail("get() timed out"); - } - } } http://git-wip-us.apache.org/repos/asf/curator/blob/8237fde0/curator-x-async/src/test/java/org/apache/curator/x/async/modeled/TestModeledAsyncCuratorFramework.java ---------------------------------------------------------------------- diff --git a/curator-x-async/src/test/java/org/apache/curator/x/async/modeled/TestModeledAsyncCuratorFramework.java b/curator-x-async/src/test/java/org/apache/curator/x/async/modeled/TestModeledAsyncCuratorFramework.java new file mode 100644 index 0000000..03d6d7d --- /dev/null +++ b/curator-x-async/src/test/java/org/apache/curator/x/async/modeled/TestModeledAsyncCuratorFramework.java @@ -0,0 +1,102 @@ +/** + * 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.curator.x.async.modeled; + +import org.apache.curator.framework.CuratorFramework; +import org.apache.curator.framework.CuratorFrameworkFactory; +import org.apache.curator.retry.RetryOneTime; +import org.apache.curator.utils.CloseableUtils; +import org.apache.curator.x.async.AsyncStage; +import org.apache.curator.x.async.CompletableBaseClassForTests; +import org.apache.curator.x.async.modeled.models.TestModel; +import org.apache.curator.x.async.modeled.models.TestNewerModel; +import org.testng.Assert; +import org.testng.annotations.AfterMethod; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.Test; +import java.math.BigInteger; +import java.util.concurrent.CountDownLatch; + +public class TestModeledAsyncCuratorFramework extends CompletableBaseClassForTests +{ + private static final ZPath path = ZPath.parse("/test/path"); + private CuratorFramework rawClient; + private JacksonModelSerializer<TestModel> serializer; + private JacksonModelSerializer<TestNewerModel> newSerializer; + + @BeforeMethod + @Override + public void setup() throws Exception + { + super.setup(); + + rawClient = CuratorFrameworkFactory.newClient(server.getConnectString(), timing.session(), timing.connection(), new RetryOneTime(1)); + rawClient.start(); + + serializer = new JacksonModelSerializer<>(TestModel.class); + newSerializer = new JacksonModelSerializer<>(TestNewerModel.class); + } + + @AfterMethod + @Override + public void teardown() throws Exception + { + CloseableUtils.closeQuietly(rawClient); + super.teardown(); + } + + @Test + public void testCrud() + { + TestModel rawModel = new TestModel("John", "Galt", "1 Galt's Gulch", 42, BigInteger.valueOf(1)); + TestModel rawModel2 = new TestModel("Wayne", "Rooney", "Old Trafford", 10, BigInteger.valueOf(1)); + ModeledAsyncCuratorFramework<TestModel> client = ModeledAsyncCuratorFramework.wrap(rawClient, path, serializer); + AsyncStage<String> stage = client.create(rawModel); + Assert.assertNull(stage.event()); + complete(stage, (s, e) -> Assert.assertNotNull(s)); + complete(client.read(), (model, e) -> Assert.assertEquals(model, rawModel)); + complete(client.update(rawModel2)); + complete(client.read(), (model, e) -> Assert.assertEquals(model, rawModel2)); + complete(client.delete()); + complete(client.checkExists(), (stat, e) -> Assert.assertNull(stat)); + } + + @Test + public void testBackwardCompatibility() + { + TestNewerModel rawNewModel = new TestNewerModel("John", "Galt", "1 Galt's Gulch", 42, BigInteger.valueOf(1), 100); + ModeledAsyncCuratorFramework<TestNewerModel> clientForNew = ModeledAsyncCuratorFramework.wrap(rawClient, path, newSerializer); + complete(clientForNew.create(rawNewModel), (s, e) -> Assert.assertNotNull(s)); + + ModeledAsyncCuratorFramework<TestModel> clientForOld = ModeledAsyncCuratorFramework.wrap(rawClient, path, serializer); + complete(clientForOld.read(), (model, e) -> Assert.assertTrue(rawNewModel.equalsOld(model))); + } + + @Test + public void testWatched() throws InterruptedException + { + CountDownLatch latch = new CountDownLatch(1); + ModeledAsyncCuratorFramework<TestModel> client = ModeledAsyncCuratorFramework.builder(rawClient, path, serializer).watched().build(); + client.checkExists().event().whenComplete((event, ex) -> latch.countDown()); + timing.sleepABit(); + Assert.assertEquals(latch.getCount(), 1); + client.create(new TestModel()); + Assert.assertTrue(timing.awaitLatch(latch)); + } +} http://git-wip-us.apache.org/repos/asf/curator/blob/8237fde0/curator-x-async/src/test/java/org/apache/curator/x/async/modeled/TestZPath.java ---------------------------------------------------------------------- diff --git a/curator-x-async/src/test/java/org/apache/curator/x/async/modeled/TestZPath.java b/curator-x-async/src/test/java/org/apache/curator/x/async/modeled/TestZPath.java new file mode 100644 index 0000000..9e7a718 --- /dev/null +++ b/curator-x-async/src/test/java/org/apache/curator/x/async/modeled/TestZPath.java @@ -0,0 +1,39 @@ +package org.apache.curator.x.async.modeled; + +import org.apache.curator.utils.ZKPaths; +import org.apache.curator.x.async.modeled.details.ZPathImpl; +import org.testng.Assert; +import org.testng.annotations.Test; + +public class TestZPath +{ + @Test + public void testRoot() + { + Assert.assertEquals(ZPath.root.nodeName(), ZKPaths.PATH_SEPARATOR); + Assert.assertEquals(ZPath.root, ZPathImpl.root); + Assert.assertTrue(ZPath.root.isRoot()); + Assert.assertEquals(ZPath.root.at("foo").parent(), ZPath.root); + Assert.assertTrue(ZPath.root.at("foo").parent().isRoot()); + } + + @Test + public void testBasic() + { + ZPath path = ZPath.root.at("one").at("two"); + Assert.assertFalse(path.isRoot()); + Assert.assertEquals(path, ZPath.root.at("one").at("two")); + Assert.assertNotEquals(path, ZPath.root.at("onex").at("two")); + Assert.assertEquals(path.nodeName(), "two"); + Assert.assertEquals(path.fullPath(), "/one/two"); + Assert.assertEquals(path.parentPath(), "/one"); + } + + @Test + public void testParsing() + { + Assert.assertEquals(ZPath.parse("/"), ZPath.root); + Assert.assertEquals(ZPath.parse("/one/two/three"), ZPath.root.at("one").at("two").at("three")); + Assert.assertEquals(ZPath.parse("/one/two/three"), ZPath.from("one", "two", "three")); + } +} http://git-wip-us.apache.org/repos/asf/curator/blob/8237fde0/curator-x-async/src/test/java/org/apache/curator/x/async/modeled/models/TestModel.java ---------------------------------------------------------------------- diff --git a/curator-x-async/src/test/java/org/apache/curator/x/async/modeled/models/TestModel.java b/curator-x-async/src/test/java/org/apache/curator/x/async/modeled/models/TestModel.java new file mode 100644 index 0000000..8a92d33 --- /dev/null +++ b/curator-x-async/src/test/java/org/apache/curator/x/async/modeled/models/TestModel.java @@ -0,0 +1,115 @@ +/** + * 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.curator.x.async.modeled.models; + +import java.math.BigInteger; +import java.util.Objects; + +public class TestModel +{ + private final String firstName; + private final String lastName; + private final String address; + private final int age; + private final BigInteger salary; + + public TestModel() + { + this("", "", "", 0, BigInteger.ZERO); + } + + public TestModel(String firstName, String lastName, String address, int age, BigInteger salary) + { + this.firstName = Objects.requireNonNull(firstName, "firstName cannot be null"); + this.lastName = Objects.requireNonNull(lastName, "lastName cannot be null"); + this.address = Objects.requireNonNull(address, "address cannot be null"); + this.age = Objects.requireNonNull(age, "age cannot be null"); + this.salary = salary; + } + + public String getFirstName() + { + return firstName; + } + + public String getLastName() + { + return lastName; + } + + public String getAddress() + { + return address; + } + + public int getAge() + { + return age; + } + + public BigInteger getSalary() + { + return salary; + } + + @Override + public boolean equals(Object o) + { + if ( this == o ) + { + return true; + } + if ( o == null || getClass() != o.getClass() ) + { + return false; + } + + TestModel testModel = (TestModel)o; + + if ( age != testModel.age ) + { + return false; + } + if ( !firstName.equals(testModel.firstName) ) + { + return false; + } + if ( !lastName.equals(testModel.lastName) ) + { + return false; + } + //noinspection SimplifiableIfStatement + if ( !address.equals(testModel.address) ) + { + return false; + } + return salary.equals(testModel.salary); + } + + @Override + public int hashCode() + { + int result = firstName.hashCode(); + result = 31 * result + lastName.hashCode(); + result = 31 * result + address.hashCode(); + result = 31 * result + age; + result = 31 * result + salary.hashCode(); + return result; + } +} http://git-wip-us.apache.org/repos/asf/curator/blob/8237fde0/curator-x-async/src/test/java/org/apache/curator/x/async/modeled/models/TestNewerModel.java ---------------------------------------------------------------------- diff --git a/curator-x-async/src/test/java/org/apache/curator/x/async/modeled/models/TestNewerModel.java b/curator-x-async/src/test/java/org/apache/curator/x/async/modeled/models/TestNewerModel.java new file mode 100644 index 0000000..94e82fb --- /dev/null +++ b/curator-x-async/src/test/java/org/apache/curator/x/async/modeled/models/TestNewerModel.java @@ -0,0 +1,137 @@ +/** + * 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.curator.x.async.modeled.models; + +import java.math.BigInteger; +import java.util.Objects; + +public class TestNewerModel +{ + private final String firstName; + private final String lastName; + private final String address; + private final int age; + private final BigInteger salary; + private final long newField; + + public TestNewerModel() + { + this("", "", "", 0, BigInteger.ZERO, 0); + } + + public TestNewerModel(String firstName, String lastName, String address, int age, BigInteger salary, long newField) + { + this.firstName = Objects.requireNonNull(firstName, "firstName cannot be null"); + this.lastName = Objects.requireNonNull(lastName, "lastName cannot be null"); + this.address = Objects.requireNonNull(address, "address cannot be null"); + this.age = Objects.requireNonNull(age, "age cannot be null"); + this.salary = salary; + this.newField = newField; + } + + public String getFirstName() + { + return firstName; + } + + public String getLastName() + { + return lastName; + } + + public String getAddress() + { + return address; + } + + public int getAge() + { + return age; + } + + public BigInteger getSalary() + { + return salary; + } + + public long getNewField() + { + return newField; + } + + public boolean equalsOld(TestModel model) + { + return firstName.equals(model.getFirstName()) + && lastName.equals(model.getLastName()) + && address.equals(model.getAddress()) + && salary.equals(model.getSalary()) + && age == model.getAge() + ; + } + + @Override + public boolean equals(Object o) + { + if ( this == o ) + { + return true; + } + if ( o == null || getClass() != o.getClass() ) + { + return false; + } + + TestNewerModel that = (TestNewerModel)o; + + if ( age != that.age ) + { + return false; + } + if ( newField != that.newField ) + { + return false; + } + if ( !firstName.equals(that.firstName) ) + { + return false; + } + if ( !lastName.equals(that.lastName) ) + { + return false; + } + //noinspection SimplifiableIfStatement + if ( !address.equals(that.address) ) + { + return false; + } + return salary.equals(that.salary); + } + + @Override + public int hashCode() + { + int result = firstName.hashCode(); + result = 31 * result + lastName.hashCode(); + result = 31 * result + address.hashCode(); + result = 31 * result + age; + result = 31 * result + salary.hashCode(); + result = 31 * result + (int)(newField ^ (newField >>> 32)); + return result; + } +} http://git-wip-us.apache.org/repos/asf/curator/blob/8237fde0/pom.xml ---------------------------------------------------------------------- diff --git a/pom.xml b/pom.xml index 34e724c..1f66800 100644 --- a/pom.xml +++ b/pom.xml @@ -79,7 +79,7 @@ <dropwizard-version>0.7.0</dropwizard-version> <maven-shade-plugin-version>2.4.3</maven-shade-plugin-version> <slf4j-version>1.7.6</slf4j-version> - <clirr-maven-plugin-version>2.6.1</clirr-maven-plugin-version> + <clirr-maven-plugin-version>2.8</clirr-maven-plugin-version> <!-- OSGi Properties --> <osgi.export.package />
