Added AsyncResult - still needs documentation
Project: http://git-wip-us.apache.org/repos/asf/curator/repo Commit: http://git-wip-us.apache.org/repos/asf/curator/commit/649c4415 Tree: http://git-wip-us.apache.org/repos/asf/curator/tree/649c4415 Diff: http://git-wip-us.apache.org/repos/asf/curator/diff/649c4415 Branch: refs/heads/CURATOR-351 Commit: 649c441529040401e2f414fbaffd8c6122263e70 Parents: 2ef420c Author: randgalt <[email protected]> Authored: Sat Jan 21 11:06:36 2017 -0500 Committer: randgalt <[email protected]> Committed: Sat Jan 21 11:06:36 2017 -0500 ---------------------------------------------------------------------- .../org/apache/curator/x/async/AsyncResult.java | 118 +++++++++++++++ .../x/async/details/AsyncResultImpl.java | 142 +++++++++++++++++++ .../curator/x/async/TestBasicOperations.java | 40 +++++- 3 files changed, 299 insertions(+), 1 deletion(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/curator/blob/649c4415/curator-x-async/src/main/java/org/apache/curator/x/async/AsyncResult.java ---------------------------------------------------------------------- diff --git a/curator-x-async/src/main/java/org/apache/curator/x/async/AsyncResult.java b/curator-x-async/src/main/java/org/apache/curator/x/async/AsyncResult.java new file mode 100644 index 0000000..5c99480 --- /dev/null +++ b/curator-x-async/src/main/java/org/apache/curator/x/async/AsyncResult.java @@ -0,0 +1,118 @@ +/** + * 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.x.async.details.AsyncResultImpl; +import org.apache.zookeeper.KeeperException; +import java.util.Optional; +import java.util.concurrent.CompletionStage; + +/** + * <p> + * Utility that combines the value, the ZooKeeper result code and the exception in one object + * allowing you to not worry about exceptional completions. i.e. the {@link java.util.concurrent.CompletionStage} + * retured by {@link org.apache.curator.x.async.AsyncResult#of(AsyncStage)} always completes successfully with an + * {@link org.apache.curator.x.async.AsyncResult} object. + * </p> + * + * <p> + * All three possible results from a ZooKeeper method are encapsulated in this object. If the ZooKeeper + * method succeeds, the internal value will be set. If there was a standard ZooKeeper error code + * ({@link org.apache.zookeeper.KeeperException.Code#NODEEXISTS}, etc.), that code is set and the + * value is null. If there was a general exception, that exception is set, the value will be null + * and the code will be {@link org.apache.zookeeper.KeeperException.Code#SYSTEMERROR}. + * </p> + * @param <T> value type + */ +public interface AsyncResult<T> +{ + /** + * Return a new stage that wraps an async stage into a result-style completion stage. The returned + * CompletionStage will always complete successfully. + * + * @param stage the stage to wrap + * @param <T> value type + * @return completion stage that resolves to a result + */ + static <T> CompletionStage<AsyncResult<T>> of(AsyncStage<T> stage) + { + return stage.handle((value, ex) -> { + if ( ex != null ) + { + if ( ex instanceof KeeperException ) + { + return new AsyncResultImpl<T>(((KeeperException)ex).code()); + } + return new AsyncResultImpl<T>(ex); + } + return new AsyncResultImpl<T>(value); + }); + } + + /** + * Returns the raw result of the ZooKeeper method or <code>null</code> + * + * @return result or <code>null</code> + */ + T getRawValue(); + + /** + * An optional wrapper around the ZooKeeper method result + * + * @return wrapped result + */ + Optional<T> getValue(); + + /** + * Return the ZooKeeper result code. If the method was successful, + * {@link org.apache.zookeeper.KeeperException.Code#OK} is returned. If there was a general + * exception {@link org.apache.zookeeper.KeeperException.Code#SYSTEMERROR} is returned. + * + * @return result code + */ + KeeperException.Code getCode(); + + /** + * Return any general exception or <code>null</code> + * + * @return exception or <code>null</code> + */ + Throwable getRawException(); + + /** + * An optional wrapper around any general exception + * + * @return wrapped exception + */ + Optional<Throwable> getException(); + + /** + * If there was a general exception (but <strong>not</strong> a {@link org.apache.zookeeper.KeeperException}) + * a {@link java.lang.RuntimeException} is thrown that wraps the exception. Otherwise, the method returns + * without any action being performed. + */ + void checkException(); + + /** + * If there was a general exception or a {@link org.apache.zookeeper.KeeperException} + * a {@link java.lang.RuntimeException} is thrown that wraps the exception. Otherwise, the method returns + * without any action being performed. + */ + void checkError(); +} http://git-wip-us.apache.org/repos/asf/curator/blob/649c4415/curator-x-async/src/main/java/org/apache/curator/x/async/details/AsyncResultImpl.java ---------------------------------------------------------------------- diff --git a/curator-x-async/src/main/java/org/apache/curator/x/async/details/AsyncResultImpl.java b/curator-x-async/src/main/java/org/apache/curator/x/async/details/AsyncResultImpl.java new file mode 100644 index 0000000..c555b83 --- /dev/null +++ b/curator-x-async/src/main/java/org/apache/curator/x/async/details/AsyncResultImpl.java @@ -0,0 +1,142 @@ +/** + * 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.details; + +import org.apache.curator.x.async.AsyncResult; +import org.apache.zookeeper.KeeperException; +import java.util.Objects; +import java.util.Optional; + +public class AsyncResultImpl<T> implements AsyncResult<T> +{ + private final T value; + private final KeeperException.Code code; + private final Throwable exception; + + public AsyncResultImpl() + { + this(null, KeeperException.Code.OK, null); + } + + public AsyncResultImpl(KeeperException.Code code) + { + this(null, code, null); + } + + public AsyncResultImpl(T value) + { + this(value, KeeperException.Code.OK, null); + } + + public AsyncResultImpl(Throwable exception) + { + this(null, KeeperException.Code.SYSTEMERROR, exception); + } + + private AsyncResultImpl(T value, KeeperException.Code code, Throwable exception) + { + this.value = value; + this.exception = exception; + this.code = Objects.requireNonNull(code, "error cannot be null"); + } + + public T getRawValue() + { + return value; + } + + public Optional<T> getValue() + { + return Optional.ofNullable(value); + } + + public KeeperException.Code getCode() + { + return code; + } + + public Throwable getRawException() + { + return exception; + } + + public Optional<Throwable> getException() + { + return Optional.ofNullable(exception); + } + + public void checkException() + { + if ( exception != null ) + { + throw new RuntimeException(exception); + } + } + + @Override + public void checkError() + { + checkException(); + if ( code != KeeperException.Code.OK ) + { + throw new RuntimeException(KeeperException.create(code)); + } + } + + @Override + public boolean equals(Object o) + { + if ( this == o ) + { + return true; + } + if ( o == null || getClass() != o.getClass() ) + { + return false; + } + + AsyncResultImpl<?> that = (AsyncResultImpl<?>)o; + + if ( value != null ? !value.equals(that.value) : that.value != null ) + { + return false; + } + //noinspection SimplifiableIfStatement + if ( code != that.code ) + { + return false; + } + return exception != null ? exception.equals(that.exception) : that.exception == null; + } + + @Override + public int hashCode() + { + int result = value != null ? value.hashCode() : 0; + result = 31 * result + code.hashCode(); + result = 31 * result + (exception != null ? exception.hashCode() : 0); + return result; + } + + @Override + public String toString() + { + return "AsyncResult{" + "value=" + value + ", code=" + code + ", exception=" + exception + '}'; + } +} http://git-wip-us.apache.org/repos/asf/curator/blob/649c4415/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 34c02aa..1c4f241 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 @@ -55,7 +55,7 @@ public class TestBasicOperations extends BaseClassForTests { super.setup(); - CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), new RetryOneTime(timing.milliseconds())); + CuratorFramework client = CuratorFrameworkFactory.newClient(server.getConnectString(), new RetryOneTime(timing.forSleepingABit().milliseconds())); client.start(); this.client = AsyncCuratorFramework.wrap(client); } @@ -144,6 +144,44 @@ public class TestBasicOperations extends BaseClassForTests Assert.assertTrue(timing.awaitLatch(latch)); } + @Test + public void testResultWrapper() throws Exception + { + CompletionStage<AsyncResult<String>> resultStage = AsyncResult.of(client.create().forPath("/first")); + complete(resultStage, (v, e) -> { + Assert.assertNull(e); + Assert.assertEquals(v.getRawValue(), "/first"); + Assert.assertNull(v.getRawException()); + Assert.assertEquals(v.getCode(), KeeperException.Code.OK); + }); + + resultStage = AsyncResult.of(client.create().forPath("/foo/bar")); + complete(resultStage, (v, e) -> { + Assert.assertNull(e); + Assert.assertNull(v.getRawValue()); + Assert.assertNull(v.getRawException()); + Assert.assertEquals(v.getCode(), KeeperException.Code.NONODE); + }); + + resultStage = AsyncResult.of(client.create().forPath("illegal path")); + complete(resultStage, (v, e) -> { + Assert.assertNull(e); + Assert.assertNull(v.getRawValue()); + Assert.assertNotNull(v.getRawException()); + Assert.assertTrue(v.getRawException() instanceof IllegalArgumentException); + Assert.assertEquals(v.getCode(), KeeperException.Code.SYSTEMERROR); + }); + + server.stop(); + resultStage = AsyncResult.of(client.create().forPath("/second")); + complete(resultStage, (v, e) -> { + Assert.assertNull(e); + Assert.assertNull(v.getRawValue()); + Assert.assertNull(v.getRawException()); + Assert.assertEquals(v.getCode(), KeeperException.Code.CONNECTIONLOSS); + }); + } + private <T, U> void complete(CompletionStage<T> stage) { complete(stage, (v, e) -> {});
