001/*
002 * Copyright 2011 Atteo.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *      http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016package org.atteo.moonshine.jta;
017
018import javax.transaction.HeuristicMixedException;
019import javax.transaction.HeuristicRollbackException;
020import javax.transaction.NotSupportedException;
021import javax.transaction.RollbackException;
022import javax.transaction.Status;
023import javax.transaction.SystemException;
024import javax.transaction.UserTransaction;
025
026import static com.google.common.base.Preconditions.checkNotNull;
027import com.google.inject.Inject;
028import com.google.inject.Provider;
029
030public class Transaction {
031    public static interface Runnable {
032        void run();
033    }
034
035    public static interface ThrowingRunnable<E extends Throwable> {
036        void run() throws E;
037    }
038
039    public static interface ReturningRunnable<T, E extends Throwable> {
040        T run() throws E;
041    }
042
043    @Inject
044    private static Provider<UserTransaction> userTransactionProvider;
045
046    public static void require(final Runnable runnable) {
047        require(new ReturningRunnable<Void, RuntimeException>() {
048            @Override
049            public Void run() {
050                runnable.run();
051                return null;
052            }
053        });
054    }
055
056    public static <E extends Throwable> void require(final ThrowingRunnable<E> runnable) throws E {
057        require(new ReturningRunnable<Void, E>() {
058            @Override
059            public Void run() throws E {
060                runnable.run();
061                return null;
062            }
063        });
064    }
065
066    public static <T, E extends Throwable> T require(ReturningRunnable<T, E> runnable) throws E {
067        checkNotNull(userTransactionProvider, "Transactions not supported. You need to add <transactional/>"
068                + " to your configuration file.");
069        UserTransaction userTransaction = userTransactionProvider.get();
070
071        boolean myTransaction = false;
072
073        try {
074            if (userTransaction.getStatus() == Status.STATUS_NO_TRANSACTION) {
075                userTransaction.begin();
076                myTransaction = true;
077            }
078
079            try {
080                return runnable.run();
081            } catch (RuntimeException e) {
082                userTransaction.setRollbackOnly();
083                throw e;
084            } finally {
085                if (myTransaction) {
086                    if (userTransaction.getStatus() == Status.STATUS_MARKED_ROLLBACK) {
087                        userTransaction.rollback();
088                    } else {
089                        userTransaction.commit();
090                    }
091                }
092            }
093        } catch (SystemException | NotSupportedException | RollbackException | HeuristicMixedException
094                | HeuristicRollbackException e) {
095            throw new RuntimeException(e);
096        }
097    }
098}