/*
 * JBoss, Home of Professional Open Source
 * Copyright 2006, Red Hat Middleware LLC, and individual contributors
 * as indicated by the @author tags.
 * See the copyright.txt in the distribution for a full listing
 * of individual contributors.
 * This copyrighted material is made available to anyone wishing to use,
 * modify, copy, or redistribute it subject to the terms and conditions
 * of the GNU Lesser General Public License, v. 2.1.
 * This program is distributed in the hope that it will be useful, but WITHOUT A
 * 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,
 * v.2.1 along with this distribution; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
 * MA  02110-1301, USA.
 *
 * (C) 2005-2006,
 * @author JBoss Inc.
 */
/*
 * Copyright (C) 2002, 2003, 2004,
 *
 * Arjuna Technologies Limited,
 * Newcastle upon Tyne,
 * Tyne and Wear,
 * UK.
 *
 * $Id: DurableTwoPhaseCommitParticipant.java,v 1.1.2.2 2004/08/09 12:34:24 nmcl Exp $
 */

package com.arjuna.mwlabs.wst.at.participants;

import com.arjuna.wst.Durable2PCParticipant;

import com.arjuna.ats.arjuna.state.InputObjectState;
import com.arjuna.ats.arjuna.state.OutputObjectState;
import com.arjuna.mw.wscf.model.twophase.participants.*;
import com.arjuna.mw.wscf.model.twophase.exceptions.*;
import com.arjuna.mw.wscf.model.twophase.vote.*;

import com.arjuna.mw.wscf.exceptions.*;

import com.arjuna.mw.wsas.exceptions.SystemException;
import com.arjuna.mw.wsas.exceptions.WrongStateException;
import com.arjuna.mw.wsas.exceptions.SystemCommunicationException;
import com.arjuna.mw.wstx.logging.wstxLogger;
import com.arjuna.mwlabs.wst.util.PersistableParticipantHelper;

/**
 * @author Mark Little (mark.little@arjuna.com)
 * @version $Id: DurableTwoPhaseCommitParticipant.java,v 1.1.2.2 2004/08/09
 *          12:34:24 nmcl Exp $
 * @since 1.0.
 */

public class DurableTwoPhaseCommitParticipant implements Participant
{
    // default ctor for crash recovery
    public DurableTwoPhaseCommitParticipant() {
    }

    public DurableTwoPhaseCommitParticipant (Durable2PCParticipant resource, String identifier)
	{
		_resource = resource;
		_id = identifier;
	}

    /**
     * tell the participant to prepare
     *
     * @return the participant's vote or a cancel vote the aprticipant is null
     *
     * @message com.arjuna.mwlabs.wst.at.participants.DurableTwoPhaseCommitParticipant.prepare_1 [com.arjuna.mwlabs.wst.at.participants.DurableTwoPhaseCommitParticipant.prepare_1] comms timeout attempting to prepare WS-AT participant {0}
     */
    public Vote prepare () throws InvalidParticipantException,
			WrongStateException, HeuristicHazardException,
			HeuristicMixedException, SystemException
	{
		try
		{
			if (_resource != null)
			{
				com.arjuna.wst.Vote vt = _resource.prepare();

				if (vt instanceof com.arjuna.wst.ReadOnly)
				{
					_readonly = true;

					return new VoteReadOnly();
				}
				else
				{
					if (vt instanceof com.arjuna.wst.Prepared)
						return new VoteConfirm();
					else
					{
						_rolledback = true;

						return new VoteCancel();
					}
				}
			}
			else
				return new VoteCancel();
		}
		//	catch (com.arjuna.mw.wst.exceptions.WrongStateException ex)
		catch (com.arjuna.wst.WrongStateException ex)
		{
			throw new WrongStateException(ex.toString());
		}
		/*
		 * catch (com.arjuna.mw.wst.exceptions.HeuristicHazardException ex {
		 * throw new HeuristicHazardException(ex.toString()); } catch
		 * (com.arjuna.mw.wst.exceptions.HeuristicMixedException ex) { throw new
		 * HeuristicMixedException(ex.toString()); }
		 */
		//	catch (com.arjuna.mw.wst.exceptions.SystemException ex)
		catch (com.arjuna.wst.SystemException ex)
		{
            if(ex instanceof com.arjuna.wst.stub.SystemCommunicationException) {
                // log an error here or else the participant may be left hanging
                // waiting for a prepare
                if (wstxLogger.arjLoggerI18N.isErrorEnabled()) {
                    wstxLogger.arjLoggerI18N.error("com.arjuna.mwlabs.wst.at.participants.DurableTwoPhaseCommitParticipant.prepare_1", new Object[] { _id });
                }
                throw new SystemCommunicationException(ex.toString());
            } else {
			throw new SystemException(ex.toString());
            }
        }
	}

