/*
 * Copyright (c) 2002-2009 "Neo Technology,"
 *     Network Engine for Objects in Lund AB [http://neotechnology.com]
 *
 * This file is part of Neo4j.
 * 
 * Neo4j is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 * 
 * This program 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 Affero General Public License for more details.
 * 
 * You should have received a copy of the GNU Affero General Public License
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
 */
package org.neo4j.kernel.impl.transaction.xaframework;

import java.io.IOException;

// TODO: make writeToFile safe (not being able to do anything but write a
// sequence of bytes that contains the command)

/**
 * A command is part of the work done during a transaction towards a XA
 * resource. Any modifying operation invoked on a <CODE>XaResource</CODE> must
 * be in a transaction and wrapped in a <CODE>XaCommand</CODE>. Each command
 * created will be written to the {@link XaLogicalLog} once it is added to the
 * transaction.
 * <p>
 * Commands are memory buckets containing information on how to presist the
 * operation in the XA resource. Any nececcery constraint checks must take place
 * before any command has been executed. Once a command in the transaction has
 * started to execute there is no turning back, all of the following commands
 * must execute without problems. To ensure this check the constraints via a tx
 * synchronization hook, at prepare or upon command creation and signal the
 * transaction before it starts to commit if something is wrong.
 * <p>
 * Throwing exception when the command rollbacks or executes could (depending on
 * TransactionManager implementation) bring down the whole system. Should
 * however a <CODE>execute</CODE> or <CODE>rollback</CODE> call fail throw a
 * proper runtime exception.
 * <p>
 * Note that a command is during normal execution only executed once but it must
 * be able to execute again "re-writing" it self to underlying persistance store
 * during recovery. Think of the following scenario:
 * <p>
 * A transaction containing 50 commands begins to commit. When 24.5 commands
 * have been executed the system crashes. System is brought up again and the
 * transaction is recreated. The transaction manager tells the resource to
 * commit (again) and the 25 first commands will be executed for a second time
 * while the 25 last commands will be executed for the first time. Also it would
 * be possible for the system to crash during recovery causing commands to
 * execute again and again and so on.
 */
public abstract class XaCommand
{
    private boolean isRecovered = false;

    /**
     * Default implementation of rollback that does nothing. This method is not
     * to undo any work done by the {@link #execute} method. Commands in a
     * {@link XaTransaction} are either all rolled back or all executed, they're
     * not linked together as usual execute/rollback methods.
     * <p>
     * Since a command only is in memory nothing has been made persistent so
     * rollback usually don't have to do anything. Sometimes however a command
     * needs to acquire resources when created (since the application thinks it
     * has done the work when the command is created). For example, if a command
     * creates some entity that has a primary id we need to generate that id
     * upon command creation. But if the command is rolled back we should
     * release that id. This is the place to do just that.
     */
    public void rollback()
    {
    };

    /**
     * Executes the command and makes it persistent. This method must succeed,
     * any protests about this command not being able to execute should be done
     * before execution of any command within the transaction.
     */
    public abstract void execute();

    /**
     * When a command is added to a transaction (usually when it is created) it
     * must be written to the {@link XaLogicalLog}. This method should write
     * all the data that is needed to re-create the command (see
     * {@link XaCommandFactory}).
     * <p>
     * Write the data to the <CODE>fileChannel</CODE>, you can use the 
     * <CODE>buffer</CODE> supplied or create your own buffer since its capacity
     * is very small (137 bytes or something). Acccess to writing commands is 
     * synchronized, only one command will be written at a time so if you need 
     * to write larger data sets the commands can share the same buffer.
     * <p>
     * Don't throw an <CODE>IOException</CODE> to imply something is wrong
     * with the command. An exception should only be thrown here if there is a
     * real IO failure. If something is wrong with this command it should have
     * been detected when it was created.
     * <p>
     * Don't <CODE>force</CODE>, <CODE>position</CODE> or anything except
     * normal forward <CODE>write</CODE> with the file channel.
     * 
     * @param fileChannel
     *            The channel to the {@link XaLogicalLog}
     * @param buffer
     *            A small byte buffer that can be used to write command data
     * @throws IOException
     *             In case of *real* IO failure
     */
    public abstract void writeToFile( LogBuffer buffer ) throws IOException;

    /**
     * If this command is created by the command factory during a recovery scan
     * of the logical log this method will be called to mark the command as a
     * "recovered command".
     */
    protected void setRecovered()
    {
        isRecovered = true;
    }

    /**
     * Returns wether or not this is a "recovered command".
     * 
     * @return <CODE>true</CODE> if command was created during a recovery else
     *         <CODE>false</CODE> is returned
     */
    public boolean isRecovered()
    {
        return isRecovered;
    }
}