From 5d6921fc0511ba26021d068455c50e6a2979a73b Mon Sep 17 00:00:00 2001 From: leventov Date: Wed, 14 Dec 2016 20:39:06 -0600 Subject: [PATCH] Backport of druid-io/druid#3693 --- .../com/metamx/common/guava/BaseSequence.java | 63 +++---- .../com/metamx/common/guava/Comparators.java | 15 ++ .../metamx/common/guava/ConcatSequence.java | 24 +-- .../common/guava/ExecuteWhenDoneYielder.java | 5 +- .../guava/ExecutorExecutingSequence.java | 138 -------------- .../java/com/metamx/common/guava/Fns.java | 49 ----- .../common/guava/IteratorWithBaggage.java | 64 ------- .../metamx/common/guava/LimitedSequence.java | 10 +- .../guava/LimitedYieldingAccumulator.java | 66 ------- .../common/guava/ResourceClosingSequence.java | 61 ------- .../common/guava/ResourceClosingYielder.java | 61 ------- .../metamx/common/guava/SequenceWrapper.java | 71 ++++++++ .../com/metamx/common/guava/Sequences.java | 44 ++++- .../metamx/common/guava/SimpleSequence.java | 52 ------ .../metamx/common/guava/WrappingSequence.java | 100 ++++++++++ .../metamx/common/guava/WrappingYielder.java | 101 +++++++++++ .../com/metamx/common/guava/YieldSign.java | 24 --- .../com/metamx/common/guava/Yielders.java | 15 +- .../common/guava/YieldingSequenceBase.java | 5 - .../metamx/common/guava/BaseSequenceTest.java | 4 +- .../guava/ExecutorExecutingSequenceTest.java | 171 ------------------ .../com/metamx/common/guava/TestSequence.java | 30 ++- .../common/guava/WithEffectSequenceTest.java | 73 ++++++++ ...nceTest.java => WrappingSequenceTest.java} | 43 ++++- 24 files changed, 509 insertions(+), 780 deletions(-) delete mode 100644 src/main/java/com/metamx/common/guava/ExecutorExecutingSequence.java delete mode 100644 src/main/java/com/metamx/common/guava/Fns.java delete mode 100644 src/main/java/com/metamx/common/guava/IteratorWithBaggage.java delete mode 100644 src/main/java/com/metamx/common/guava/LimitedYieldingAccumulator.java delete mode 100644 src/main/java/com/metamx/common/guava/ResourceClosingSequence.java delete mode 100644 src/main/java/com/metamx/common/guava/ResourceClosingYielder.java create mode 100644 src/main/java/com/metamx/common/guava/SequenceWrapper.java delete mode 100644 src/main/java/com/metamx/common/guava/SimpleSequence.java create mode 100644 src/main/java/com/metamx/common/guava/WrappingSequence.java create mode 100644 src/main/java/com/metamx/common/guava/WrappingYielder.java delete mode 100644 src/main/java/com/metamx/common/guava/YieldSign.java delete mode 100644 src/test/java/com/metamx/common/guava/ExecutorExecutingSequenceTest.java create mode 100644 src/test/java/com/metamx/common/guava/WithEffectSequenceTest.java rename src/test/java/com/metamx/common/guava/{ResourceClosingSequenceTest.java => WrappingSequenceTest.java} (52%) diff --git a/src/main/java/com/metamx/common/guava/BaseSequence.java b/src/main/java/com/metamx/common/guava/BaseSequence.java index c418c33d..ed363b82 100644 --- a/src/main/java/com/metamx/common/guava/BaseSequence.java +++ b/src/main/java/com/metamx/common/guava/BaseSequence.java @@ -16,9 +16,6 @@ package com.metamx.common.guava; -import com.google.common.base.Throwables; -import com.metamx.common.logger.Logger; - import java.io.Closeable; import java.io.IOException; import java.util.Iterator; @@ -27,30 +24,9 @@ */ public class BaseSequence> implements Sequence { - private static final Logger log = new Logger(BaseSequence.class); private final IteratorMaker maker; - public static Sequence simple(final Iterable iterable) - { - return new BaseSequence<>( - new BaseSequence.IteratorMaker>() - { - @Override - public Iterator make() - { - return iterable.iterator(); - } - - @Override - public void cleanup(Iterator iterFromMake) - { - - } - } - ); - } - public BaseSequence( IteratorMaker maker ) @@ -66,11 +42,18 @@ public OutType accumulate(OutType initValue, final Accumulator Yielder toYielder(OutType initValue, YieldingAccumulat try { return makeYielder(initValue, accumulator, iterator); } - catch (Exception e) { - // We caught an Exception instead of returning a really, real, live, real boy, errr, iterator - // So we better try to close our stuff, 'cause the exception is what is making it out of here. + catch (Throwable t) { try { maker.cleanup(iterator); } - catch (RuntimeException e1) { - log.error(e1, "Exception thrown when closing maker. Logging and ignoring."); + catch (Exception e) { + t.addSuppressed(e); } - throw Throwables.propagate(e); + throw t; } } @@ -135,16 +116,14 @@ public Yielder next(OutType initValue) try { return makeYielder(initValue, accumulator, iter); } - catch (Exception e) { - // We caught an Exception instead of returning a really, real, live, real boy, errr, iterator - // So we better try to close our stuff, 'cause the exception is what is making it out of here. + catch (Throwable t) { try { maker.cleanup(iter); } - catch (RuntimeException e1) { - log.error(e1, "Exception thrown when closing maker. Logging and ignoring."); + catch (Exception e) { + t.addSuppressed(e); } - throw Throwables.propagate(e); + throw t; } } @@ -162,10 +141,10 @@ public void close() throws IOException }; } - public static interface IteratorMaker> + public interface IteratorMaker> { - public IterType make(); + IterType make(); - public void cleanup(IterType iterFromMake); + void cleanup(IterType iterFromMake); } } diff --git a/src/main/java/com/metamx/common/guava/Comparators.java b/src/main/java/com/metamx/common/guava/Comparators.java index a818cc22..c286082f 100644 --- a/src/main/java/com/metamx/common/guava/Comparators.java +++ b/src/main/java/com/metamx/common/guava/Comparators.java @@ -16,6 +16,7 @@ package com.metamx.common.guava; +import com.google.common.primitives.Longs; import org.joda.time.DateTimeComparator; import org.joda.time.Interval; @@ -71,6 +72,13 @@ public int compare(T t, T t1) @Override public int compare(Interval lhs, Interval rhs) { + if (lhs.getChronology().equals(rhs.getChronology())) { + int compare = Longs.compare(lhs.getStartMillis(), rhs.getStartMillis()); + if (compare == 0) { + return Longs.compare(lhs.getEndMillis(), rhs.getEndMillis()); + } + return compare; + } int retVal = dateTimeComp.compare(lhs.getStart(), rhs.getStart()); if (retVal == 0) { retVal = dateTimeComp.compare(lhs.getEnd(), rhs.getEnd()); @@ -86,6 +94,13 @@ public int compare(Interval lhs, Interval rhs) @Override public int compare(Interval lhs, Interval rhs) { + if (lhs.getChronology().equals(rhs.getChronology())) { + int compare = Longs.compare(lhs.getEndMillis(), rhs.getEndMillis()); + if (compare == 0) { + return Longs.compare(lhs.getStartMillis(), rhs.getStartMillis()); + } + return compare; + } int retVal = dateTimeComp.compare(lhs.getEnd(), rhs.getEnd()); if (retVal == 0) { retVal = dateTimeComp.compare(lhs.getStart(), rhs.getStart()); diff --git a/src/main/java/com/metamx/common/guava/ConcatSequence.java b/src/main/java/com/metamx/common/guava/ConcatSequence.java index fa3362c1..8136eb99 100644 --- a/src/main/java/com/metamx/common/guava/ConcatSequence.java +++ b/src/main/java/com/metamx/common/guava/ConcatSequence.java @@ -17,8 +17,8 @@ package com.metamx.common.guava; import com.google.common.base.Throwables; -import com.google.common.io.Closeables; +import java.io.Closeable; import java.io.IOException; /** @@ -71,11 +71,14 @@ public Sequence accumulate(Sequence accumulated, Sequence in) try { return makeYielder(yielderYielder, initValue, accumulator); } - catch (RuntimeException e) { - // We caught a RuntimeException instead of returning a really, real, live, real boy, errr, iterator - // So we better try to close our stuff, 'cause the exception is what is making it out of here. - CloseQuietly.close(yielderYielder); - throw e; + catch (Throwable t) { + try { + yielderYielder.close(); + } + catch (Exception e) { + t.addSuppressed(e); + } + throw t; } } @@ -85,10 +88,6 @@ public Yielder makeYielder( YieldingAccumulator accumulator ) { - if (yielderYielder.isDone()) { - return Yielders.done(initValue, yielderYielder); - } - while (!yielderYielder.isDone()) { Yielder yielder = yielderYielder.get().toYielder(initValue, accumulator); if (accumulator.yielded()) { @@ -150,8 +149,9 @@ public boolean isDone() @Override public void close() throws IOException { - yielder.close(); - yielderYielder.close(); + try (Closeable toClose = yielderYielder) { + yielder.close(); + } } }; } diff --git a/src/main/java/com/metamx/common/guava/ExecuteWhenDoneYielder.java b/src/main/java/com/metamx/common/guava/ExecuteWhenDoneYielder.java index f568eb36..57e04741 100644 --- a/src/main/java/com/metamx/common/guava/ExecuteWhenDoneYielder.java +++ b/src/main/java/com/metamx/common/guava/ExecuteWhenDoneYielder.java @@ -54,9 +54,10 @@ public boolean isDone() @Override public void close() throws IOException { - if (isDone()) { + boolean done = isDone(); + baseYielder.close(); + if (done) { executor.execute(runnable); } - baseYielder.close(); } } diff --git a/src/main/java/com/metamx/common/guava/ExecutorExecutingSequence.java b/src/main/java/com/metamx/common/guava/ExecutorExecutingSequence.java deleted file mode 100644 index ec2e76d8..00000000 --- a/src/main/java/com/metamx/common/guava/ExecutorExecutingSequence.java +++ /dev/null @@ -1,138 +0,0 @@ -/* - * Copyright 2011,2012 Metamarkets Group Inc. - * - * Licensed 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 com.metamx.common.guava; - -import com.google.common.base.Throwables; - -import java.io.IOException; -import java.util.concurrent.Callable; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Future; - -/** - */ -public class ExecutorExecutingSequence implements Sequence -{ - private final Sequence sequence; - private final ExecutorService exec; - - public ExecutorExecutingSequence( - Sequence sequence, - ExecutorService exec - ) - { - this.sequence = sequence; - this.exec = exec; - } - - @Override - public OutType accumulate(final OutType initValue, final Accumulator accumulator) - { - Future future = exec.submit( - new Callable() - { - @Override - public OutType call() throws Exception - { - return sequence.accumulate(initValue, accumulator); - } - } - ); - try { - return future.get(); - } - catch (InterruptedException e) { - throw Throwables.propagate(e); - } - catch (ExecutionException e) { - throw Throwables.propagate(e); - } - } - - @Override - public Yielder toYielder(final OutType initValue, final YieldingAccumulator accumulator) - { - Future> future = exec.submit( - new Callable>() - { - @Override - public Yielder call() throws Exception - { - return makeYielder(sequence.toYielder(initValue, accumulator)); - } - } - ); - try { - return future.get(); - } - catch (InterruptedException e) { - throw Throwables.propagate(e); - } - catch (ExecutionException e) { - throw Throwables.propagate(e); - } - } - - private Yielder makeYielder(final Yielder yielder) - { - return new Yielder() - { - @Override - public OutType get() - { - return yielder.get(); - } - - @Override - public Yielder next(final OutType initValue) - { - Future> future = exec.submit( - new Callable>() - { - @Override - public Yielder call() throws Exception - { - return makeYielder(yielder.next(initValue)); - } - } - ); - try { - return future.get(); - } - catch (InterruptedException e) { - throw Throwables.propagate(e); - } - catch (ExecutionException e) { - throw Throwables.propagate(e); - } - } - - @Override - public boolean isDone() - { - return yielder.isDone(); - } - - @Override - public void close() throws IOException - { - yielder.close(); - } - }; - } -} diff --git a/src/main/java/com/metamx/common/guava/Fns.java b/src/main/java/com/metamx/common/guava/Fns.java deleted file mode 100644 index f2bbaabe..00000000 --- a/src/main/java/com/metamx/common/guava/Fns.java +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright 2011,2012 Metamarkets Group Inc. - * - * Licensed 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 com.metamx.common.guava; - -import com.google.common.base.Function; - -import java.util.Map; - -/** - */ -public class Fns -{ - public static Function splitFn(final String splitChar, final int numCols) - { - return new Function() - { - public String[] apply(String input) - { - return input.split(splitChar, numCols); - } - }; - } - - public static Function, OutType> getFromMap(final KeyType key) - { - return new Function, OutType>() - { - @Override - public OutType apply(Map in) - { - return in.get(key); - } - }; - } -} diff --git a/src/main/java/com/metamx/common/guava/IteratorWithBaggage.java b/src/main/java/com/metamx/common/guava/IteratorWithBaggage.java deleted file mode 100644 index f969ef43..00000000 --- a/src/main/java/com/metamx/common/guava/IteratorWithBaggage.java +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright 2011,2012 Metamarkets Group Inc. - * - * Licensed 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 com.metamx.common.guava; - -import com.metamx.common.parsers.CloseableIterator; - -import java.io.Closeable; -import java.io.IOException; -import java.util.Iterator; - -/** - */ -public class IteratorWithBaggage implements CloseableIterator -{ - private final Iterator baseIter; - private final Closeable baggage; - - public IteratorWithBaggage( - Iterator baseIter, - Closeable baggage - ) - { - this.baseIter = baseIter; - this.baggage = baggage; - } - - @Override - public boolean hasNext() - { - return baseIter.hasNext(); - } - - @Override - public T next() - { - return baseIter.next(); - } - - @Override - public void remove() - { - baseIter.remove(); - } - - @Override - public void close() throws IOException - { - baggage.close(); - } -} diff --git a/src/main/java/com/metamx/common/guava/LimitedSequence.java b/src/main/java/com/metamx/common/guava/LimitedSequence.java index 017e7d3f..95c94719 100644 --- a/src/main/java/com/metamx/common/guava/LimitedSequence.java +++ b/src/main/java/com/metamx/common/guava/LimitedSequence.java @@ -24,12 +24,12 @@ * Limits the number of inputs from this sequence. For example, if there are actually 100 things in the sequence * but the limit is set to 10, the Sequence will act as if it only had 10 things. */ -public class LimitedSequence extends YieldingSequenceBase +final class LimitedSequence extends YieldingSequenceBase { private final Sequence baseSequence; private final int limit; - public LimitedSequence( + LimitedSequence( Sequence baseSequence, int limit ) @@ -56,7 +56,7 @@ private class LimitedYielder implements Yielder private final Yielder subYielder; private final LimitedYieldingAccumulator limitedAccumulator; - public LimitedYielder( + LimitedYielder( Yielder subYielder, LimitedYieldingAccumulator limitedAccumulator ) @@ -107,7 +107,7 @@ private class LimitedYieldingAccumulator extends DelegatingYieldingA int count; boolean interruptYield = false; - public LimitedYieldingAccumulator(YieldingAccumulator accumulator) + LimitedYieldingAccumulator(YieldingAccumulator accumulator) { super(accumulator); count = 0; @@ -135,7 +135,7 @@ public OutType accumulate(OutType accumulated, T in) return retVal; } - public boolean isInterruptYield() + boolean isInterruptYield() { return interruptYield; } diff --git a/src/main/java/com/metamx/common/guava/LimitedYieldingAccumulator.java b/src/main/java/com/metamx/common/guava/LimitedYieldingAccumulator.java deleted file mode 100644 index db863673..00000000 --- a/src/main/java/com/metamx/common/guava/LimitedYieldingAccumulator.java +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright 2011 - 2015 Metamarkets Group Inc. - * - * Licensed 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 com.metamx.common.guava; - -/** - * @deprecated this class uses expensive volatile counter inside, but it is not thread-safe. It is going to be removed - * in the future. - */ -@Deprecated -public class LimitedYieldingAccumulator extends YieldingAccumulator -{ - private final int limit; - private final YieldingAccumulator delegate; - - private volatile int count = 0; - - public LimitedYieldingAccumulator( - YieldingAccumulator delegate, int limit - ) - { - this.limit = limit; - this.delegate = delegate; - } - - @Override - public void yield() - { - delegate.yield(); - } - - @Override - public boolean yielded() - { - return delegate.yielded(); - } - - @Override - public void reset() - { - delegate.reset(); - } - - @Override - public OutType accumulate(OutType accumulated, T in) - { - if (count < limit) { - count++; - return delegate.accumulate(accumulated, in); - } - return accumulated; - } -} diff --git a/src/main/java/com/metamx/common/guava/ResourceClosingSequence.java b/src/main/java/com/metamx/common/guava/ResourceClosingSequence.java deleted file mode 100644 index 678a7854..00000000 --- a/src/main/java/com/metamx/common/guava/ResourceClosingSequence.java +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright 2011 - 2015 Metamarkets Group Inc. - * - * Licensed 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 com.metamx.common.guava; - -import java.io.Closeable; - -/** - */ -public class ResourceClosingSequence implements Sequence -{ - private final Sequence baseSequence; - private final Closeable closeable; - - public ResourceClosingSequence(Sequence baseSequence, Closeable closeable) - { - this.baseSequence = baseSequence; - this.closeable = closeable; - } - - @Override - public OutType accumulate(OutType initValue, Accumulator accumulator) - { - try { - return baseSequence.accumulate(initValue, accumulator); - } - finally { - CloseQuietly.close(closeable); - } - } - - @Override - public Yielder toYielder( - OutType initValue, YieldingAccumulator accumulator - ) - { - final Yielder baseYielder; - try { - baseYielder = baseSequence.toYielder(initValue, accumulator); - } - catch (RuntimeException e) { - CloseQuietly.close(closeable); - throw e; - } - - return new ResourceClosingYielder<>(baseYielder, closeable); - } -} diff --git a/src/main/java/com/metamx/common/guava/ResourceClosingYielder.java b/src/main/java/com/metamx/common/guava/ResourceClosingYielder.java deleted file mode 100644 index aa0aceb2..00000000 --- a/src/main/java/com/metamx/common/guava/ResourceClosingYielder.java +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright 2011 - 2015 Metamarkets Group Inc. - * - * Licensed 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 com.metamx.common.guava; - -import java.io.Closeable; -import java.io.IOException; - -/** - */ -public class ResourceClosingYielder implements Yielder -{ - private final Yielder baseYielder; - private final Closeable closeable; - - public ResourceClosingYielder(Yielder baseYielder, Closeable closeable) - { - this.baseYielder = baseYielder; - this.closeable = closeable; - } - - @Override - public OutType get() - { - return baseYielder.get(); - } - - @Override - public Yielder next(OutType initValue) - { - return new ResourceClosingYielder<>(baseYielder.next(initValue), closeable); - } - - @Override - public boolean isDone() - { - return baseYielder.isDone(); - } - - @Override - public void close() throws IOException - { - if (closeable != null) { - closeable.close(); - } - baseYielder.close(); - } -} diff --git a/src/main/java/com/metamx/common/guava/SequenceWrapper.java b/src/main/java/com/metamx/common/guava/SequenceWrapper.java new file mode 100644 index 00000000..c7f4c3fc --- /dev/null +++ b/src/main/java/com/metamx/common/guava/SequenceWrapper.java @@ -0,0 +1,71 @@ +/* + * Copyright 2016 Metamarkets Group Inc. + * + * Licensed 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 com.metamx.common.guava; + +import com.google.common.base.Supplier; + +/** + * @see Sequences#wrap(Sequence, SequenceWrapper) + */ +public abstract class SequenceWrapper +{ + /** + * Executed before sequence processing, i. e. before {@link Sequence#accumulate} or {@link Sequence#toYielder} on the + * wrapped sequence. Default implementation does nothing. + */ + public void before() + { + // do nothing + } + + /** + * Wraps any bits of the wrapped sequence processing: {@link Sequence#accumulate} or {@link Sequence#toYielder} and + * {@link Yielder#next(Object)} on the wrapped yielder. Doesn't wrap {@link #before} and {@link #after}. + * + *

