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.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.*;
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 class TransactionHandler {

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

    /**
     * The transaction configuration.
     */
    @Inject
    private Instance<TransactionConfiguration> configurationInstance;

    /**
     * Transaction lifecycle event.
     */
    @Inject
    Event<TransactionEvent> lifecycleEvent;

    /**
     * The context bound to the current transaction.
     */
    @Inject
    private Instance<TransactionContext> transactionContextInstance;

    /**
     * The instance of the test result.
     */
    @Inject
    private Instance<TestResult> testResultInstance;

    /**
     * Initializes a transaction before execution of the test.
     *
     * @param beforeTest the test event
     */
    public void startTransactionBeforeTest(@Observes(precedence = 10) Before beforeTest) {

        TransactionProvider transactionProvider;

        if (isTransactionEnabled(beforeTest)) {

            TransactionMode transactionMode = getTransactionMode(beforeTest);

            if (transactionMode != TransactionMode.DISABLED) {
                transactionProvider = getTransactionProvider();

                // creates the transaction context
                TransactionContext transactionContext = transactionContextInstance.get();
                transactionContext.activate();

                lifecycleEvent.fire(new BeforeTransactionStarted());

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

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

    /**
     * Compensates the transaction after execution of the test.
     *
     * @param afterTest the test event
     */
    public void endTransactionAfterTest(@Observes(precedence = 50) After afterTest) {

        TransactionProvider transactionProvider;

        if (isTransactionEnabled(afterTest)) {

            // retrieves the transaction mode, declared for the test method
            TransactionMode transactionMode = getTransactionMode(afterTest);

            if (transactionMode != TransactionMode.DISABLED) {
                try {
                    lifecycleEvent.fire(new BeforeTransactionEnded());

                    transactionProvider = getTransactionProvider();

                    TransactionalTest transactionalTest =
                            new TransactionalTestImpl(getTransactionManager(afterTest));

                    if (transactionMode == TransactionMode.ROLLBACK || isTestRequiresRollback()) {
                        // rollbacks the transaction
                        transactionProvider.rollbackTransaction(transactionalTest);
                    } else {
                        // commits the transaction
                        transactionProvider.commitTransaction(transactionalTest);
                    }

                } finally {
                    lifecycleEvent.fire(new AfterTransactionEnded());

                    // finally destroys the transaction context
                    TransactionContext transactionContext = transactionContextInstance.get();
                    transactionContext.destroy();
                }
            }
        }
    }

    /**
     * Returns whether the test requires to be rollback.
     * </p>
     * By default it will return true if the last executed test has failed.
     *
     * @return true if test requires rollback, false otherwise
     */
    private boolean isTestRequiresRollback() {

        return testResultInstance.get().getStatus() == TestResult.Status.FAILED;
    }

    /**
     * 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) {

        return testEvent.getTestMethod().isAnnotationPresent(Transactional.class) ||
                testEvent.getTestClass().isAnnotationPresent(Transactional.class);
    }

    /**
     * Retrieves the transaction mode for the current test.
     *
     * @param testEvent the test event
     *
     * @return the transaction mode
     */
    private TransactionMode getTransactionMode(TestEvent testEvent) {

        // retrieves the transaction annotation
        Transactional transactional = getTransactionalAnnotation(testEvent);

        // returns the transaction mode
        return transactional.value();
    }

    /**
     * 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 is
     *
     * @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 the transaction manager name still hasn't been set then tries to get the manager from the configuration
        if (transactionManager.length() == 0) {

            if (configurationInstance.get().getManager() != null) {
                transactionManager = configurationInstance.get().getManager();
            }
        }

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

    /**
     * Retrieves the {@link Transactional} from the test method or class that was used for configuring the transaction
     * support.
     *
     * @param testEvent the test event
     *
     * @return the {@link Transactional} annotation
     */
    private Transactional getTransactionalAnnotation(TestEvent testEvent) {

        if (testEvent.getTestMethod().isAnnotationPresent(Transactional.class)) {
            return testEvent.getTestMethod().getAnnotation(Transactional.class);
        } else {
            return testEvent.getTestClass().getAnnotation(Transactional.class);
        }
    }

    /**
     * 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 {
            ServiceLoader serviceLoader = serviceLoaderInstance.get();

            TransactionProvider transactionProvider = serviceLoader.onlyOne(TransactionProvider.class);

            if (transactionProvider == null) {
                throw new TransactionProviderNotFoundException(
                        "Transaction provider for given test case has not been found.");
            }

            return transactionProvider;
        } catch (IllegalStateException exc) {
            // thrown if there were multiple providers registered in the context
            throw new TransactionProviderNotFoundException(
                    "More then one transaction provider has been specified.", exc);
        }
    }
}
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.