/* * Copyright (c) 2002-2010 "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; import java.nio.ByteBuffer; import java.nio.channels.ReadableByteChannel; import javax.transaction.xa.Xid; import org.neo4j.kernel.impl.transaction.XidImpl; public class LogIoUtils { public static LogEntry readEntry( ByteBuffer buffer, ReadableByteChannel channel, XaCommandFactory cf ) throws IOException { try { byte entry = readNextByte( buffer, channel ); switch ( entry ) { case LogEntry.TX_START: return readTxStartEntry( buffer, channel ); case LogEntry.TX_PREPARE: return readTxPrepareEntry( buffer, channel ); case LogEntry.TX_1P_COMMIT: return readTxOnePhaseCommitEntry( buffer, channel ); case LogEntry.TX_2P_COMMIT: return readTxTwoPhaseCommitEntry( buffer, channel ); case LogEntry.COMMAND: return readTxCommandEntry( buffer, channel, cf ); case LogEntry.DONE: return readTxDoneEntry( buffer, channel ); case LogEntry.EMPTY: return null; default: throw new IOException( "Unknown entry[" + entry + "]" ); } } catch ( ReadPastEndException e ) { return null; } } private static LogEntry.Start readTxStartEntry( ByteBuffer buf, ReadableByteChannel channel ) throws IOException, ReadPastEndException { byte globalIdLength = readNextByte( buf, channel ); byte branchIdLength = readNextByte( buf, channel ); byte globalId[] = new byte[globalIdLength]; readIntoBufferAndFlip( ByteBuffer.wrap( globalId ), channel, globalIdLength ); byte branchId[] = new byte[branchIdLength]; readIntoBufferAndFlip( ByteBuffer.wrap( branchId ), channel, branchIdLength ); int identifier = readNextInt( buf, channel ); int formatId = readNextInt( buf, channel ); // re-create the transaction Xid xid = new XidImpl( globalId, branchId, formatId ); return new LogEntry.Start( xid, identifier, -1 ); } private static LogEntry.Prepare readTxPrepareEntry( ByteBuffer buf, ReadableByteChannel channel ) throws IOException, ReadPastEndException { return new LogEntry.Prepare( readNextInt( buf, channel ) ); } private static LogEntry.OnePhaseCommit readTxOnePhaseCommitEntry( ByteBuffer buf, ReadableByteChannel channel ) throws IOException, ReadPastEndException { return new LogEntry.OnePhaseCommit( readNextInt( buf, channel ), readNextLong( buf, channel ), readNextInt( buf, channel ) ); } private static LogEntry.Done readTxDoneEntry( ByteBuffer buf, ReadableByteChannel channel ) throws IOException, ReadPastEndException { return new LogEntry.Done( readNextInt( buf, channel ) ); } private static LogEntry.TwoPhaseCommit readTxTwoPhaseCommitEntry( ByteBuffer buf, ReadableByteChannel channel ) throws IOException, ReadPastEndException { return new LogEntry.TwoPhaseCommit( readNextInt( buf, channel ), readNextLong( buf, channel ), readNextInt( buf, channel ) ); } private static LogEntry.Command readTxCommandEntry( ByteBuffer buf, ReadableByteChannel channel, XaCommandFactory cf ) throws IOException, ReadPastEndException { int identifier = readNextInt( buf, channel ); XaCommand command = cf.readCommand( channel, buf ); if ( command == null ) { return null; } return new LogEntry.Command( identifier, command ); } public static void writeLogEntry( LogEntry entry, LogBuffer buffer ) throws IOException { if ( entry instanceof LogEntry.Command ) { buffer.put( LogEntry.COMMAND ).putInt( entry.getIdentifier() ); XaCommand command = ((LogEntry.Command) entry).getXaCommand(); command.writeToFile( buffer ); } else if ( entry instanceof LogEntry.Start ) { LogEntry.Start start = (LogEntry.Start) entry; Xid xid = start.getXid(); byte globalId[] = xid.getGlobalTransactionId(); byte branchId[] = xid.getBranchQualifier(); int formatId = xid.getFormatId(); int identifier = start.getIdentifier(); buffer.put( LogEntry.TX_START ).put( (byte) globalId.length ).put( (byte) branchId.length ).put( globalId ).put( branchId ) .putInt( identifier ).putInt( formatId ); } else if ( entry instanceof LogEntry.Done ) { buffer.put( LogEntry.DONE ).putInt( entry.getIdentifier() ); } else if ( entry instanceof LogEntry.OnePhaseCommit ) { LogEntry.Commit commit = (LogEntry.Commit) entry; buffer.put( LogEntry.TX_1P_COMMIT ).putInt( commit.getIdentifier() ).putLong( commit.getTxId() ).putInt( commit.getMasterId() ); } else if ( entry instanceof LogEntry.Prepare ) { buffer.put( LogEntry.TX_PREPARE ).putInt( entry.getIdentifier() ); } else if ( entry instanceof LogEntry.TwoPhaseCommit ) { LogEntry.Commit commit = (LogEntry.Commit) entry; buffer.put( LogEntry.TX_2P_COMMIT ).putInt( commit.getIdentifier() ).putLong( commit.getTxId() ).putInt( commit.getMasterId() ); } } private static int readNextInt( ByteBuffer buf, ReadableByteChannel channel ) throws IOException, ReadPastEndException { return readIntoBufferAndFlip( buf, channel, 4 ).getInt(); } private static long readNextLong( ByteBuffer buf, ReadableByteChannel channel ) throws IOException, ReadPastEndException { return readIntoBufferAndFlip( buf, channel, 8 ).getLong(); } private static byte readNextByte( ByteBuffer buf, ReadableByteChannel channel ) throws IOException, ReadPastEndException { return readIntoBufferAndFlip( buf, channel, 1 ).get(); } private static ByteBuffer readIntoBufferAndFlip( ByteBuffer buf, ReadableByteChannel channel, int numberOfBytes ) throws IOException, ReadPastEndException { buf.clear(); buf.limit( numberOfBytes ); if ( channel.read( buf ) != buf.limit() ) { throw new ReadPastEndException(); } buf.flip(); return buf; } }