Package org.jboss.arquillian.transaction.impl.lifecycle

Source Code of org.jboss.arquillian.transaction.impl.lifecycle.TransactionHandler

/*
* JBoss, Home of Professional Open Source
* Copyright 2012 Red Hat Inc. and/or its affiliates and other contributors
* as indicated by the @authors tag. All rights reserved.
* See the copyright.txt in the distribution for a
* full listing of individual contributors.
*
* 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.jboss.arquillian.transaction.impl.lifecycle;

import org.jboss.arquillian.core.api.Event;
import org.jboss.arquillian.core.api.Instance;
import org.jboss.arquillian.core.api.annotation.Inject;
import org.jboss.arquillian.core.api.annotation.Observes;
import org.jboss.arquillian.core.spi.ServiceLoader;
import org.jboss.arquillian.test.spi.TestResult;
import org.jboss.arquillian.test.spi.TestResult.Status;
import org.jboss.arquillian.test.spi.event.suite.After;
import org.jboss.arquillian.test.spi.event.suite.Before;
import org.jboss.arquillian.test.spi.event.suite.TestEvent;
import org.jboss.arquillian.transaction.api.annotation.TransactionMode;
import org.jboss.arquillian.transaction.api.annotation.Transactional;
import org.jboss.arquillian.transaction.impl.configuration.TransactionConfiguration;
import org.jboss.arquillian.transaction.impl.test.TransactionalTestImpl;
import org.jboss.arquillian.transaction.spi.context.TransactionContext;
import org.jboss.arquillian.transaction.spi.event.AfterTransactionEnded;
import org.jboss.arquillian.transaction.spi.event.AfterTransactionStarted;
import org.jboss.arquillian.transaction.spi.event.BeforeTransactionEnded;
import org.jboss.arquillian.transaction.spi.event.BeforeTransactionStarted;
import org.jboss.arquillian.transaction.spi.event.TransactionEvent;
import org.jboss.arquillian.transaction.spi.provider.TransactionEnabler;
import org.jboss.arquillian.transaction.spi.provider.TransactionProvider;
import org.jboss.arquillian.transaction.spi.test.TransactionalTest;

/**
* The transaction life cycle handler, which is responsible for initializing new
* transactions before execution of the test method and compensating it
* afterwards, based on the strategy defined by {@link Transactional}
* annotation.
* <p/>
* The implementation delegates to registered {@link TransactionProvider} in the
* current context to perform actual operation. If no provider has been found or
* multiple classes has been registered then
* {@link TransactionProviderNotFoundException} is being thrown.
*
* @author <a href="mailto:bartosz.majsak@gmail.com">Bartosz Majsak</a>
* @author <a href="mailto:jmnarloch@gmail.com">Jakub Narloch</a>
* @see Transactional
* @see TransactionProvider
*/
public abstract class TransactionHandler {

    /**
     * Instance of {@link ServiceLoader}, used for retrieving
     * required SPIs registered in the context.
     */
    @Inject
    private Instance<ServiceLoader> serviceLoaderInstance;

    @Inject
    private Instance<TransactionConfiguration> configurationInstance;

    @Inject
    Event<TransactionEvent> lifecycleEvent;

    @Inject
    private Instance<TransactionContext> transactionContextInstance;

    @Inject
    private Instance<TestResult> testResultInstance;

    public abstract boolean isTransactionSupported(TestEvent testEvent);

    public void startTransactionBeforeTest(@Observes(precedence = 10) Before beforeTest) {

        if (isTransactionEnabled(beforeTest)) {

            TransactionContext transactionContext = transactionContextInstance.get();
            transactionContext.activate();

            lifecycleEvent.fire(new BeforeTransactionStarted());

            getTransactionProvider().beginTransaction(new TransactionalTestImpl(getTransactionManager(beforeTest)));

            lifecycleEvent.fire(new AfterTransactionStarted());
        }
    }

    public void endTransactionAfterTest(@Observes(precedence = 50) After afterTest) {

        if (isTransactionEnabled(afterTest)) {

            try {
                lifecycleEvent.fire(new BeforeTransactionEnded());

                TransactionProvider transactionProvider = getTransactionProvider();
                TransactionalTest transactionalTest = new TransactionalTestImpl(getTransactionManager(afterTest));

                if (rollbackRequired(afterTest)) {
                    transactionProvider.rollbackTransaction(transactionalTest);
                } else {
                    transactionProvider.commitTransaction(transactionalTest);
                }

            } finally {
                lifecycleEvent.fire(new AfterTransactionEnded());
                transactionContextInstance.get().destroy();
            }
        }
    }

