http://git-wip-us.apache.org/repos/asf/aurora/blob/06ddaadb/commons/src/main/java/org/apache/aurora/common/base/Command.java ---------------------------------------------------------------------- diff --git a/commons/src/main/java/org/apache/aurora/common/base/Command.java b/commons/src/main/java/org/apache/aurora/common/base/Command.java new file mode 100644 index 0000000..b33bee4 --- /dev/null +++ b/commons/src/main/java/org/apache/aurora/common/base/Command.java @@ -0,0 +1,23 @@ +/** + * 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 org.apache.aurora.common.base; + +/** + * A command that does not throw any checked exceptions. + * + * @author John Sirois + */ +public interface Command extends ExceptionalCommand<RuntimeException> { + // convenience typedef +}
http://git-wip-us.apache.org/repos/asf/aurora/blob/06ddaadb/commons/src/main/java/org/apache/aurora/common/base/Commands.java ---------------------------------------------------------------------- diff --git a/commons/src/main/java/org/apache/aurora/common/base/Commands.java b/commons/src/main/java/org/apache/aurora/common/base/Commands.java new file mode 100644 index 0000000..6d9fc4c --- /dev/null +++ b/commons/src/main/java/org/apache/aurora/common/base/Commands.java @@ -0,0 +1,77 @@ +/** + * 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 org.apache.aurora.common.base; + +import com.google.common.collect.ImmutableList; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * Utility functions for working with commands. + * + * @author John Sirois + */ +public final class Commands { + + /** + * A command that does nothing when executed. + */ + public static final Command NOOP = new Command() { + @Override public void execute() { + // noop + } + }; + + private Commands() { + // utility + } + + /** + * Converts a command into a supplier returning null. + * + * @return A supplier whose {@link Supplier#get()} will cause the given + * {@code command} to be executed and {@code null} to be returned. + */ + public static <E extends Exception> ExceptionalSupplier<Void, E> asSupplier( + final ExceptionalCommand<E> command) { + checkNotNull(command); + + return new ExceptionalSupplier<Void, E>() { + @Override public Void get() throws E { + command.execute(); + return null; + } + }; + } + + /** + * Combines multiple {@code commands} into a single command. A {@link RuntimeException} thrown + * during the execution of one of the commands will prevent the subsequent commands from being + * executed. + * + * @param commands Commands to compound. + * @return A command whose {@link Command#execute()} will cause the given {@code commands} to be + * executed serially. + */ + public static Command compound(Iterable<Command> commands) { + final ImmutableList<Command> executableCommands = ImmutableList.copyOf(commands); + return new Command() { + @Override public void execute() { + for (Command command : executableCommands) { + command.execute(); + } + } + }; + } +} http://git-wip-us.apache.org/repos/asf/aurora/blob/06ddaadb/commons/src/main/java/org/apache/aurora/common/base/Either.java ---------------------------------------------------------------------- diff --git a/commons/src/main/java/org/apache/aurora/common/base/Either.java b/commons/src/main/java/org/apache/aurora/common/base/Either.java new file mode 100644 index 0000000..a746e72 --- /dev/null +++ b/commons/src/main/java/org/apache/aurora/common/base/Either.java @@ -0,0 +1,358 @@ +/** + * 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 org.apache.aurora.common.base; + +import javax.annotation.Nullable; + +import com.google.common.base.Objects; +import com.google.common.base.Optional; +import com.google.common.collect.Iterables; +import com.google.common.collect.Lists; + +/** + * A value of one of two possible types. + * + * <p>Often Either processing is used as an alternative exception flow control. In these uses the + * left type represents failure by convention and the right type the success path result. + * + * @param <L> The left type. + * @param <R> The right type. + */ +public final class Either<L, R> { + private final Optional<L> left; + private final Optional<R> right; + + private Either(Optional<L> left, Optional<R> right) { + this.left = left; + this.right = right; + } + + /** + * Turns a left into a right and vice-versa. + * + * @return A new swapped either instance. + */ + public Either<R, L> swap() { + return new Either<R, L>(right, left); + } + + /** + * Returns an optional the will be {@link Optional#isPresent() present} is this is a left + * instance. + * + * @return An optional value for the left. + */ + public Optional<L> left() { + return left; + } + + /** + * Returns an optional the will be {@link Optional#isPresent() present} is this is a right + * instance. + * + * @return An optional value for the right. + */ + public Optional<R> right() { + return right; + } + + /** + * Returns {@code true} if this is a left instance. + * + * @return {@code true} if this is a left. + */ + public boolean isLeft() { + return left().isPresent(); + } + + /** + * Returns {@code true} if this is a right instance. + * + * @return {@code true} if this is a right. + */ + public boolean isRight() { + return right().isPresent(); + } + + /** + * Returns the underlying value if this is a left; otherwise, throws. + * + * @return The underlying value. + * @throws IllegalStateException if this is a right instance. + */ + public L getLeft() { + return left().get(); + } + + /** + * Returns the underlying value if this is a right; otherwise, throws. + * + * @return The underlying value. + * @throws IllegalStateException if this is a right instance. + */ + public R getRight() { + return right().get(); + } + + /** + * If this is a left, maps its value into a new left; otherwise just returns this right. + * + * @param transformer The transformation to apply to the left value. + * @param <M> The type a left value will be mapped to. + * @return The mapped left or else the right. + */ + public <M> Either<M, R> mapLeft(Function<? super L, M> transformer) { + if (isLeft()) { + return left(transformer.apply(getLeft())); + } else { + @SuppressWarnings("unchecked") // I am a right so my left is never accessible + Either<M, R> self = (Either<M, R>) this; + return self; + } + } + + /** + * If this is a right, maps its value into a new right; otherwise just returns this left. + * + * @param transformer The transformation to apply to the left value. + * @param <M> The type a right value will be mapped to. + * @return The mapped right or else the left. + */ + public <M> Either<L, M> mapRight(Function<? super R, M> transformer) { + if (isRight()) { + return right(transformer.apply(getRight())); + } else { + @SuppressWarnings("unchecked") // I am a left so my right is never accessible + Either<L, M> self = (Either<L, M>) this; + return self; + } + } + + /** + * Can transform either a left or a right into a result. + * + * @param <L> The left type. + * @param <R> The right type. + * @param <T> The transformation result type. + */ + public abstract static class Transformer<L, R, T> implements Function<Either<L, R>, T> { + + /** + * Maps left values to a result. + * + * @param left the left value to map. + * @return The mapped value. + */ + public abstract T mapLeft(L left); + + /** + * Maps right values to a result. + * + * @param right the right value to map. + * @return The mapped value. + */ + public abstract T mapRight(R right); + + @Override + public final T apply(Either<L, R> either) { + return either.map(this); + } + } + + /** + * Creates a transformer from left and right transformation functions. + * + * @param leftTransformer A transformer to process left values. + * @param rightTransformer A transformer to process right values. + * @param <L> The left type. + * @param <R> The right type. + * @param <T> The transformation result type. + * @return A new transformer composed of left and right transformer functions. + */ + public static <L, R, T> Transformer<L, R, T> transformer( + final Function<? super L, T> leftTransformer, + final Function<? super R, T> rightTransformer) { + + return new Transformer<L, R, T>() { + @Override public T mapLeft(L item) { + return leftTransformer.apply(item); + } + @Override public T mapRight(R item) { + return rightTransformer.apply(item); + } + }; + } + + /** + * Transforms this either instance to a value regardless of whether it is a left or a right. + * + * @param transformer The transformer to map this either instance. + * @param <T> The type the transformer produces. + * @return A value mapped by the transformer from this left or right instance. + */ + public <T> T map(final Transformer<? super L, ? super R, T> transformer) { + if (isLeft()) { + return transformer.mapLeft(getLeft()); + } else { + return transformer.mapRight(getRight()); + } + } + + @Override + public boolean equals(@Nullable Object o) { + if (!(o instanceof Either)) { + return false; + } + Either<?, ?> other = (Either<?, ?>) o; + return Objects.equal(left, other.left) + && Objects.equal(right, other.right); + } + + @Override + public int hashCode() { + return Objects.hashCode(left, right); + } + + @Override + public String toString() { + if (isLeft()) { + return String.format("Left(%s)", getLeft()); + } else { + return String.format("Right(%s)", getRight()); + } + } + + /** + * Creates a left either instance. + * + * @param value The left value to wrap - may not be null. + * @param <L> The left type. + * @param <R> The right type. + * @return A left either instance wrapping {@code value}. + */ + public static <L, R> Either<L, R> left(L value) { + return new Either<L, R>(Optional.of(value), Optional.<R>absent()); + } + + /** + * Creates a right either instance. + * + * @param value The right value to wrap - may not be null. + * @param <L> The left type. + * @param <R> The right type. + * @return A right either instance wrapping {@code value}. + */ + public static <L, R> Either<L, R> right(R value) { + return new Either<L, R>(Optional.<L>absent(), Optional.of(value)); + } + + /** + * Extracts all the lefts from a sequence of eithers lazily. + * + * @param results A sequence of either's to process. + * @param <L> The left type. + * @param <R> The right type. + * @return A lazy iterable that will produce the lefts present in results in order. + */ + public static <L, R> Iterable<L> lefts(Iterable<Either<L, R>> results) { + return Optional.presentInstances(Iterables.transform(results, + new Function<Either<L, R>, Optional<L>>() { + @Override public Optional<L> apply(Either<L, R> item) { + return item.left(); + } + })); + } + + /** + * Extracts all the rights from a sequence of eithers lazily. + * + * @param results A sequence of either's to process. + * @param <L> The left type. + * @param <R> The right type. + * @return A lazy iterable that will produce the rights present in results in order. + */ + public static <L, R> Iterable<R> rights(Iterable<Either<L, R>> results) { + return Optional.presentInstances(Iterables.transform(results, + new Function<Either<L, R>, Optional<R>>() { + @Override public Optional<R> apply(Either<L, R> item) { + return item.right(); + } + })); + } + + /** + * A convenience method equivalent to calling {@code guard(work, exceptionType)}. + */ + public static <X extends Exception, R> Either<X, R> guard( + Class<X> exceptionType, + ExceptionalSupplier<R, X> work) { + + @SuppressWarnings("unchecked") + Either<X, R> either = guard(work, exceptionType); + return either; + } + + /** + * A convenience method equivalent to calling + * {@code guard(Lists.asList(execpetionType, rest), work)}. + */ + public static <X extends Exception, R> Either<X, R> guard( + ExceptionalSupplier<R, X> work, + Class<? extends X> exceptionType, + Class<? extends X>... rest) { + + return guard(Lists.asList(exceptionType, rest), work); + } + + /** + * Thrown when guarded work throws an unguarded exception. The {@link #getCause() cause} will + * contain the original unguarded exception. + */ + public static class UnguardedException extends RuntimeException { + public UnguardedException(Throwable cause) { + super(cause); + } + } + + /** + * Converts work that can throw exceptions into an either with a left exception base type. This + * can be useful to fold an exception throwing library call into an either processing style + * pipeline. + * + * @param exceptionTypes The expected exception types. + * @param work The work to perform to get a result produce an error. + * @param <X> The base error type. + * @param <R> The success type. + * @return An either wrapping the result of performing {@code work}. + * @throws UnguardedException if work throws an unguarded exception type. + */ + public static <X extends Exception, R> Either<X, R> guard( + Iterable<Class<? extends X>> exceptionTypes, + ExceptionalSupplier<R, X> work) { + + try { + return right(work.get()); + // We're explicitly dealing with generic exception types here by design. + // SUPPRESS CHECKSTYLE RegexpSinglelineJava + } catch (Exception e) { + for (Class<? extends X> exceptionType : exceptionTypes) { + if (exceptionType.isInstance(e)) { + X exception = exceptionType.cast(e); + return left(exception); + } + } + throw new UnguardedException(e); + } + } +} http://git-wip-us.apache.org/repos/asf/aurora/blob/06ddaadb/commons/src/main/java/org/apache/aurora/common/base/ExceptionTransporter.java ---------------------------------------------------------------------- diff --git a/commons/src/main/java/org/apache/aurora/common/base/ExceptionTransporter.java b/commons/src/main/java/org/apache/aurora/common/base/ExceptionTransporter.java new file mode 100644 index 0000000..e5cb60d --- /dev/null +++ b/commons/src/main/java/org/apache/aurora/common/base/ExceptionTransporter.java @@ -0,0 +1,84 @@ +/** + * 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 org.apache.aurora.common.base; + +import com.google.common.base.Function; + +/** + * A utility for transporting checked exceptions across boundaries that do not allow for checked + * exception propagation. + * + * @param <E> The type of checked exception the ExceptionTransported can transport + * + * @author John Sirois + */ +public class ExceptionTransporter<E extends Exception> { + + /** + * An exception wrapper used to transport checked exceptions. Never leaves an + * {@link ExceptionTransporter#guard(com.google.common.base.Function)} call. + */ + private static final class TransportingException extends RuntimeException { + private TransportingException(Exception cause) { + super("It is a usage error to see this message!", cause); + } + } + + /** + * Guards a unit of work that internally can generate checked exceptions. Callers wrap up the + * work in a function that rethrows any checked exceptions using the supplied + * ExceptionTransporter. Guard will ensure the original exception is unwrapped an re-thrown. + * + * @param work The unit of work that guards its checked exceptions. + * @param <T> The type returned by the unit of work when it successfully completes. + * @param <X> The type of checked exception that the unit of work wishes to guard. + * @return the result of the unit of work if no excpetions are thrown + * @throws X when the unit of work uses the ExceptionTransporter to throw a checked exception + */ + public static <T, X extends Exception> T guard(Function<ExceptionTransporter<X>, T> work) + throws X { + + try { + return work.apply(new ExceptionTransporter<X>()); + } catch (TransportingException e) { + @SuppressWarnings("unchecked") + X cause = (X) e.getCause(); + throw cause; + } + } + + /** + * Throws the given {@code checked} exception across a boundary that does not allow checked + * exceptions. Although a RuntimeException is returned by this method signature, the method never + * actually completes normally. The return type does allow the following usage idiom however: + * <pre> + * public String apply(ExceptionTransporter transporter) { + * try { + * return doChecked(); + * } catch (CheckedException e) { + * // Although transport internally throws and does not return, we satisfy the compiler that + * // our method returns a value or throws by pretending to throw the RuntimeException that + * // never actually gets returned by transporter.transport(...) + * throw transporter.transport(e); + * } + * } + * </pre> + * + * @param checked The checked exception to transport. + * @return A RuntimeException that can be thrown to satisfy the compiler at the call site + */ + public RuntimeException transport(E checked) { + throw new TransportingException(checked); + } +} http://git-wip-us.apache.org/repos/asf/aurora/blob/06ddaadb/commons/src/main/java/org/apache/aurora/common/base/ExceptionalClosure.java ---------------------------------------------------------------------- diff --git a/commons/src/main/java/org/apache/aurora/common/base/ExceptionalClosure.java b/commons/src/main/java/org/apache/aurora/common/base/ExceptionalClosure.java new file mode 100644 index 0000000..66b3314 --- /dev/null +++ b/commons/src/main/java/org/apache/aurora/common/base/ExceptionalClosure.java @@ -0,0 +1,35 @@ +/** + * 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 org.apache.aurora.common.base; + +/** + * An interface that captures a unit of work against an item. + * + * @param <T> The closure type. + * @param <E> The exception type thrown by the closure. + * + * @author John Sirois + */ +public interface ExceptionalClosure<T, E extends Exception> { + + /** + * Performs a unit of work on item, possibly throwing {@code E} in the process. + * + * <p>TODO(John Sirois): consider supporting @Nullable + * + * @param item the item to perform work against + * @throws E if there was a problem performing the work + */ + void execute(T item) throws E; +} http://git-wip-us.apache.org/repos/asf/aurora/blob/06ddaadb/commons/src/main/java/org/apache/aurora/common/base/ExceptionalCommand.java ---------------------------------------------------------------------- diff --git a/commons/src/main/java/org/apache/aurora/common/base/ExceptionalCommand.java b/commons/src/main/java/org/apache/aurora/common/base/ExceptionalCommand.java new file mode 100644 index 0000000..8e38133 --- /dev/null +++ b/commons/src/main/java/org/apache/aurora/common/base/ExceptionalCommand.java @@ -0,0 +1,31 @@ +/** + * 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 org.apache.aurora.common.base; + +/** + * An interface that captures a unit of work. + * + * @param <E> The type of exception that the command throws. + * + * @author John Sirois + */ +public interface ExceptionalCommand<E extends Exception> { + + /** + * Performs a unit of work, possibly throwing {@code E} in the process. + * + * @throws E if there was a problem performing the work + */ + void execute() throws E; +} http://git-wip-us.apache.org/repos/asf/aurora/blob/06ddaadb/commons/src/main/java/org/apache/aurora/common/base/ExceptionalFunction.java ---------------------------------------------------------------------- diff --git a/commons/src/main/java/org/apache/aurora/common/base/ExceptionalFunction.java b/commons/src/main/java/org/apache/aurora/common/base/ExceptionalFunction.java new file mode 100644 index 0000000..f867c35 --- /dev/null +++ b/commons/src/main/java/org/apache/aurora/common/base/ExceptionalFunction.java @@ -0,0 +1,37 @@ +/** + * 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 org.apache.aurora.common.base; + +/** + * An interface that captures a unit of work against an item. + * + * @param <S> The argument type for the function. + * @param <T> The return type for the function. + * @param <E> The exception type that the function throws. + * + * @author John Sirois + */ +public interface ExceptionalFunction<S, T, E extends Exception> { + + /** + * Performs a unit of work on item, possibly throwing {@code E} in the process. + * + * <p>TODO(John Sirois): consider supporting @Nullable + * + * @param item The item to perform work against. + * @return The result of the computation. + * @throws E if there was a problem performing the work. + */ + T apply(S item) throws E; +} http://git-wip-us.apache.org/repos/asf/aurora/blob/06ddaadb/commons/src/main/java/org/apache/aurora/common/base/ExceptionalFunctions.java ---------------------------------------------------------------------- diff --git a/commons/src/main/java/org/apache/aurora/common/base/ExceptionalFunctions.java b/commons/src/main/java/org/apache/aurora/common/base/ExceptionalFunctions.java new file mode 100644 index 0000000..f06c66c --- /dev/null +++ b/commons/src/main/java/org/apache/aurora/common/base/ExceptionalFunctions.java @@ -0,0 +1,153 @@ +/** + * 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 org.apache.aurora.common.base; + +import com.google.common.collect.ImmutableList; + +/** + * Utility functions for working with exceptional functions. + * + * @author John Sirois + */ +public final class ExceptionalFunctions { + + private ExceptionalFunctions() { + // utility + } + + /** + * Returns an {@link ExceptionalSupplier}/{@link java.util.concurrent.Callable} object that will + * return the result of {@code function} applied to {@code argument}. Evaluation is lazy and + * un-memoized. + */ + public static <S, T, E extends Exception> CallableExceptionalSupplier<T, E> curry( + final ExceptionalFunction<S, T, E> function, final S argument) { + + return new CallableExceptionalSupplier<T, E>() { + @Override + public T get() throws E { + return function.apply(argument); + } + }; + } + + /** + * Returns an ExceptionalFunction that is a composition of multiple ExceptionalFunctions. + */ + public static <T, E extends Exception> ExceptionalFunction<T, T, E> compose( + final Iterable<ExceptionalFunction<T, T, E>> functions) { + return new ExceptionalFunction<T, T, E>() { + @Override + public T apply(T input) throws E { + T result = input; + for (ExceptionalFunction<T, T, E> f : functions) { + result = f.apply(result); + } + return result; + } + }; + } + + /** + * Returns a List of ExceptionalFunctions from variable number of ExceptionalFunctions. + */ + public static <T, E extends Exception> ExceptionalFunction<T, T, E> compose( + ExceptionalFunction<T, T, E> function, ExceptionalFunction<T, T, E>... functions) { + return compose(ImmutableList.<ExceptionalFunction<T, T, E>>builder() + .add(function) + .add(functions) + .build()); + } + + /** + * Returns a new ExceptionalFunction which composes two ExceptionalFunctions of compatible types. + * + * @param second function to apply to result of first. + * @param first function to apply to input item. + * @param <A> input type of first. + * @param <B> input type of second. + * @param <C> output type of second. + * @param <E> exception type. + * @return new composed ExceptionalFunction. + */ + public static <A, B, C, E extends Exception> ExceptionalFunction<A, C, E> compose( + final ExceptionalFunction<B, C, ? extends E> second, + final ExceptionalFunction<A, ? extends B, ? extends E> first) { + return new ExceptionalFunction<A, C, E>() { + @Override + public C apply(A item) throws E { + return second.apply(first.apply(item)); + } + }; + } + + /** + * Builds an ExceptionalFunction from {@link com.google.common.base.Function}. + * + * @param function guava Function. + * @param <S> input type. + * @param <T> output type. + * @param <E> exception type. + * @return new ExceptionalFunction. + */ + public static <S, T, E extends Exception> ExceptionalFunction<S, T, E> forFunction( + final com.google.common.base.Function<S, T> function) { + return new ExceptionalFunction<S, T, E>() { + @Override + public T apply(S item) { + return function.apply(item); + } + }; + } + + /** + * Builds an ExceptionalFunction from a return value. The returned ExceptionalFunction will always + * return the given value. + * + * @param value value to return. + * @param <S> input type. + * @param <T> output type. + * @param <E> exception type. + * @return new ExceptionalFunction. + */ + public static <S, T, E extends Exception> ExceptionalFunction<S, T, E> constant( + final T value) { + return new ExceptionalFunction<S, T, E>() { + @Override + public T apply(S item) throws E { + return value; + } + }; + } + + /** + * Builds an ExceptionalFunction from an Exception. The returned ExceptionalFunction will always + * throw the given Exception. + * + * @param exception exception to throw. + * @param <S> input type. + * @param <T> output type. + * @param <E> exception type. + * @return new ExceptionalFunction. + */ + public static <S, T, E extends Exception> ExceptionalFunction<S, T, E> forException( + final E exception) { + return new ExceptionalFunction<S, T, E>() { + @Override + public T apply(S item) throws E { + throw exception; + } + }; + } +} http://git-wip-us.apache.org/repos/asf/aurora/blob/06ddaadb/commons/src/main/java/org/apache/aurora/common/base/ExceptionalSupplier.java ---------------------------------------------------------------------- diff --git a/commons/src/main/java/org/apache/aurora/common/base/ExceptionalSupplier.java b/commons/src/main/java/org/apache/aurora/common/base/ExceptionalSupplier.java new file mode 100644 index 0000000..27a4be4 --- /dev/null +++ b/commons/src/main/java/org/apache/aurora/common/base/ExceptionalSupplier.java @@ -0,0 +1,33 @@ +/** + * 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 org.apache.aurora.common.base; + +/** + * An interface that captures a source of data. + * + * @param <T> The supplied value type. + * @param <E> The type of exception that the supplier throws. + * + * @author John Sirois + */ +public interface ExceptionalSupplier<T, E extends Exception> { + + /** + * Supplies an item, possibly throwing {@code E} in the process of obtaining the item. + * + * @return the result of the computation + * @throws E if there was a problem performing the work + */ + T get() throws E; +} http://git-wip-us.apache.org/repos/asf/aurora/blob/06ddaadb/commons/src/main/java/org/apache/aurora/common/base/Function.java ---------------------------------------------------------------------- diff --git a/commons/src/main/java/org/apache/aurora/common/base/Function.java b/commons/src/main/java/org/apache/aurora/common/base/Function.java new file mode 100644 index 0000000..d089eb8 --- /dev/null +++ b/commons/src/main/java/org/apache/aurora/common/base/Function.java @@ -0,0 +1,29 @@ +/** + * 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 org.apache.aurora.common.base; + +/** + * A convenience typedef that also ties into google's {@code Function}. + * + * @param <S> The argument type for the function. + * @param <T> The return type for the function. + * + * @author John Sirois + */ +public interface Function<S, T> + extends ExceptionalFunction<S, T, RuntimeException>, com.google.common.base.Function<S, T> { + + @Override + T apply(S item); +} http://git-wip-us.apache.org/repos/asf/aurora/blob/06ddaadb/commons/src/main/java/org/apache/aurora/common/base/MorePreconditions.java ---------------------------------------------------------------------- diff --git a/commons/src/main/java/org/apache/aurora/common/base/MorePreconditions.java b/commons/src/main/java/org/apache/aurora/common/base/MorePreconditions.java new file mode 100644 index 0000000..1fe19d4 --- /dev/null +++ b/commons/src/main/java/org/apache/aurora/common/base/MorePreconditions.java @@ -0,0 +1,144 @@ +/** + * 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 org.apache.aurora.common.base; + +import com.google.common.base.Preconditions; +import com.google.common.collect.Iterables; + +import org.apache.commons.lang.StringUtils; + +/** + * A utility helpful in concisely checking preconditions on arguments. This utility is a complement + * to {@link com.google.common.base.Preconditions}. + * + * @author John Sirois + */ +public final class MorePreconditions { + + private static final String ARG_NOT_BLANK_MSG = "Argument cannot be blank"; + + private MorePreconditions() { + // utility + } + + /** + * Checks that a string is both non-null and non-empty. + * + * @see #checkNotBlank(String, String, Object...) + */ + public static String checkNotBlank(String argument) { + return checkNotBlank(argument, ARG_NOT_BLANK_MSG); + } + + /** + * Checks that a string is both non-null and non-empty. + * + * @param argument the argument to validate + * @param message the message template for validation exception messages where %s serves as the + * sole argument placeholder + * @param args any arguments needed by the message template + * @return the argument if it is valid + * @throws NullPointerException if the argument is null + * @throws IllegalArgumentException if the argument is the empty string or a pure whitespace + * string + */ + public static String checkNotBlank(String argument, String message, Object... args) { + Preconditions.checkNotNull(argument, message, args); + Preconditions.checkArgument(!StringUtils.isBlank(argument), message, args); + return argument; + } + + /** + * Checks that an Iterable is both non-null and non-empty. This method does not check individual + * elements in the Iterable. + * + * @see #checkNotBlank(Iterable, String, Object...) + */ + public static <S, T extends Iterable<S>> T checkNotBlank(T argument) { + return checkNotBlank(argument, ARG_NOT_BLANK_MSG); + } + + /** + * Checks that an Iterable is both non-null and non-empty. This method does not check individual + * elements in the Iterable, it just checks that the Iterable has at least one element. + * + * @param argument the argument to validate + * @param message the message template for validation exception messages where %s serves as the + * sole argument placeholder + * @param args any arguments needed by the message template + * @return the argument if it is valid + * @throws NullPointerException if the argument is null + * @throws IllegalArgumentException if the argument has no iterable elements + */ + public static <S, T extends Iterable<S>> T checkNotBlank(T argument, String message, + Object... args) { + Preconditions.checkNotNull(argument, message, args); + Preconditions.checkArgument(!Iterables.isEmpty(argument), message, args); + return argument; + } + + /** + * Checks that a double falls within a specified range, inclusive + * + * @param argument argument to validate. + * @param minimum minimum possible valid value for the argument. + * @param maximum maximum possible valid value for the argument. + * @param message the message template for validation exception messages where %s serves as the + * sole argument placeholder. + * @return the argument if it is valid. + * @throws IllegalArgumentException if the argument falls outside of the specified range. + */ + public static double checkArgumentRange(double argument, double minimum, double maximum, + String message) { + Preconditions.checkArgument(minimum <= argument, message, argument); + Preconditions.checkArgument(argument <= maximum, message, argument); + return argument; + } + + /** + * Checks that an int falls within a specified range, inclusive + * + * @param argument argument to validate. + * @param minimum minimum possible valid value for the argument. + * @param maximum maximum possible valid value for the argument. + * @param message the message template for validation exception messages where %s serves as the + * sole argument placeholder. + * @return the argument if it is valid. + * @throws IllegalArgumentException if the argument falls outside of the specified range. + */ + public static int checkArgumentRange(int argument, int minimum, int maximum, + String message) { + Preconditions.checkArgument(minimum <= argument, message, argument); + Preconditions.checkArgument(argument <= maximum, message, argument); + return argument; + } + + /** + * Checks that at least one of the specified arguments is true. + * + * @param message the message for validation exception messages. + * @param arguments one or more arguments to check. + * @return true if at least one of the arguments is true. + * @throws IllegalArgumentException if none of the arguments are true. + */ + public static boolean checkArguments(String message, + Boolean... arguments) { + for (Boolean argument : arguments) { + if (argument) { + return true; + } + } + throw new IllegalArgumentException(message); + } +} http://git-wip-us.apache.org/repos/asf/aurora/blob/06ddaadb/commons/src/main/java/org/apache/aurora/common/base/MoreSuppliers.java ---------------------------------------------------------------------- diff --git a/commons/src/main/java/org/apache/aurora/common/base/MoreSuppliers.java b/commons/src/main/java/org/apache/aurora/common/base/MoreSuppliers.java new file mode 100644 index 0000000..9ab877d --- /dev/null +++ b/commons/src/main/java/org/apache/aurora/common/base/MoreSuppliers.java @@ -0,0 +1,103 @@ +/** + * 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 org.apache.aurora.common.base; + +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Modifier; + +import javax.annotation.Nullable; + +import com.google.common.base.Preconditions; + +/** + * Utility methods for working with Suppliers. + * + * @author John Sirois + */ +public final class MoreSuppliers { + + private MoreSuppliers() { + // utility + } + + /** + * Creates a Supplier that uses the no-argument constructor of {@code type} to supply new + * instances. + * + * @param type the type of object this supplier creates + * @param <T> the type of object this supplier creates + * @return a Supplier that created a new obeject of type T on each call to {@link Supplier#get()} + * @throws IllegalArgumentException if the given {@code type} does not have a no-arg constructor + */ + public static <T> Supplier<T> of(final Class<? extends T> type) { + Preconditions.checkNotNull(type); + + try { + final Constructor<? extends T> constructor = getNoArgConstructor(type); + return new Supplier<T>() { + @Override public T get() { + try { + return constructor.newInstance(); + } catch (InstantiationException e) { + throw instantiationFailed(e, type); + } catch (IllegalAccessException e) { + throw instantiationFailed(e, type); + } catch (InvocationTargetException e) { + throw instantiationFailed(e, type); + } + } + }; + } catch (NoSuchMethodException e) { + throw new IllegalArgumentException("No accessible no-arg constructor for " + type, e); + } + } + + private static RuntimeException instantiationFailed(Exception cause, Object type) { + return new RuntimeException("Could not create a new instance of type: " + type, cause); + } + + private static <T> Constructor<T> getNoArgConstructor(Class<T> type) + throws NoSuchMethodException { + + try { + Constructor<T> constructor = type.getConstructor(); + if (!MoreSuppliers.class.getPackage().equals(type.getPackage()) + && !Modifier.isPublic(type.getModifiers())) { + // Handle a public no-args constructor in a non-public class + constructor.setAccessible(true); + } + return constructor; + } catch (NoSuchMethodException e) { + Constructor<T> declaredConstructor = type.getDeclaredConstructor(); + declaredConstructor.setAccessible(true); + return declaredConstructor; + } + } + + /** + * Returns an {@link ExceptionalSupplier} that always supplies {@code item} without error. + * + * @param item The item to supply. + * @param <T> The type of item being supplied. + * @return A supplier that will always supply {@code item}. + */ + public static <T> Supplier<T> ofInstance(@Nullable final T item) { + return new Supplier<T>() { + @Override public T get() { + return item; + } + }; + } +} http://git-wip-us.apache.org/repos/asf/aurora/blob/06ddaadb/commons/src/main/java/org/apache/aurora/common/base/Supplier.java ---------------------------------------------------------------------- diff --git a/commons/src/main/java/org/apache/aurora/common/base/Supplier.java b/commons/src/main/java/org/apache/aurora/common/base/Supplier.java new file mode 100644 index 0000000..cb6e86a --- /dev/null +++ b/commons/src/main/java/org/apache/aurora/common/base/Supplier.java @@ -0,0 +1,28 @@ +/** + * 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 org.apache.aurora.common.base; + +/** + * A convenience typedef that also ties into google's {@code Supplier}. + * + * @param <T> The supplied type. + * + * @author John Sirois + */ +public interface Supplier<T> + extends ExceptionalSupplier<T, RuntimeException>, com.google.common.base.Supplier<T> { + + @Override + T get(); +} http://git-wip-us.apache.org/repos/asf/aurora/blob/06ddaadb/commons/src/main/java/org/apache/aurora/common/base/SupplierE.java ---------------------------------------------------------------------- diff --git a/commons/src/main/java/org/apache/aurora/common/base/SupplierE.java b/commons/src/main/java/org/apache/aurora/common/base/SupplierE.java new file mode 100644 index 0000000..ecad43e --- /dev/null +++ b/commons/src/main/java/org/apache/aurora/common/base/SupplierE.java @@ -0,0 +1,25 @@ +/** + * 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 org.apache.aurora.common.base; + +/** + * A convenience typedef for suppliers that throw multiple exception types. + * + * @param <T> The supplied type. + * + * @author John Sirois + */ +public interface SupplierE<T> extends ExceptionalSupplier<T, Exception> { + // typedef +} http://git-wip-us.apache.org/repos/asf/aurora/blob/06ddaadb/commons/src/main/java/org/apache/aurora/common/collections/Bits.java ---------------------------------------------------------------------- diff --git a/commons/src/main/java/org/apache/aurora/common/collections/Bits.java b/commons/src/main/java/org/apache/aurora/common/collections/Bits.java new file mode 100644 index 0000000..f07e04a --- /dev/null +++ b/commons/src/main/java/org/apache/aurora/common/collections/Bits.java @@ -0,0 +1,116 @@ +/** + * 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 org.apache.aurora.common.collections; + +import com.google.common.base.Preconditions; + +/** + * Convenience class for doing bit-level operations on ints and longs. + * + * @author William Farner + */ +public final class Bits { + + private static final int LSB = 0; + private static final int INT_MSB = 31; + private static final int LONG_MSB = 63; + + private Bits() { + // Utility. + } + + /** + * Tests whether a bit is set in an int value. + * + * @param value The bit field to test. + * @param bit The index of the bit to test, where bit 0 is the LSB. + * @return {@code true} if the bit is set, {@code false} otherwise. + */ + public static boolean isBitSet(int value, int bit) { + Preconditions.checkState(bit >= LSB); + Preconditions.checkState(bit <= INT_MSB); + int mask = 1 << bit; + return (value & mask) != 0; + } + + /** + * Tests whether a bit is set in a long value. + * + * @param value The bit field to test. + * @param bit The index of the bit to test, where bit 0 is the LSB. + * @return {@code true} if the bit is set, {@code false} otherwise. + */ + public static boolean isBitSet(long value, int bit) { + Preconditions.checkState(bit >= LSB); + Preconditions.checkState(bit <= LONG_MSB); + long mask = 1L << bit; + return (value & mask) != 0; + } + + /** + * Sets a bit in an int value. + * + * @param value The bit field to modify. + * @param bit The index of the bit to set, where bit 0 is the LSB. + * @return The original value, with the indexed bit set. + */ + public static int setBit(int value, int bit) { + Preconditions.checkState(bit >= LSB); + Preconditions.checkState(bit <= INT_MSB); + int mask = 1 << bit; + return value | mask; + } + + /** + * Sets a bit in a long value. + * + * @param value The bit field to modify. + * @param bit The index of the bit to set, where bit 0 is the LSB. + * @return The original value, with the indexed bit set. + */ + public static long setBit(long value, int bit) { + Preconditions.checkState(bit >= LSB); + Preconditions.checkState(bit <= LONG_MSB); + long mask = 1L << bit; + return value | mask; + } + + /** + * Clears a bit in an int value. + * + * @param value The bit field to modify. + * @param bit The index of the bit to clear, where bit 0 is the LSB. + * @return The original value, with the indexed bit clear. + */ + public static int clearBit(int value, int bit) { + Preconditions.checkState(bit >= LSB); + Preconditions.checkState(bit <= INT_MSB); + int mask = ~setBit(0, bit); + return value & mask; + } + + /** + * Clears a bit in a long value. + * + * @param value The bit field to modify. + * @param bit The index of the bit to clear, where bit 0 is the LSB. + * @return The original value, with the indexed bit clear. + */ + public static long clearBit(long value, int bit) { + Preconditions.checkState(bit >= LSB); + Preconditions.checkState(bit <= LONG_MSB); + long mask = ~setBit(0L, bit); + return value & mask; + } +} http://git-wip-us.apache.org/repos/asf/aurora/blob/06ddaadb/commons/src/main/java/org/apache/aurora/common/collections/BoundedQueue.java ---------------------------------------------------------------------- diff --git a/commons/src/main/java/org/apache/aurora/common/collections/BoundedQueue.java b/commons/src/main/java/org/apache/aurora/common/collections/BoundedQueue.java new file mode 100644 index 0000000..3e2d5fa --- /dev/null +++ b/commons/src/main/java/org/apache/aurora/common/collections/BoundedQueue.java @@ -0,0 +1,76 @@ +/** + * 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 org.apache.aurora.common.collections; + +import java.util.Iterator; +import java.util.concurrent.LinkedBlockingDeque; + +/** + * A limited implementation of a bounded queue. Values can be added and iterated over, and will + * automatically expire when the queue exceeds capacity. + * + * @param <T> The type that this queue contains. + * + * @author William Farner +*/ +public class BoundedQueue<T> implements Iterable<T> { + private final LinkedBlockingDeque<T> values; + + /** + * Creates a new bounded queue. + * + * @param limit Maximum number of items that can be in the queue at any time. + */ + public BoundedQueue(int limit) { + values = new LinkedBlockingDeque<T>(limit); + } + + /** + * Adds a value to head of the queue, evicting the oldest item if the queue is at capacity. + * + * @param value Value to add. + */ + public synchronized void add(T value) { + if (values.remainingCapacity() == 0) { + values.removeFirst(); + } + values.addLast(value); + } + + /** + * Removes all values from the queue. + */ + public synchronized void clear() { + values.clear(); + } + + /** + * Returns the size of the queue. + * + * @return The current queue length. + */ + public synchronized int size() { + return values.size(); + } + + @Override + public synchronized Iterator<T> iterator() { + return values.iterator(); + } + + @Override + public synchronized String toString() { + return values.toString(); + } +} http://git-wip-us.apache.org/repos/asf/aurora/blob/06ddaadb/commons/src/main/java/org/apache/aurora/common/collections/Iterables2.java ---------------------------------------------------------------------- diff --git a/commons/src/main/java/org/apache/aurora/common/collections/Iterables2.java b/commons/src/main/java/org/apache/aurora/common/collections/Iterables2.java new file mode 100644 index 0000000..072159e --- /dev/null +++ b/commons/src/main/java/org/apache/aurora/common/collections/Iterables2.java @@ -0,0 +1,148 @@ +/** + * 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 org.apache.aurora.common.collections; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Iterator; +import java.util.List; + +import com.google.common.base.Function; +import com.google.common.cache.CacheBuilder; +import com.google.common.cache.CacheLoader; +import com.google.common.cache.LoadingCache; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.Iterables; +import com.google.common.collect.Lists; + +/** + * Utility functions for dealing with iterables. + * + * @author William Farner + */ +public final class Iterables2 { + + private Iterables2() { + // Utility class. + } + + /** + * An iterator that zips multiple iterables into a single list iterator, filling missing values + * with a provided default. + * + * @param <T> The value type for the iterator. + */ + private static class ZippingIterator<T> implements Iterator<List<T>> { + + private final Iterable<Iterable<T>> iterables; + private final T defaultValue; + + private List<Iterator<T>> iterators = null; + private final LoadingCache<Iterator<T>, Boolean> overflowing = CacheBuilder.newBuilder().build( + new CacheLoader<Iterator<T>, Boolean>() { + @Override public Boolean load(Iterator<T> iterator) { + return false; + } + }); + + ZippingIterator(Iterable<Iterable<T>> iterables, T defaultValue) { + this.iterables = iterables; + this.defaultValue = defaultValue; + } + + private void init() { + if (iterators == null) { + // Iterables -> Iterators. + iterators = ImmutableList.copyOf(Iterables.transform(iterables, + new Function<Iterable<T>, Iterator<T>>() { + @Override public Iterator<T> apply(Iterable<T> it) { return it.iterator(); } + })); + } + } + + @Override public boolean hasNext() { + init(); + for (Iterator<T> it : iterators) { + if (it.hasNext()) { + return true; + } + } + + return false; + } + + @Override public List<T> next() { + init(); + List<T> data = new ArrayList<T>(iterators.size()); + + for (Iterator<T> it : iterators) { + if (it.hasNext()) { + data.add(it.next()); + } else { + overflowing.asMap().put(it, true); + data.add(defaultValue); + } + } + + return data; + } + + @Override public void remove() { + init(); + for (Iterator<T> it : iterators) { + if (!overflowing.getUnchecked(it)) { + it.remove(); + } + } + } + + @Override public String toString() { + return Lists.newArrayList(iterables).toString(); + } + } + + /** + * Zips multiple iterables into one iterable that will return iterators to step over + * rows of the input iterators (columns). The order of the returned values within each row will + * match the ordering of the input iterables. The iterators will iterate the length of the longest + * input iterable, filling other columns with {@code defaultValue}. + * The returned iterator is lazy, in that 'rows' are constructed as they are requested. + * + * @param iterables Columns to iterate over. + * @param defaultValue Default fill value when an input iterable is exhausted. + * @param <T> Type of value being iterated over. + * @return An iterator that iterates over rows of the input iterables. + */ + public static <T> Iterable<List<T>> zip(final Iterable<Iterable<T>> iterables, + final T defaultValue) { + + return new Iterable<List<T>>() { + @Override public Iterator<List<T>> iterator() { + return new ZippingIterator<T>(iterables, defaultValue); + } + }; + } + + /** + * Varargs convenience function to call {@link #zip(Iterable, Object)}. + * + * @param defaultValue Default fill value when an input iterable is exhausted. + * @param iterables Columns to iterate over. + * @param <T> Type of value being iterated over. + * @return An iterator that iterates over rows of the input iterables. + */ + public static <T> Iterable<List<T>> zip(T defaultValue, Iterable<T>... iterables) { + return zip(Arrays.asList(iterables), defaultValue); + } +} http://git-wip-us.apache.org/repos/asf/aurora/blob/06ddaadb/commons/src/main/java/org/apache/aurora/common/collections/Multimaps.java ---------------------------------------------------------------------- diff --git a/commons/src/main/java/org/apache/aurora/common/collections/Multimaps.java b/commons/src/main/java/org/apache/aurora/common/collections/Multimaps.java new file mode 100644 index 0000000..7f94a8a --- /dev/null +++ b/commons/src/main/java/org/apache/aurora/common/collections/Multimaps.java @@ -0,0 +1,134 @@ +/** + * 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 org.apache.aurora.common.collections; + +import java.util.Collection; +import java.util.Iterator; +import java.util.Map; +import java.util.Set; + +import com.google.common.base.Preconditions; +import com.google.common.base.Predicate; +import com.google.common.collect.ArrayListMultimap; +import com.google.common.collect.Maps; +import com.google.common.collect.Multimap; +import com.google.common.collect.Multiset; +import com.google.common.collect.Ordering; +import com.google.common.collect.Sets; + +/** + * Utility class for functions related to Multimaps in the java collections library. + * + * @author William Farner + */ +public final class Multimaps { + + private Multimaps() { + // Utility. + } + + /** + * Prunes a multimap based on a predicate, returning the pruned values. The input map will be + * modified. + * + * @param map The multimap to prune. + * @param filterRule The pruning rule. When the predicate returns {@code false} for an entry, it + * will be pruned, otherwise it will be retained. + * @param <K> The key type in the multimap. + * @param <V> The value type in the multimap. + * @return A new multimap, containing the pruned keys/values. + */ + public static <K, V> Multimap<K, V> prune(Multimap<K, V> map, + Predicate<? super Collection<V>> filterRule) { + Preconditions.checkNotNull(map); + Preconditions.checkNotNull(filterRule); + Multimap<K, V> pruned = ArrayListMultimap.create(); + Iterator<Map.Entry<K, Collection<V>>> asMapItr = map.asMap().entrySet().iterator(); + while (asMapItr.hasNext()) { + Map.Entry<K, Collection<V>> asMapEntry = asMapItr.next(); + if (!filterRule.apply(asMapEntry.getValue())) { + pruned.putAll(asMapEntry.getKey(), asMapEntry.getValue()); + asMapItr.remove(); + } + } + + return pruned; + } + + private static final class AtLeastSize implements Predicate<Collection<?>> { + private final int minSize; + + AtLeastSize(int minSize) { + Preconditions.checkArgument(minSize >= 0); + this.minSize = minSize; + } + + @Override + public boolean apply(Collection<?> c) { + return c.size() >= minSize; + } + } + + /** + * Convenience method to prune key/values pairs where the size of the value collection is below a + * threshold. + * + * @param map The multimap to prune. + * @param minSize The minimum size for retained value collections. + * @param <K> The key type in the multimap. + * @param <V> The value type in the multimap. + * @return A new multimap, containing the pruned keys/values. + * @throws IllegalArgumentException if minSize < 0 + */ + public static <K, V> Multimap<K, V> prune(Multimap<K, V> map, int minSize) { + return prune(map, new AtLeastSize(minSize)); + } + + /** + * Returns the set of keys associated with groups of a size greater than or equal to a given size. + * + * @param map The multimap to search. + * @param minSize The minimum size to return associated keys for. + * @param <K> The key type for the multimap. + * @return The keys associated with groups of size greater than or equal to {@code minSize}. + * @throws IllegalArgumentException if minSize < 0 + */ + public static <K> Set<K> getLargeGroups(Multimap<K, ?> map, int minSize) { + return Sets.newHashSet( + Maps.filterValues(map.asMap(), new AtLeastSize(minSize)).keySet()); + } + + /** + * Returns the set of keys associated with the largest values in the multimap. + * + * @param map The multimap to search. + * @param topValues Number of groupings to find the keys for. + * @return The keys associated with the largest groups, of maximum size {@code topValues}. + */ + public static <K> Set<K> getLargestGroups(Multimap<K, ?> map, int topValues) { + Ordering<Multiset.Entry<K>> groupOrdering = new Ordering<Multiset.Entry<K>>() { + @Override + public int compare(Multiset.Entry<K> entry1, Multiset.Entry<K> entry2) { + return entry1.getCount() - entry2.getCount(); + // overflow-safe, since sizes are nonnegative + } + }; + Set<K> topKeys = Sets.newHashSetWithExpectedSize(topValues); + for (Multiset.Entry<K> entry + : groupOrdering.greatestOf(map.keys().entrySet(), topValues)) { + topKeys.add(entry.getElement()); + } + return topKeys; + } +} http://git-wip-us.apache.org/repos/asf/aurora/blob/06ddaadb/commons/src/main/java/org/apache/aurora/common/collections/Pair.java ---------------------------------------------------------------------- diff --git a/commons/src/main/java/org/apache/aurora/common/collections/Pair.java b/commons/src/main/java/org/apache/aurora/common/collections/Pair.java new file mode 100644 index 0000000..b1f9b63 --- /dev/null +++ b/commons/src/main/java/org/apache/aurora/common/collections/Pair.java @@ -0,0 +1,127 @@ +/** + * 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 org.apache.aurora.common.collections; + +import javax.annotation.Nullable; + +import com.google.common.base.Function; + +import org.apache.commons.lang.builder.EqualsBuilder; +import org.apache.commons.lang.builder.HashCodeBuilder; + + +/** + * An immutable 2-tuple with value-equals semantics. + * + * @param <A> The type of the 1st item in the pair. + * @param <B> The type of the 2nd item in the pair. + * + * @author William Farner + */ +public class Pair<A, B> { + + @Nullable + private final A first; + @Nullable + private final B second; + + /** + * Creates a new pair. + * + * @param first The first value. + * @param second The second value. + */ + public Pair(@Nullable A first, @Nullable B second) { + this.first = first; + this.second = second; + } + + @Nullable + public A getFirst() { + return first; + } + + @Nullable + public B getSecond() { + return second; + } + + @Override + public boolean equals(Object o) { + if (o == this) { return true; } + if (!(o instanceof Pair)) { return false; } + + Pair<?, ?> that = (Pair<?, ?>) o; + return new EqualsBuilder() + .append(this.first, that.first) + .append(this.second, that.second) + .isEquals(); + } + + @Override + public String toString() { + return String.format("(%s, %s)", getFirst(), getSecond()); + } + + @Override + public int hashCode() { + return new HashCodeBuilder() + .append(first) + .append(second) + .toHashCode(); + } + + /** + * Creates a function that can extract the first item of pairs of the given type parametrization. + * + * @param <S> The type of the 1st item in the pair. + * @param <T> The type of the 2nd item in the pair. + * @return A function that will extract the 1st item in a pair. + */ + public static <S, T> Function<Pair<S, T>, S> first() { + return new Function<Pair<S, T>, S>() { + @Override public S apply(Pair<S, T> pair) { + return pair.first; + } + }; + } + + /** + * Creates a function that can extract the second item of pairs of the given type parametrization. + * + * @param <S> The type of the 1st item in the pair. + * @param <T> The type of the 2nd item in the pair. + * @return A function that will extract the 2nd item in a pair. + */ + public static <S, T> Function<Pair<S, T>, T> second() { + return new Function<Pair<S, T>, T>() { + @Override public T apply(Pair<S, T> pair) { + return pair.second; + } + }; + } + + /** + * Convenience method to create a pair. + * + * @param a The first value. + * @param b The second value. + * @param <A> The type of the 1st item in the pair. + * @param <B> The type of the 2nd item in the pair. + * @return A new pair of [a, b]. + */ + public static <A, B> Pair<A, B> of(@Nullable A a, @Nullable B b) { + return new Pair<A, B>(a, b); + } +} http://git-wip-us.apache.org/repos/asf/aurora/blob/06ddaadb/commons/src/main/java/org/apache/aurora/common/common/webassets/bootstrap/BootstrapModule.java ---------------------------------------------------------------------- diff --git a/commons/src/main/java/org/apache/aurora/common/common/webassets/bootstrap/BootstrapModule.java b/commons/src/main/java/org/apache/aurora/common/common/webassets/bootstrap/BootstrapModule.java new file mode 100644 index 0000000..9cb6f54 --- /dev/null +++ b/commons/src/main/java/org/apache/aurora/common/common/webassets/bootstrap/BootstrapModule.java @@ -0,0 +1,90 @@ +/** + * 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 org.apache.aurora.common.common.webassets.bootstrap; + +import com.google.common.io.Resources; +import com.google.common.net.MediaType; +import com.google.inject.AbstractModule; + +import org.apache.aurora.common.application.http.Registration; + +/** + * A binding module to register bootstrap HTTP assets. + */ +public final class BootstrapModule extends AbstractModule { + /** + * Enum for available Bootstrap versions to choose from. + */ + public enum BootstrapVersion { + VERSION_2_1_1 ("2.1.1"), + VERSION_2_3_2 ("2.3.2"); + + private final String version; + + BootstrapVersion(String s) { + version = s; + } + } + + private final String version; + + /** + * Default constructor. + */ + public BootstrapModule() { + this(BootstrapVersion.VERSION_2_1_1); + } + + /** + * BootstrapModule Constructor. + * + * @param version supplies the bootstrap version to select. + */ + public BootstrapModule(BootstrapVersion version) { + this.version = version.version; + } + + private void register(String mountPath, String resourcePath, String contentType) { + Registration.registerHttpAsset( + binder(), + "/" + mountPath, + Resources.getResource(BootstrapModule.class, resourcePath), + contentType, + true); + } + + @Override + protected void configure() { + register( + "css/bootstrap-responsive.min.css", + version + "/css/bootstrap-responsive.min.css", + MediaType.CSS_UTF_8.toString()); + register( + "css/bootstrap.min.css", + version + "/css/bootstrap.min.css", + MediaType.CSS_UTF_8.toString()); + register( + "img/glyphicons-halflings-white.png", + version + "/img/glyphicons-halflings-white.png", + MediaType.PNG.toString()); + register( + "img/glyphicons-halflings.png", + version + "/img/glyphicons-halflings.png", + MediaType.PNG.toString()); + register( + "js/bootstrap.min.js", + version + "/js/bootstrap.min.js", + MediaType.JAVASCRIPT_UTF_8.toString()); + } +} http://git-wip-us.apache.org/repos/asf/aurora/blob/06ddaadb/commons/src/main/java/org/apache/aurora/common/common/webassets/jquery/JQueryModule.java ---------------------------------------------------------------------- diff --git a/commons/src/main/java/org/apache/aurora/common/common/webassets/jquery/JQueryModule.java b/commons/src/main/java/org/apache/aurora/common/common/webassets/jquery/JQueryModule.java new file mode 100644 index 0000000..a8e4524 --- /dev/null +++ b/commons/src/main/java/org/apache/aurora/common/common/webassets/jquery/JQueryModule.java @@ -0,0 +1,36 @@ +/** + * 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 org.apache.aurora.common.common.webassets.jquery; + +import com.google.common.io.Resources; +import com.google.common.net.MediaType; +import com.google.inject.AbstractModule; + +import org.apache.aurora.common.application.http.Registration; + +/** + * A binding module to register jQuery HTTP assets. + */ +public final class JQueryModule extends AbstractModule { + + @Override + protected void configure() { + Registration.registerHttpAsset( + binder(), + "/js/jquery.min.js", + Resources.getResource(JQueryModule.class, "js/jquery-1.8.2.min.js"), + MediaType.JAVASCRIPT_UTF_8.toString(), + true); + } +} http://git-wip-us.apache.org/repos/asf/aurora/blob/06ddaadb/commons/src/main/java/org/apache/aurora/common/inject/Bindings.java ---------------------------------------------------------------------- diff --git a/commons/src/main/java/org/apache/aurora/common/inject/Bindings.java b/commons/src/main/java/org/apache/aurora/common/inject/Bindings.java new file mode 100644 index 0000000..f910f22 --- /dev/null +++ b/commons/src/main/java/org/apache/aurora/common/inject/Bindings.java @@ -0,0 +1,316 @@ +/** + * 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 org.apache.aurora.common.inject; + +import java.lang.annotation.Annotation; + +import javax.inject.Qualifier; + +import com.google.common.base.Preconditions; +import com.google.common.collect.ImmutableList; +import com.google.inject.AbstractModule; +import com.google.inject.Binder; +import com.google.inject.BindingAnnotation; +import com.google.inject.Key; +import com.google.inject.Module; +import com.google.inject.PrivateModule; +import com.google.inject.TypeLiteral; +import com.google.inject.binder.LinkedBindingBuilder; +import com.google.inject.multibindings.Multibinder; +import com.google.inject.name.Names; + +/** + * A utility that helps with guice bindings. + * + * @author John Sirois + */ +public final class Bindings { + + + private Bindings() { + // utility + } + + /** + * Equivalent to calling {@code requireBinding(binder, Key.get(required, Names.named(namedKey)))}. + */ + public static void requireNamedBinding(Binder binder, Class<?> required, String namedKey) { + requireBinding(binder, Key.get(Preconditions.checkNotNull(required), + Names.named(Preconditions.checkNotNull(namedKey)))); + } + + /** + * Equivalent to calling {@code requireBinding(binder, Key.get(required))}. + */ + public static void requireBinding(Binder binder, Class<?> required) { + requireBinding(binder, Key.get(Preconditions.checkNotNull(required))); + } + + /** + * Registers {@code required} as non-optional dependency in the {@link com.google.inject.Injector} + * associated with {@code binder}. + * + * @param binder A binder to require bindings against. + * @param required The dependency that is required. + */ + public static void requireBinding(Binder binder, final Key<?> required) { + Preconditions.checkNotNull(binder); + Preconditions.checkNotNull(required); + + binder.install(new AbstractModule() { + @Override protected void configure() { + requireBinding(required); + } + }); + } + + /** + * A convenient version of {@link #exposing(Iterable, com.google.inject.Module)} when you just + * want to expose a single binding. + */ + public static Module exposing(Key<?> key, Module module) { + return exposing(ImmutableList.of(key), module); + } + + /** + * Creates a module that hides all the given module's bindings and only exposes bindings for + * the given key. + * + * @param keys The keys of the bindings to expose. + * @param module The module to hide most bindings for. + * @return A limited visibility module. + */ + public static Module exposing(final Iterable<? extends Key<?>> keys, final Module module) { + Preconditions.checkNotNull(keys); + Preconditions.checkNotNull(module); + + return new PrivateModule() { + @Override protected void configure() { + install(module); + for (Key<?> key : keys) { + expose(key); + } + } + }; + } + + /** + * A guice binding helper that allows for any combination of Class, TypeLiteral or Key binding + * without forcing guiced implementation to provide all the overloaded binding methods they would + * otherwise have to. + * + * @param <T> The type this helper can be used to bind implementations for. + */ + public interface BindHelper<T> { + + /** + * Associates this BindHelper with an Injector instance. + * + * @param binder The binder for the injector implementations will be bound in. + * @return A binding builder that can be used to bind an implementation with. + */ + LinkedBindingBuilder<T> with(Binder binder); + } + + /** + * Creates a BindHelper for the given binding key that can be used to bind a single instance. + * + * @param key The binding key the returned BindHelper can be use to bind implementations for. + * @param <T> The type the returned BindHelper can be used to bind implementations for. + * @return A BindHelper that can be used to bind an implementation with. + */ + public static <T> BindHelper<T> binderFor(final Key<T> key) { + return new BindHelper<T>() { + public LinkedBindingBuilder<T> with(Binder binder) { + return binder.bind(key); + } + }; + } + + /** + * Creates a BindHelper for the given type that can be used to add a binding of to a set. + * + * @param type The type the returned BindHelper can be use to bind implementations for. + * @param <T> The type the returned BindHelper can be used to bind implementations for. + * @return A BindHelper that can be used to bind an implementation with. + */ + public static <T> BindHelper<T> multiBinderFor(final Class<T> type) { + return new BindHelper<T>() { + public LinkedBindingBuilder<T> with(Binder binder) { + return Multibinder.newSetBinder(binder, type).addBinding(); + } + }; + } + + /** + * Checks that the given annotation instance is a {@link BindingAnnotation @BindingAnnotation}. + * + * @param annotation The annotation instance to check. + * @param <T> The type of the binding annotation. + * @return The checked binding annotation. + * @throws NullPointerException If the given {@code annotation} is null. + * @throws IllegalArgumentException If the given {@code annotation} is not a + * {@literal @BindingAnnotation}. + */ + public static <T extends Annotation> T checkBindingAnnotation(T annotation) { + Preconditions.checkNotNull(annotation); + checkBindingAnnotation(annotation.annotationType()); + return annotation; + } + + /** + * Checks that the given annotation type is a {@link BindingAnnotation @BindingAnnotation}. + * + * @param annotationType The annotation type to check. + * @param <T> The type of the binding annotation. + * @return The checked binding annotation type. + * @throws NullPointerException If the given {@code annotationType} is null. + * @throws IllegalArgumentException If the given {@code annotationType} is not a + * {@literal @BindingAnnotation}. + */ + public static <T extends Annotation> Class<T> checkBindingAnnotation(Class<T> annotationType) { + Preconditions.checkNotNull(annotationType); + boolean bindingAnnotation = annotationType.isAnnotationPresent(BindingAnnotation.class); + boolean qualifier = annotationType.isAnnotationPresent(Qualifier.class); + Preconditions.checkArgument(bindingAnnotation || qualifier, + "%s is not a @BindingAnnotation or @Qualifier", annotationType); + return annotationType; + } + + /** + * A factory for binding {@link Key keys}. + */ + public interface KeyFactory { + + /** + * Creates plain un-annotated keys. + */ + KeyFactory PLAIN = new KeyFactory() { + @Override public <T> Key<T> create(Class<T> type) { + return Key.get(type); + } + @Override public <T> Key<T> create(TypeLiteral<T> type) { + return Key.get(type); + } + }; + + /** + * Creates a key for the given type. + * + * @param type The type to create a key for. + * @param <T> The keyed type. + * @return A key. + */ + <T> Key<T> create(Class<T> type); + + /** + * Creates a key for the given type. + * + * @param type The type to create a key for. + * @param <T> The keyed type. + * @return A key. + */ + <T> Key<T> create(TypeLiteral<T> type); + } + + /** + * Creates a key factory that produces keys for a given annotation instance. + * + * @param annotation The annotation instance to apply to all keys. + * @return A key factory that creates annotated keys. + */ + public static KeyFactory annotatedKeyFactory(final Annotation annotation) { + checkBindingAnnotation(annotation); + return new KeyFactory() { + @Override public <T> Key<T> create(Class<T> type) { + return Key.get(type, annotation); + } + @Override public <T> Key<T> create(TypeLiteral<T> type) { + return Key.get(type, annotation); + } + }; + } + + /** + * Creates a key factory that produces keys for a given annotation type. + * + * @param annotationType The annotation type to apply to all keys. + * @return A key factory that creates annotated keys. + */ + public static KeyFactory annotatedKeyFactory(final Class<? extends Annotation> annotationType) { + checkBindingAnnotation(annotationType); + return new KeyFactory() { + @Override public <T> Key<T> create(Class<T> type) { + return Key.get(type, annotationType); + } + @Override public <T> Key<T> create(TypeLiteral<T> type) { + return Key.get(type, annotationType); + } + }; + } + + /** + * A utility that helps rebind keys. + */ + public static final class Rebinder { + private final Binder binder; + private final KeyFactory bindToFactory; + + /** + * Creates a Rebinder that links bindings to keys from the given {@code bindToFactory}. + * + * @param binder A binder to rebind keys in. + * @param bindToFactory A factory for the rebinding key. + */ + public Rebinder(Binder binder, KeyFactory bindToFactory) { + this.binder = Preconditions.checkNotNull(binder); + this.bindToFactory = Preconditions.checkNotNull(bindToFactory); + } + + /** + * Rebinds the given key to another, linking bindings. + * + * @param fromKey The source key to rebind. + * @return The key that {@code key} was rebound to. + */ + public <T> Key<T> rebind(Key<T> fromKey) { + Key<T> toKey = bindToFactory.create(fromKey.getTypeLiteral()); + binder.bind(toKey).to(fromKey); + requireBinding(binder, fromKey); + return toKey; + } + } + + /** + * Creates a Rebinder that rebinds keys to the given annotation instance. + * + * @param binder A binder to rebind keys in. + * @param annotation The annotation instance to rebind keys to. + * @return A Rebinder targeting the given {@code annotationType}. + */ + public static Rebinder rebinder(Binder binder, Annotation annotation) { + return new Rebinder(binder, annotatedKeyFactory(annotation)); + } + + /** + * Creates a Rebinder that rebinds keys to the given annotation type. + * + * @param binder A binder to rebind keys in. + * @param annotationType The annotation type to rebind keys to. + * @return A Rebinder targeting the given {@code annotationType}. + */ + public static Rebinder rebinder(Binder binder, Class<? extends Annotation> annotationType) { + return new Rebinder(binder, annotatedKeyFactory(annotationType)); + } +}