{@code sequenceProcessing.get()} must be called just once. Implementation of this method should look like + *

+   * ... do something
+   * try {
+   *   return sequenceProcessing.get();
+   * }
+   * finally {
+   *   ... do something else
+   * }
+   * 
+ */ + public RetType wrap(Supplier sequenceProcessing) throws Exception + { + return sequenceProcessing.get(); + } + + /** + * Executed after sequence processing, i. e. after {@link Sequence#accumulate} on the wrapped sequence or after {@link + * Yielder#close()} on the wrapped yielder, or if exception was thrown from any method called on the wrapped sequence + * or yielder, or from {@link #before}, or from {@link #wrap} methods of this SequenceWrapper. + * + *

Even if {@code thrown} is not null, implementation of this method shouldn't rethrow it, it is done outside. + * + * @param isDone true if all elements in the sequence were processed and no exception was thrown, false otherwise + * @param thrown an exception thrown from any method called on the wrapped sequence or yielder, or from {@link + * #before()}, or from {@link #wrap} methods of this SequenceWrapper, or null if no exception was thrown. + */ + public void after(boolean isDone, Throwable thrown) throws Exception + { + // do nothing + } +} diff --git a/src/main/java/com/metamx/common/guava/Sequences.java b/src/main/java/com/metamx/common/guava/Sequences.java index fb705eb6..439510d3 100644 --- a/src/main/java/com/metamx/common/guava/Sequences.java +++ b/src/main/java/com/metamx/common/guava/Sequences.java @@ -17,6 +17,7 @@ package com.metamx.common.guava; import com.google.common.base.Function; +import com.google.common.base.Preconditions; import com.google.common.base.Predicate; import com.google.common.collect.Lists; @@ -24,6 +25,7 @@ import java.util.Arrays; import java.util.Collections; import java.util.Comparator; +import java.util.Iterator; import java.util.List; import java.util.concurrent.Executor; @@ -36,7 +38,22 @@ public class Sequences public static Sequence simple(final Iterable iterable) { - return BaseSequence.simple(iterable); + return new BaseSequence<>( + new BaseSequence.IteratorMaker>() + { + @Override + public Iterator make() + { + return iterable.iterator(); + } + + @Override + public void cleanup(Iterator iterFromMake) + { + + } + } + ); } @SuppressWarnings("unchecked") @@ -75,9 +92,28 @@ public static Sequence limit(final Sequence sequence, final int limit) return new LimitedSequence<>(sequence, limit); } - public static Sequence withBaggage(final Sequence seq, Closeable baggage) + public static Sequence withBaggage(final Sequence seq, final Closeable baggage) { - return new ResourceClosingSequence<>(seq, baggage); + Preconditions.checkNotNull(baggage, "baggage"); + return wrap(seq, new SequenceWrapper() + { + @Override + public void after(boolean isDone, Throwable thrown) throws Exception + { + baggage.close(); + } + }); + } + + /** + * Allows to execute something before, after or around the processing of the given sequence. See documentation to + * {@link SequenceWrapper} methods for some details. + */ + public static Sequence wrap(Sequence seq, SequenceWrapper wrapper) + { + Preconditions.checkNotNull(seq, "seq"); + Preconditions.checkNotNull(wrapper, "wrapper"); + return new WrappingSequence<>(seq, wrapper); } public static Sequence withEffect(final Sequence seq, final Runnable effect, final Executor exec) @@ -105,7 +141,7 @@ public static Sequence sort(final Sequence sequence, final Comparator< { List seqList = Sequences.toList(sequence, Lists.newArrayList()); Collections.sort(seqList, comparator); - return BaseSequence.simple(seqList); + return simple(seqList); } public static > ListType toList(Sequence seq, ListType list) diff --git a/src/main/java/com/metamx/common/guava/SimpleSequence.java b/src/main/java/com/metamx/common/guava/SimpleSequence.java deleted file mode 100644 index 6fc4fb83..00000000 --- a/src/main/java/com/metamx/common/guava/SimpleSequence.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright 2011,2012 Metamarkets Group Inc. - * - * Licensed 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 com.metamx.common.guava; - -import java.util.Iterator; - -/** - */ -public class SimpleSequence extends BaseSequence> -{ - public static Sequence create(Iterable iterable) - { - return new SimpleSequence<>(iterable); - } - - public SimpleSequence( - final Iterable iterable - ) - { - super( - new IteratorMaker>() - { - @Override - public Iterator make() - { - return iterable.iterator(); - } - - @Override - public void cleanup(Iterator iterFromMake) - { - - } - } - ); - } - -} diff --git a/src/main/java/com/metamx/common/guava/WrappingSequence.java b/src/main/java/com/metamx/common/guava/WrappingSequence.java new file mode 100644 index 00000000..8a5a1abf --- /dev/null +++ b/src/main/java/com/metamx/common/guava/WrappingSequence.java @@ -0,0 +1,100 @@ +/* + * Copyright 2016 Metamarkets Group Inc. + * + * Licensed 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 com.metamx.common.guava; + +import com.google.common.base.Preconditions; +import com.google.common.base.Supplier; +import com.google.common.base.Throwables; + +/** + */ +final class WrappingSequence implements Sequence +{ + private final Sequence baseSequence; + private final SequenceWrapper wrapper; + + WrappingSequence(Sequence baseSequence, SequenceWrapper wrapper) + { + this.baseSequence = Preconditions.checkNotNull(baseSequence, "baseSequence"); + this.wrapper = Preconditions.checkNotNull(wrapper, "wrapper"); + } + + @Override + public OutType accumulate(final OutType outType, final Accumulator accumulator) + { + OutType result; + try { + wrapper.before(); + result = wrapper.wrap(new Supplier() + { + @Override + public OutType get() + { + return baseSequence.accumulate(outType, accumulator); + } + }); + } + catch (Throwable t) { + // Close on failure + try { + wrapper.after(false, t); + } + catch (Exception e) { + t.addSuppressed(e); + } + throw Throwables.propagate(t); + } + // "Normal" close + try { + wrapper.after(true, null); + } + catch (Exception e) { + throw Throwables.propagate(e); + } + return result; + } + + @Override + public Yielder toYielder( + final OutType initValue, + final YieldingAccumulator accumulator + ) + { + try { + wrapper.before(); + return wrapper.wrap(new Supplier>() + { + @Override + public Yielder get() + { + return new WrappingYielder<>(baseSequence.toYielder(initValue, accumulator), wrapper); + } + }); + } + catch (Throwable t) { + // Close on failure + try { + wrapper.after(false, t); + } + catch (Exception e) { + t.addSuppressed(e); + } + throw Throwables.propagate(t); + } + } +} diff --git a/src/main/java/com/metamx/common/guava/WrappingYielder.java b/src/main/java/com/metamx/common/guava/WrappingYielder.java new file mode 100644 index 00000000..bf412fc4 --- /dev/null +++ b/src/main/java/com/metamx/common/guava/WrappingYielder.java @@ -0,0 +1,101 @@ +/* + * Copyright 2016 Metamarkets Group Inc. + * + * Licensed 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 com.metamx.common.guava; + +import com.google.common.base.Supplier; +import com.google.common.base.Throwables; + +import java.io.IOException; + +final class WrappingYielder implements Yielder +{ + private final Yielder baseYielder; + private final SequenceWrapper wrapper; + + WrappingYielder(Yielder baseYielder, SequenceWrapper wrapper) + { + this.baseYielder = baseYielder; + this.wrapper = wrapper; + } + + @Override + public OutType get() + { + return baseYielder.get(); + } + + @Override + public Yielder next(final OutType initValue) + { + try { + return wrapper.wrap(new Supplier>() + { + @Override + public Yielder get() + { + return new WrappingYielder<>(baseYielder.next(initValue), wrapper); + } + }); + } + catch (Throwable t) { + // Close on failure + try { + wrapper.after(false, t); + } + catch (Exception e) { + t.addSuppressed(e); + } + throw Throwables.propagate(t); + } + } + + @Override + public boolean isDone() + { + return baseYielder.isDone(); + } + + @Override + public void close() throws IOException + { + boolean isDone; + try { + isDone = isDone(); + baseYielder.close(); + } + catch (Throwable t) { + // Close on failure + try { + wrapper.after(false, t); + } + catch (Exception e) { + t.addSuppressed(e); + } + Throwables.propagateIfInstanceOf(t, IOException.class); + throw Throwables.propagate(t); + } + // "Normal" close + try { + wrapper.after(isDone, null); + } + catch (Exception e) { + Throwables.propagateIfInstanceOf(e, IOException.class); + throw Throwables.propagate(e); + } + } +} diff --git a/src/main/java/com/metamx/common/guava/YieldSign.java b/src/main/java/com/metamx/common/guava/YieldSign.java deleted file mode 100644 index 61b4875c..00000000 --- a/src/main/java/com/metamx/common/guava/YieldSign.java +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright 2011,2012 Metamarkets Group Inc. - * - * Licensed 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 com.metamx.common.guava; - -/** - */ -public interface YieldSign -{ - public T yield(T toYield); -} diff --git a/src/main/java/com/metamx/common/guava/Yielders.java b/src/main/java/com/metamx/common/guava/Yielders.java index 487e46e9..9c5a84e2 100644 --- a/src/main/java/com/metamx/common/guava/Yielders.java +++ b/src/main/java/com/metamx/common/guava/Yielders.java @@ -16,16 +16,15 @@ package com.metamx.common.guava; -import com.google.common.io.Closeables; +import com.google.common.base.Throwables; -import java.io.Closeable; import java.io.IOException; /** */ public class Yielders { - public static Yielder done(final T finalVal, final Closeable closeable) + public static Yielder done(final T finalVal, final AutoCloseable closeable) { return new Yielder() { @@ -50,7 +49,15 @@ public boolean isDone() @Override public void close() throws IOException { - Closeables.close(closeable, false); + if (closeable != null) { + try { + closeable.close(); + } + catch (Exception e) { + Throwables.propagateIfInstanceOf(e, IOException.class); + throw Throwables.propagate(e); + } + } } }; } diff --git a/src/main/java/com/metamx/common/guava/YieldingSequenceBase.java b/src/main/java/com/metamx/common/guava/YieldingSequenceBase.java index 93f19a2f..2ccc1ad4 100644 --- a/src/main/java/com/metamx/common/guava/YieldingSequenceBase.java +++ b/src/main/java/com/metamx/common/guava/YieldingSequenceBase.java @@ -16,11 +16,6 @@ package com.metamx.common.guava; -import com.google.common.base.Throwables; -import com.google.common.io.Closeables; - -import java.io.IOException; - /** * A Sequence that is based entirely on the Yielder implementation. *

diff --git a/src/test/java/com/metamx/common/guava/BaseSequenceTest.java b/src/test/java/com/metamx/common/guava/BaseSequenceTest.java index c3a3feb6..59edbfd9 100644 --- a/src/test/java/com/metamx/common/guava/BaseSequenceTest.java +++ b/src/test/java/com/metamx/common/guava/BaseSequenceTest.java @@ -31,14 +31,14 @@ public class BaseSequenceTest public void testSanity() throws Exception { final List vals = Arrays.asList(1, 2, 3, 4, 5); - SequenceTestHelper.testAll(BaseSequence.simple(vals), vals); + SequenceTestHelper.testAll(Sequences.simple(vals), vals); } @Test public void testNothing() throws Exception { final List vals = Arrays.asList(); - SequenceTestHelper.testAll(BaseSequence.simple(vals), vals); + SequenceTestHelper.testAll(Sequences.simple(vals), vals); } @Test diff --git a/src/test/java/com/metamx/common/guava/ExecutorExecutingSequenceTest.java b/src/test/java/com/metamx/common/guava/ExecutorExecutingSequenceTest.java deleted file mode 100644 index 51119df2..00000000 --- a/src/test/java/com/metamx/common/guava/ExecutorExecutingSequenceTest.java +++ /dev/null @@ -1,171 +0,0 @@ -/* - * Copyright 2011,2012 Metamarkets Group Inc. - * - * Licensed 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 com.metamx.common.guava; - -import com.google.common.base.Throwables; -import com.google.common.util.concurrent.Futures; -import junit.framework.Assert; -import org.junit.Test; - -import java.util.Arrays; -import java.util.Collection; -import java.util.List; -import java.util.concurrent.Callable; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Future; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.TimeoutException; -import java.util.concurrent.atomic.AtomicInteger; - -/** - */ -public class ExecutorExecutingSequenceTest -{ - @Test - public void testSanity() throws Exception - { - TestExecutor exec = new TestExecutor(); - final List vals = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13); - ExecutorExecutingSequence seq = new ExecutorExecutingSequence<>(Sequences.simple(vals), exec); - - SequenceTestHelper.testAccumulation("", seq, vals); - Assert.assertEquals(1, exec.getTimesCalled()); - - exec.reset(); - - SequenceTestHelper.testYield("", 3, seq, vals); - Assert.assertEquals(5, exec.getTimesCalled()); - } - - @Test - public void testSanity2() throws Exception - { - TestExecutor exec = new TestExecutor(); - final List vals = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15); - ExecutorExecutingSequence seq = new ExecutorExecutingSequence<>(Sequences.simple(vals), exec); - - SequenceTestHelper.testAccumulation("", seq, vals); - Assert.assertEquals(1, exec.getTimesCalled()); - - exec.reset(); - - SequenceTestHelper.testYield("", 3, seq, vals); - Assert.assertEquals(6, exec.getTimesCalled()); - } - - public static class TestExecutor implements ExecutorService - { - int timesCalled = 0; - - public int getTimesCalled() - { - return timesCalled; - } - - public void reset() - { - timesCalled = 0; - } - - @Override - public void shutdown() - { - throw new UnsupportedOperationException(); - } - - @Override - public List shutdownNow() - { - throw new UnsupportedOperationException(); - } - - @Override - public boolean isShutdown() - { - throw new UnsupportedOperationException(); - } - - @Override - public boolean isTerminated() - { - throw new UnsupportedOperationException(); - } - - @Override - public boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException - { - throw new UnsupportedOperationException(); - } - - @Override - public Future submit(Callable task) - { - ++timesCalled; - try { - return Futures.immediateCheckedFuture(task.call()); - } - catch (Exception e) { - throw Throwables.propagate(e); - } - } - - @Override - public Future submit(Runnable task, T result) - { - throw new UnsupportedOperationException(); - } - - @Override - public Future submit(Runnable task) - { - throw new UnsupportedOperationException(); - } - - @Override - public List> invokeAll(Collection> tasks) throws InterruptedException - { - throw new UnsupportedOperationException(); - } - - @Override - public List> invokeAll(Collection> tasks, long timeout, TimeUnit unit) - throws InterruptedException - { - throw new UnsupportedOperationException(); - } - - @Override - public T invokeAny(Collection> tasks) throws InterruptedException, ExecutionException - { - throw new UnsupportedOperationException(); - } - - @Override - public T invokeAny(Collection> tasks, long timeout, TimeUnit unit) - throws InterruptedException, ExecutionException, TimeoutException - { - throw new UnsupportedOperationException(); - } - - @Override - public void execute(Runnable command) - { - throw new UnsupportedOperationException(); - } - } -} diff --git a/src/test/java/com/metamx/common/guava/TestSequence.java b/src/test/java/com/metamx/common/guava/TestSequence.java index dd0add25..4727896b 100644 --- a/src/test/java/com/metamx/common/guava/TestSequence.java +++ b/src/test/java/com/metamx/common/guava/TestSequence.java @@ -16,12 +16,12 @@ package com.metamx.common.guava; +import java.io.Closeable; import java.util.Arrays; -import java.util.Iterator; import java.util.concurrent.atomic.AtomicBoolean; /** -*/ + */ public class TestSequence implements Sequence { public static TestSequence create(Iterable iterable) @@ -39,21 +39,17 @@ public static TestSequence create(T... vals) public TestSequence(final Iterable iterable) { - base = new BaseSequence<>( - new BaseSequence.IteratorMaker>() - { - @Override - public Iterator make() - { - return iterable.iterator(); - } - - @Override - public void cleanup(Iterator iterFromMake) - { - closed.set(true); - } - }); + base = Sequences.withBaggage( + Sequences.simple(iterable), + new Closeable() + { + @Override + public void close() + { + closed.set(true); + } + } + ); } @Override diff --git a/src/test/java/com/metamx/common/guava/WithEffectSequenceTest.java b/src/test/java/com/metamx/common/guava/WithEffectSequenceTest.java new file mode 100644 index 00000000..56e2fd14 --- /dev/null +++ b/src/test/java/com/metamx/common/guava/WithEffectSequenceTest.java @@ -0,0 +1,73 @@ +/* + * Copyright 2016 Metamarkets Group Inc. + * + * Licensed 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 com.metamx.common.guava; + +import com.google.common.util.concurrent.MoreExecutors; +import org.junit.Assert; +import org.junit.Test; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.concurrent.atomic.AtomicInteger; + +public class WithEffectSequenceTest +{ + @Test + public void testConsistentEffectApplicationOrder() + { + final AtomicInteger effect1 = new AtomicInteger(); + final AtomicInteger effect2 = new AtomicInteger(); + final AtomicInteger counter = new AtomicInteger(); + + Sequence sequence = Sequences.withEffect( + Sequences.withEffect( + Sequences.simple(Arrays.asList(1, 2, 3)), + new Runnable() + { + @Override + public void run() + { + effect1.set(counter.incrementAndGet()); + } + }, + MoreExecutors.sameThreadExecutor() + ), + new Runnable() + { + @Override + public void run() + { + effect2.set(counter.incrementAndGet()); + } + }, + MoreExecutors.sameThreadExecutor() + ); + // Run sequence via accumulate + Sequences.toList(sequence, new ArrayList()); + Assert.assertEquals(1, effect1.get()); + Assert.assertEquals(2, effect2.get()); + + // Ensure sequence runs via Yielder, because LimitedSequence extends YieldingSequenceBase + // "Limiting" a sequence of 3 elements with 4 to let effects be executed. If e. g. limit with 1 or 2, effects are + // not executed. + Sequence yieldingSequence = Sequences.limit(sequence, 4); + Sequences.toList(yieldingSequence, new ArrayList()); + Assert.assertEquals(3, effect1.get()); + Assert.assertEquals(4, effect2.get()); + } +} diff --git a/src/test/java/com/metamx/common/guava/ResourceClosingSequenceTest.java b/src/test/java/com/metamx/common/guava/WrappingSequenceTest.java similarity index 52% rename from src/test/java/com/metamx/common/guava/ResourceClosingSequenceTest.java rename to src/test/java/com/metamx/common/guava/WrappingSequenceTest.java index e84bf06b..4659dcbb 100644 --- a/src/test/java/com/metamx/common/guava/ResourceClosingSequenceTest.java +++ b/src/test/java/com/metamx/common/guava/WrappingSequenceTest.java @@ -21,13 +21,14 @@ import java.io.Closeable; import java.io.IOException; +import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.concurrent.atomic.AtomicInteger; /** */ -public class ResourceClosingSequenceTest +public class WrappingSequenceTest { @Test public void testSanity() throws Exception @@ -51,4 +52,44 @@ public void close() throws IOException closedCounter.set(0); SequenceTestHelper.testClosed(closedCounter, Sequences.withBaggage(new UnsupportedSequence(), closeable)); } + + @Test + public void testConsistentCloseOrder() + { + final AtomicInteger closed1 = new AtomicInteger(); + final AtomicInteger closed2 = new AtomicInteger(); + final AtomicInteger counter = new AtomicInteger(); + + Sequence sequence = Sequences.withBaggage( + Sequences.withBaggage( + Sequences.simple(Arrays.asList(1, 2, 3)), + new Closeable() + { + @Override + public void close() throws IOException + { + closed1.set(counter.incrementAndGet()); + } + } + ), + new Closeable() + { + @Override + public void close() throws IOException + { + closed2.set(counter.incrementAndGet()); + } + } + ); + // Run sequence via accumulate + Sequences.toList(sequence, new ArrayList()); + Assert.assertEquals(1, closed1.get()); + Assert.assertEquals(2, closed2.get()); + + // Ensure sequence runs via Yielder, because LimitedSequence extends YieldingSequenceBase + Sequence yieldingSequence = Sequences.limit(sequence, 1); + Sequences.toList(yieldingSequence, new ArrayList()); + Assert.assertEquals(3, closed1.get()); + Assert.assertEquals(4, closed2.get()); + } }