/** 
 * 
 * Copyright 2004 Protique Ltd
 * 
 * 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.activemq.test;

import org.activemq.ActiveMQConnectionFactory;
import org.activemq.util.IndentPrinter;

import javax.jms.Connection;
import javax.jms.ConnectionFactory;
import javax.jms.Destination;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageConsumer;
import javax.jms.MessageProducer;
import javax.jms.Session;
import javax.jms.XASession;
import javax.transaction.xa.XAResource;
import java.util.ArrayList;

/**
 * @version $Revision: 1.1.1.1 $
 */
public abstract class JmsXATransactionTestSupport extends TestSupport {

    protected ConnectionFactory connectionFactory;
    protected Connection connection;
    protected Session session;
    protected MessageConsumer consumer;
    protected MessageProducer producer;

    public JmsXATransactionTestSupport() {
        super();
    }

    public JmsXATransactionTestSupport(String name) {
        super(name);
    }

    public void testSendOutsideXATransaction() throws Exception {
        try {
            producer.send(session.createTextMessage("First Message"));
            fail("Using an XA session outside of a transaction should throw an exception.");
        }
        catch (JMSException e) {
            // normal
        }
    }


    public void testSendRollback() throws Exception {

        XAResource resource = ((XASession) session).getXAResource();
        Message[] outbound = new Message[]{
            session.createTextMessage("First Message"),
            session.createTextMessage("Second Message")
        };


        XidStub xid1 = new XidStub(new byte[]{1, 2, 3, 4, 5});
        resource.start(xid1, XAResource.TMNOFLAGS);
        producer.send(outbound[0]);
        resource.end(xid1, XAResource.TMSUCCESS);
        resource.commit(xid1, true);

        XidStub xid2 = new XidStub(new byte[]{2, 2, 3, 4, 5});
        resource.start(xid2, XAResource.TMNOFLAGS);
        producer.send(session.createTextMessage("I'm going to get rolled back."));
        resource.end(xid2, XAResource.TMSUCCESS);
        resource.rollback(xid2);

        XidStub xid3 = new XidStub(new byte[]{3, 2, 3, 4, 5});
        resource.start(xid3, XAResource.TMNOFLAGS);
        producer.send(outbound[1]);
        resource.end(xid3, XAResource.TMSUCCESS);
        if (resource.prepare(xid3) == XAResource.XA_OK) {
            resource.commit(xid3, false);
        }

        ArrayList messages = new ArrayList();
        XidStub xid4 = new XidStub(new byte[]{4, 2, 3, 4, 5});
        resource.start(xid4, XAResource.TMNOFLAGS);
        messages.add(consumer.receive(1000));
        messages.add(consumer.receive(1000));
        resource.end(xid4, XAResource.TMSUCCESS);
        resource.commit(xid4, true);

        Message inbound[] = new Message[messages.size()];
        messages.toArray(inbound);

        assertTextMessagesEqual("Rollback did not work.", outbound, inbound);
    }

    public void testSendPrepareRecoverRollback() throws Exception {

        XAResource resource = ((XASession) session).getXAResource();
        Message[] outbound = new Message[]{
            session.createTextMessage("First Message"),
            session.createTextMessage("Second Message")
        };

        XidStub xid2 = new XidStub(new byte[]{1, 2, 3, 4, 6});
        resource.start(xid2, XAResource.TMNOFLAGS);
        producer.send(session.createTextMessage("I'm going to get rolled back."));
        resource.end(xid2, XAResource.TMSUCCESS);
        assertEquals(XAResource.XA_OK, resource.prepare(xid2));


        XidStub xid3 = new XidStub(new byte[]{2, 2, 3, 4, 6});
        resource.start(xid3, XAResource.TMNOFLAGS);
        producer.send(outbound[1]);
        resource.end(xid3, XAResource.TMSUCCESS);

        assertEquals(XAResource.XA_OK, resource.prepare(xid3));

        restart();

        resource = ((XASession) session).getXAResource();

        // lets commit a transaction
        resource.commit(xid3, false);

        // lets try rollback a transaction
        resource.rollback(xid2);

    }


    abstract protected JmsResourceProvider getJmsResourceProvider();

    protected void setUp() throws Exception {
        super.setUp();
        openConnection();
    }

    protected void restart() throws JMSException {
        closeConnection();
        openConnection();
    }

    protected void openConnection() throws JMSException {
        JmsResourceProvider p = getJmsResourceProvider();

        connectionFactory = p.createConnectionFactory();
        connection = p.createConnection(connectionFactory);
        System.out.println("Created connection: " + connection);
        session = p.createSession(connection);
        System.out.println("Created session: " + session);
        Destination destination = p.createDestination(session, "FOO");
        System.out.println("Created destination: " + destination + " of type: " + destination.getClass());
        producer = p.createProducer(session, destination);
        System.out.println("Created producer: " + producer);
        consumer = p.createConsumer(session, destination);
        System.out.println("Created consumer: " + consumer);
        connection.start();
    }

    protected void tearDown() throws Exception {
        System.out.println("Test Done.  Stats");
        ((ActiveMQConnectionFactory) connectionFactory).getFactoryStats().dump(new IndentPrinter());
        closeConnection();
    }

    private void closeConnection() throws JMSException {
        System.out.println("Closing down connection");

        connection.stop();
        connection.close();
        System.out.println("Connection closed.");
    }

}
