/* * Copyright 2011 Atteo. * * 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.atteo.moonshine.jta; import javax.transaction.HeuristicMixedException; import javax.transaction.HeuristicRollbackException; import javax.transaction.NotSupportedException; import javax.transaction.RollbackException; import javax.transaction.Status; import javax.transaction.SystemException; import javax.transaction.UserTransaction; import static com.google.common.base.Preconditions.checkNotNull; import com.google.inject.Inject; import com.google.inject.Provider; public class Transaction { private Transaction() { } public static interface Runnable { void run(); } public static interface ThrowingRunnable<E extends Throwable> { void run() throws E; } public static interface ReturningRunnable<T, E extends Throwable> { T run() throws E; } private static final InheritableThreadLocal<Provider<UserTransaction>> userTransactionProviders = new InheritableThreadLocal<>(); @Inject private static void injectTransactionProvider(Provider<UserTransaction> provider) { userTransactionProviders.set(provider); } public static void require(final Runnable runnable) { require(() -> { runnable.run(); return null; }); } public static <E extends Throwable> void require(final ThrowingRunnable<E> runnable) throws E { require(() -> { runnable.run(); return null; }); } public static <T, E extends Throwable> T require(ReturningRunnable<T, E> runnable) throws E { checkNotNull(userTransactionProviders.get(), "Transactions not supported. You need to add <transactional/>" + " to your configuration file."); UserTransaction userTransaction = userTransactionProviders.get().get(); return require(userTransaction, runnable); } private static <T, E extends Throwable> T require(UserTransaction userTransaction, ReturningRunnable<T, E> runnable) throws E { boolean myTransaction = false; try { if (userTransaction.getStatus() == Status.STATUS_NO_TRANSACTION) { userTransaction.begin(); myTransaction = true; } try { return runnable.run(); } catch (RuntimeException e) { userTransaction.setRollbackOnly(); throw e; } finally { if (myTransaction) { if (userTransaction.getStatus() == Status.STATUS_MARKED_ROLLBACK) { userTransaction.rollback(); } else { userTransaction.commit(); } } } } catch (SystemException | NotSupportedException | RollbackException | HeuristicMixedException | HeuristicRollbackException e) { throw new RuntimeException(e); } } }