    /**
     * attempt to commit the participant
     *
     * @message com.arjuna.mwlabs.wst.at.participants.DurableTwoPhaseCommitParticipant.confirm_1 [com.arjuna.mwlabs.wst.at.participants.DurableTwoPhaseCommitParticipant.confirm_1] comms timeout attempting to commit WS-AT participant {0}
     */
    public void confirm () throws InvalidParticipantException,
			WrongStateException, HeuristicHazardException,
			HeuristicMixedException, HeuristicCancelException, SystemException
	{
		if (_resource != null)
		{
			try
			{
				if (!_readonly)
				{
					_resource.commit();
				}
			}
			//	    catch (com.arjuna.mw.wst.exceptions.WrongStateException ex)
			catch (com.arjuna.wst.WrongStateException ex)
			{
				throw new WrongStateException(ex.toString());
			}
			/*
			 * catch (com.arjuna.mw.wst.exceptions.HeuristicHazardException ex) {
			 * throw new HeuristicHazardException(ex.toString()); } catch
			 * (com.arjuna.mw.wst.exceptions.HeuristicMixedException ex) { throw
			 * new HeuristicMixedException(ex.toString()); } catch
			 * (com.arjuna.mw.wst.exceptions.HeuristicRollbackException ex) {
			 * throw new HeuristicCancelException(ex.toString()); }
			 */
			//	    catch (com.arjuna.mw.wst.exceptions.SystemException ex)
			catch (com.arjuna.wst.SystemException ex)
			{
				if(ex instanceof com.arjuna.wst.stub.SystemCommunicationException) {
                    // log an error here -- we will end up writing a heuristic transaction record too
                    if (wstxLogger.arjLoggerI18N.isErrorEnabled()) {
                        wstxLogger.arjLoggerI18N.error("com.arjuna.mwlabs.wst.at.participants.DurableTwoPhaseCommitParticipant.confirm_1", new Object[] { _id });
                    }
                    throw new SystemCommunicationException(ex.toString());
                } else {
                    throw new SystemException(ex.toString());
                }
            }
		}
		else
			throw new InvalidParticipantException();
	}

    /**
     * attempt to cancel the participant
     *
     * @message com.arjuna.mwlabs.wst.at.participants.DurableTwoPhaseCommitParticipant.cancel_1 [com.arjuna.mwlabs.wst.at.participants.DurableTwoPhaseCommitParticipant.cancel_1] comms timeout attempting to cancel WS-AT participant {0}
     */
	public void cancel () throws InvalidParticipantException,
			WrongStateException, HeuristicHazardException,
			HeuristicMixedException, HeuristicConfirmException, SystemException
	{
		if (_resource != null)
		{
			try
			{
				if (!_rolledback)
					_resource.rollback();
			}
			//	    catch (com.arjuna.mw.wst.exceptions.WrongStateException ex)
			catch (com.arjuna.wst.WrongStateException ex)
			{
				throw new WrongStateException(ex.toString());
			}
			/*
			 * catch (com.arjuna.mw.wst.exceptions.HeuristicHazardException ex) {
			 * throw new HeuristicHazardException(ex.toString()); } catch
			 * (com.arjuna.mw.wst.exceptions.HeuristicMixedException ex) { throw
			 * new HeuristicMixedException(ex.toString()); } catch
			 * (com.arjuna.mw.wst.exceptions.HeuristicCommitException ex) {
			 * throw new HeuristicConfirmException(ex.toString()); }
			 */
			//	    catch (com.arjuna.mw.wst.exceptions.SystemException ex)
			catch (com.arjuna.wst.SystemException ex)
			{
                if(ex instanceof com.arjuna.wst.stub.SystemCommunicationException) {
                    // log an error here -- if the participant is dead it will retry anyway
                    if (wstxLogger.arjLoggerI18N.isErrorEnabled()) {
                        wstxLogger.arjLoggerI18N.error("com.arjuna.mwlabs.wst.at.participants.DurableTwoPhaseCommitParticipant.cancel_1", new Object[] { _id });
                    }
                    throw new SystemCommunicationException(ex.toString());
                } else {
                    throw new SystemException(ex.toString());
                }
			}
		}
		else
			throw new InvalidParticipantException();
	}

	// TODO mark ParticipantCancelledException explicitly?

    public void confirmOnePhase () throws InvalidParticipantException,
			WrongStateException, HeuristicHazardException,
			HeuristicMixedException, HeuristicCancelException, SystemException
	{
		if (_resource != null)
		{
			Vote v = null;

			try
			{
				v = prepare();
			}
            catch (Exception ex)
			{
                // either the prepare timed out or the participant was invalid or in an
                // invalid state
                
                ex.printStackTrace();

				v = new VoteCancel();
			}

                        if (v instanceof VoteReadOnly)
                        {
                            _readonly = true;
                        }
                        else if (v instanceof VoteCancel)
			{
				_rolledback = false;

                // TODO only do this if we didn't return VoteCancel

                try {
                    cancel();
                } catch (SystemCommunicationException sce) {
                    // if the rollback times out as well as the prepare we
                    // return an exception which indicates a failed transaction
                }
                throw new ParticipantCancelledException();
			}
			else
			{
				if (v instanceof VoteConfirm)
				{
					try
					{
						confirm();
					}
					catch (HeuristicHazardException ex)
					{
						throw ex;
					}
					catch (HeuristicMixedException ex)
					{
						throw ex;
					}
					catch (HeuristicCancelException ex)
					{
						throw ex;
					}
					catch (Exception ex)
					{
						throw new HeuristicHazardException();
					}
				}
				else
				{
					cancel(); // TODO error

					throw new HeuristicHazardException();
				}
			}
		}
		else
			throw new InvalidParticipantException();
	}

	public void forget () throws InvalidParticipantException,
			WrongStateException, SystemException
	{
	}

	public void unknown () throws SystemException
	{
		/*
		 * If the transaction is unknown, then we assume it rolled back.
		 */

		try
		{
			cancel();
		}
		catch (Exception ex)
		{
			// TODO
		}
	}

	public String id () throws SystemException
	{
		return _id;
	}

	public boolean save_state (OutputObjectState os)
	{
        return PersistableParticipantHelper.save_state(os, _resource) ;
	}

	public boolean restore_state (InputObjectState os)
	{
        _resource = (Durable2PCParticipant)PersistableParticipantHelper.restore_state(os) ;
        return true ;
	}

	private Durable2PCParticipant _resource;
	private String _id;
	private boolean _readonly;
	private boolean _rolledback;

}