    // -- Private methods

    /**
     * Returns whether the transaction is enabled for the current test.
     *
     * @param testEvent the test event
     *
     * @return true if the transaction support has been enabled, false otherwise
     */
    private boolean isTransactionEnabled(TestEvent testEvent) {
        if (!isTransactionSupported(testEvent)) {
            return false;
        }

        final TransactionMode transactionMode = getTransactionMode(testEvent);
        return transactionMode != null && !TransactionMode.DISABLED.equals(transactionMode);
    }

    private boolean rollbackRequired(TestEvent testEvent) {
        return testRequiresRollbackDueToFailure() || TransactionMode.ROLLBACK.equals(getTransactionMode(testEvent));
    }

    /**
     * Returns whether the test requires to be rolled back. </p>
     * By default it will return true if the last executed test has failed.
     *
     * @return true if test requires rollback, false otherwise
     */
    private boolean testRequiresRollbackDueToFailure() {
        final Status actualStatus = testResultInstance.get().getStatus();
        return TestResult.Status.FAILED.equals(actualStatus);
    }

    /**
     * Retrieves the transaction mode for the current test.
     *
     * @param testEvent the test event
     *
     * @return the transaction mode
     */
    private TransactionMode getTransactionMode(TestEvent testEvent) {
        TransactionMode result = extractFromTestEvent(testEvent);
        if (TransactionMode.DEFAULT.equals(result)) {
            result = configurationInstance.get().getTransactionDefaultMode();
        }
        return result;
    }

    private TransactionMode extractFromTestEvent(TestEvent testEvent) {
        TransactionMode methodLevel = null;
        TransactionMode classLevel = null;
        final TransactionEnablerLoader transactionEnablerLoader = new TransactionEnablerLoader(serviceLoaderInstance.get());
        for (TransactionEnabler enabler : transactionEnablerLoader.getTransactionEnablers()) {
            if (methodLevel == null && enabler.isTransactionHandlingDefinedOnMethodLevel(testEvent)) {
                methodLevel = enabler.getTransactionModeFromMethodLevel(testEvent);
            }

            if (classLevel == null && enabler.isTransactionHandlingDefinedOnClassLevel(testEvent)) {
                classLevel = enabler.getTransactionModeFromClassLevel(testEvent);
            }
        }
        if (methodLevel != null) {
            return methodLevel;
        }

        return classLevel;
    }

    /**
     * Retrieves the transaction manager. The default implementation tries to
     * first retrieve then transaction manager name from the annotation first on
     * the method level then class. If non of above condition is meet then is
     * used the manager name provided through configuration.
     *
     * @param testEvent the test event
     *
     * @return the transaction manager name or null if one hasn't been set
     */
    private String getTransactionManager(TestEvent testEvent) {

        Transactional transactional;
        String transactionManager = "";

        // tries to retrieve the name of the manager from annotated test method
        transactional = testEvent.getTestMethod().getAnnotation(Transactional.class);
        if (transactional != null) {
            transactionManager = transactional.manager();
        }

        // if the transaction manager name hasn't been set then tries to
        // retrieve it from class level annotation
        if (transactionManager.length() == 0) {
            transactional = testEvent.getTestClass().getAnnotation(Transactional.class);
            if (transactional != null) {
                transactionManager = transactional.manager();
            }
        }

        if (transactionManager.length() == 0) {
            transactionManager = obtainTranscationManagerFromConfiguration(transactionManager);
        }

        return transactionManager.length() != 0 ? transactionManager : null;
    }

    private String obtainTranscationManagerFromConfiguration(String transactionManager) {
        if (configurationInstance.get().getManager() != null) {
            transactionManager = configurationInstance.get().getManager();
        }
        return transactionManager;
    }

    /**
     * Retrieves the {@link TransactionProvider} registered in current context.
     *
     * @return the transaction provider
     *
     * @throws TransactionProviderNotFoundException
     *             if no provider could be found or there are multiple providers registered.
     */
    private TransactionProvider getTransactionProvider() {
        try {
            final TransactionProvider transactionProvider = serviceLoaderInstance.get().onlyOne(TransactionProvider.class);
            if (transactionProvider == null) {
                throw new TransactionProviderNotFoundException("Transaction provider for given test case has not been found.");
            }
            return transactionProvider;
        } catch (IllegalStateException e) {
            throw new TransactionProviderNotFoundException("More then one transaction provider has been specified.", e);
        }
    }

}
TOP

Related Classes of org.jboss.arquillian.transaction.impl.lifecycle.TransactionHandler

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.