/**
 * JOnAS: Java(TM) Open Application Server
 * Copyright (C) 2009-2011 Bull S.A.S.
 * Contact: jonas-team@ow2.org
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
 *
 * --------------------------------------------------------------------------
 * $Id: TransactionValve.java 21566 2011-08-08 12:28:12Z cazauxj $
 * --------------------------------------------------------------------------
 */

package org.ow2.jonas.web.tomcat7.tx;

import static javax.transaction.Status.STATUS_COMMITTED;

import java.io.IOException;

import javax.servlet.ServletException;
import javax.transaction.SystemException;
import javax.transaction.Transaction;
import javax.transaction.TransactionManager;

import org.apache.catalina.connector.Request;
import org.apache.catalina.connector.Response;
import org.apache.catalina.valves.ValveBase;
import org.ow2.util.log.Log;
import org.ow2.util.log.LogFactory;

/**
 * This valve is used when TX service is enabled. It ensures that the transaction is aborted at the end of the execution of the method if it was started in the method.
 * @author Florent Benoit
 */
public class TransactionValve extends ValveBase {

    /**
     * Logger.
     */
    private Log logger = LogFactory.getLog(TransactionValve.class);

    /**
     * Link to the transaction manager.
     */
    private TransactionManager transactionmanager = null;



    /**
     * Default constructor.
     * @param transactionManager the given transaction manager
     */
    public TransactionValve(final TransactionManager transactionManager) {
        super(true);
        this.transactionmanager = transactionManager;
    }

    /**
     * Ensure that there is no pending transaction at the end of the method.
     * @param request The servlet request to be processed
     * @param response The servlet response to be created
     * @exception IOException if an input/output error occurs
     * @exception ServletException if a servlet error occurs
     */
    @Override
    public void invoke(final Request request, final Response response) throws IOException, ServletException {

        // There is a transaction ?
        Transaction existingTransaction = null;
        try {
            existingTransaction = transactionmanager.getTransaction();
        } catch (SystemException e) {
            throw new IllegalStateException("Unable to check if there is a transaction", e);
        }

        // Call next valve
        try {
            getNext().invoke(request, response);
        } finally {
            // If there wasn't transaction before the call
            if (existingTransaction == null) {
                // check if a transaction has been started in the method ?
                Transaction txAfterMethod = null;
                try {
                    txAfterMethod = transactionmanager.getTransaction();
                } catch (SystemException e) {
                    throw new IllegalStateException("Unable to check if there is a transaction", e);
                }

                // 4.2.2 chapter of Java EE 5
                // Transactions may not span web requests from a client. A web component starts a
                // transaction in the service method of a servlet (or, for a JSP page, the service
                // method of the equivalent JSP page Implementation Class) and it must be completed
                // before the service method returns. Returning from the service method with an
                // active transaction context is an error. The web container is required to detect this
                // error and abort the transaction.

                if (txAfterMethod != null) {
                    int transactionStatus = 0;
                    try {
                        transactionStatus = txAfterMethod.getStatus();
                        // There is a transaction and it was not committed
                        if (transactionStatus != STATUS_COMMITTED) {
                            String errMsg = "Transaction started in the servlet '" + request.getServletPath()
                                    + "' but not committed. Rolling back...";
                            // Log error
                            logger.error(errMsg);
                            // Rollback
                            txAfterMethod.rollback();
                        }
                    } catch (SystemException e) {
                              throw new IllegalStateException("Cannot rollback transaction", e);
                    }
                }
            }

        }
    }
}
