/*
 * Copyright 2004-2015 the Seasar Foundation and the Others.
 *
 * 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.seasar.framework.ejb.tx;

import java.rmi.RemoteException;
import java.util.List;

import javax.ejb.ApplicationException;

import org.aopalliance.intercept.MethodInvocation;
import org.seasar.extension.tx.DefaultTransactionCallback;
import org.seasar.extension.tx.TransactionManagerAdapter;

/**
 * EJB3用のトランザクションコールバック実装クラスです。
 * <p>
 * 宣言的トランザクションが適用されたメソッドから{@link ApplicationException}で注釈された例外がスローされた場合は、
 * {@link ApplicationException#rollback()}の指定に従いトランザクションをロールバックするようマークします。
 * </p>
 * 
 * @author koichik
 * @since 2.4.18
 */
public class EJB3TransactionCallback extends DefaultTransactionCallback {

    /**
     * @param methodInvocation
     * @param txRules
     */
    @SuppressWarnings("unchecked")
    public EJB3TransactionCallback(final MethodInvocation methodInvocation,
            final List txRules) {
        super(methodInvocation, txRules);
    }

    @Override
    protected void applyTxRule(final TransactionManagerAdapter adapter,
            final Throwable t) {
        if (isRollingBack(t)) {
            adapter.setRollbackOnly();
            return;
        }
        super.applyTxRule(adapter, t);
    }

    /**
     * EJB3仕様に従い、 発生した例外によってトランザクションをロールバックしなくてはならない場合は<code>true</code>を、
     * それ以外の場合は<code>false</code>を返します。
     * 
     * @param throwable
     *            発生した例外
     * @return 発生した例外によってトランザクションをロールバックしなくてはならない場合は<code>true</code>
     */
    protected static boolean isRollingBack(final Throwable throwable) {
        final Class<? extends Throwable> exceptionClass = throwable.getClass();
        final ApplicationException annotation = exceptionClass
                .getAnnotation(ApplicationException.class);
        if (annotation != null) {
            return annotation.rollback();
        }
        if (throwable instanceof RemoteException) {
            return true;
        }
        if (throwable instanceof RuntimeException) {
            return true;
        }
        if (throwable instanceof Exception) {
            return false;
        }
        return true;
    }

}
