/* * Copyright © 2014-2015 Cask Data, 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 co.cask.cdap.data2.transaction; import co.cask.tephra.Transaction; import co.cask.tephra.TransactionAware; import co.cask.tephra.TransactionContext; import co.cask.tephra.TransactionExecutor; import co.cask.tephra.TransactionFailureException; import co.cask.tephra.TransactionSystemClient; import com.google.common.base.Functions; import com.google.common.base.Supplier; import com.google.common.collect.ImmutableList; import com.google.common.collect.Iterables; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.Collections; import java.util.concurrent.Callable; /** * Helper class for interacting with {@link Transaction} and {@link TransactionSystemClient}. */ public final class Transactions { private static final Logger LOG = LoggerFactory.getLogger(Transactions.class); /** * Aborts the given transaction without throwing any exception. If there is exception raised during abort, * it will get logged as an error. */ public static void abortQuietly(TransactionSystemClient txClient, Transaction tx) { try { txClient.abort(tx); } catch (Throwable t) { LOG.error("Exception when aborting transaction {}", tx, t); } } /** * Invalidates the given transaction without throwing any exception. If there is exception raised during invalidation, * it will get logged as an error. */ public static void invalidateQuietly(TransactionSystemClient txClient, Transaction tx) { try { if (!txClient.invalidate(tx.getWritePointer())) { LOG.error("Failed to invalidate transaction {}", tx); } } catch (Throwable t) { LOG.error("Exception when invalidating transaction {}", tx, t); } } /** * Wraps the given {@link Throwable} as a {@link TransactionFailureException} if it is not already an instance of * {@link TransactionFailureException}. */ public static TransactionFailureException asTransactionFailure(Throwable t) { return asTransactionFailure(t, "Exception raised in transactional execution. Cause: " + t.getMessage()); } /** * Wraps the given {@link Throwable} as a {@link TransactionFailureException} if it is not already an instance of * {@link TransactionFailureException}. * * @param t the original exception * @param message the exception message to use in case wrapping is needed */ public static TransactionFailureException asTransactionFailure(Throwable t, String message) { if (t instanceof TransactionFailureException) { return (TransactionFailureException) t; } return new TransactionFailureException(message, t); } public static Supplier<TransactionContext> constantContextSupplier(final TransactionSystemClient txClient, final TransactionAware txAware) { return constantContextSupplier(txClient, Collections.singleton(txAware)); } public static Supplier<TransactionContext> constantContextSupplier(final TransactionSystemClient txClient, final Iterable<? extends TransactionAware> txAwares) { return new Supplier<TransactionContext>() { @Override public TransactionContext get() { return new TransactionContext(txClient, Iterables.transform(txAwares, Functions.<TransactionAware>identity())); } }; } /** * Handy method to create {@link TransactionExecutor} (See TEPHRA-71). */ public static TransactionExecutor createTransactionExecutor(TransactionExecutorFactory factory, final TransactionSystemClient txClient, final Iterable<? extends TransactionAware> txAwares) { return factory.createExecutor(constantContextSupplier(txClient, txAwares)); } /** * Handy method to create {@link TransactionExecutor} with single {@link TransactionAware}. */ public static TransactionExecutor createTransactionExecutor(TransactionExecutorFactory factory, TransactionSystemClient txClient, TransactionAware txAware) { return createTransactionExecutor(factory, txClient, ImmutableList.of(txAware)); } /** * Handy method to create {@link TransactionExecutor} (See TEPHRA-71). */ public static TransactionExecutor createTransactionExecutor(co.cask.tephra.TransactionExecutorFactory factory, Iterable<? extends TransactionAware> txAwares) { return factory.createExecutor(Iterables.transform(txAwares, Functions.<TransactionAware>identity())); } public static TransactionExecutor createTransactionExecutor(co.cask.tephra.TransactionExecutorFactory factory, TransactionAware txAware) { return factory.createExecutor(Collections.singleton(txAware)); } /** * Executes the given {@link Runnable} in a short transaction using the given {@link TransactionContext}. * * @param txContext the {@link TransactionContext} for managing the transaction lifecycle * @param name descriptive name for the runnable * @param runnable the Runnable to be executed inside a transaction * @throws TransactionFailureException if failed to execute in a transaction. The cause of the exception carries the * reason of failure. */ public static void execute(TransactionContext txContext, String name, final Runnable runnable) throws TransactionFailureException { execute(txContext, name, new Callable<Void>() { @Override public Void call() throws Exception { runnable.run(); return null; } }); } /** * Executes the given {@link Callable} in a short transaction using the given {@link TransactionContext}. * * @param txContext the {@link TransactionContext} for managing the transaction lifecycle * @param name descriptive name for the runnable * @param callable the Callable to be executed inside a transaction * @return the result returned by {@link Callable#call()} * @throws TransactionFailureException if failed to execute in a transaction. The cause of the exception carries the * reason of failure. */ public static <V> V execute(TransactionContext txContext, String name, Callable<V> callable) throws TransactionFailureException { V result = null; txContext.start(); try { result = callable.call(); } catch (Throwable t) { // Abort will always throw with the TransactionFailureException. txContext.abort(new TransactionFailureException("Failed to execute method " + name + " inside a transaction", t)); } // If commit failed, the tx will be aborted and exception will be raised txContext.finish(); return result; } private Transactions() { // Private the constructor for util class } }