/*
 * Copyright 2009 Red Hat, Inc.
 * Red Hat licenses this file to you 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.hornetq.javaee.example.server;

import javax.ejb.Remote;
import javax.ejb.Stateless;
import javax.ejb.TransactionManagement;
import javax.ejb.TransactionManagementType;
import javax.jms.MessageProducer;
import javax.jms.Queue;
import javax.jms.TextMessage;
import javax.jms.XAConnection;
import javax.jms.XAConnectionFactory;
import javax.jms.XASession;
import javax.naming.InitialContext;
import javax.transaction.Transaction;
import javax.transaction.TransactionManager;
import javax.transaction.xa.XAException;
import javax.transaction.xa.XAResource;
import javax.transaction.xa.Xid;

/**
 * An EJB which sends a JMS message in the transaction and "pauses" while the transaction
 * is prepared so that the server can be crashed in this state.
 * The JMS message will be recovered when the server is restarted.
 *
 * @author <a href="mailto:andy.taylor@jboss.org">Andy Taylor</a>
 * @author <a href="mailto:jmesnil@redhat.com">Jeff Mesnil</a>
 */
@Stateless
@Remote(XARecoveryExampleService.class)
@TransactionManagement(TransactionManagementType.BEAN)
public class XARecoveryExampleBean implements XARecoveryExampleService
{

   public void send(final String text) throws Exception
   {
      InitialContext ic = null;
      XAConnection xaConnection = null;
      try
      {
         // Step 1. Create the initial context
         ic = new InitialContext();

         // Step 2. Lookup the Transaction Manager
         TransactionManager tm = (TransactionManager)ic.lookup("java:/TransactionManager");

         // Step 3. Look up the XA Connection Factory
         XAConnectionFactory xacf = (XAConnectionFactory)ic.lookup("java:/XAConnectionFactory");

         // Step 4. Look up the Queue
         Queue queue = (Queue)ic.lookup("queue/testQueue");

         // Step 5. Create a XA connection, a XA session and a message producer for the queue
         xaConnection = xacf.createXAConnection();
         XASession session = xaConnection.createXASession();
         MessageProducer messageProducer = session.createProducer(queue);

         // Step 6. Create a "fake" XAResource which will crash the server in its commit phase
         XAResource failingXAResource = new FailingXAResource();

         // Step 7. Begin the transaction
         tm.begin();
         Transaction tx = tm.getTransaction();

         // Step 8. Enlist the failing XAResource
         tx.enlistResource(failingXAResource);

         // Step 9. Enlist the JMS XAResource
         tx.enlistResource(session.getXAResource());

         // Step 10. Send The Text Message
         TextMessage message = session.createTextMessage(text);
         messageProducer.send(message);
         System.out.format("Sent message: %s\n\t(JMS MessageID: %s)\n", message.getText(), message.getJMSMessageID());

         // Step 12. Delist the failing XAResource
         tx.delistResource(failingXAResource, XAResource.TMSUCCESS);

         // Step 13. Delist the JMS XAResource
         tx.delistResource(session.getXAResource(), XAResource.TMSUCCESS);

         // Step 14. Commit the transaction
         // Both XA Resources will be prepared.
         // then the failingXAResource will crash the server in its commit phase
         // and the commit method will never be called on the JMS XA Resource: it will
         // be in the prepared state when the server crashes
         System.out.println("committing the tx");
         tx.commit();
      }
      finally
      {
         // Step 15. Be sure to close all resources!
         if (ic != null)
         {
            ic.close();
         }
         if (xaConnection != null)
         {
            xaConnection.close();
         }
      }
   }

   /**
    * A XAResource which crashes the server in its commit phase
    */
   private static class FailingXAResource implements XAResource
   {

      public void commit(final Xid arg0, final boolean arg1) throws XAException
      {
         System.out.println("########################");
         System.out.println("# Crashing the server! #");
         System.out.println("########################");
         Runtime.getRuntime().halt(1);
      }

      public void end(final Xid arg0, final int arg1) throws XAException
      {
      }

      public void forget(final Xid arg0) throws XAException
      {
      }

      public int getTransactionTimeout() throws XAException
      {
         return 0;
      }

      public boolean isSameRM(final XAResource arg0) throws XAException
      {
         return false;
      }

      public int prepare(final Xid arg0) throws XAException
      {

         return XAResource.XA_OK;
      }

      public Xid[] recover(final int arg0) throws XAException
      {
         return null;
      }

      public void rollback(final Xid arg0) throws XAException
      {
      }

      public boolean setTransactionTimeout(final int arg0) throws XAException
      {
         return false;
      }

      public void start(final Xid arg0, final int arg1) throws XAException
      {
      }

   }
}
