/* * 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; } }