org.objectweb.howl.log.LogFile Maven / Gradle / Ivy
/*
* JOnAS: Java(TM) Open Application Server
* Copyright (C) 2004 Bull S.A.
* All rights reserved.
*
* Contact: [email protected]
*
* This software is licensed under the BSD license.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* ------------------------------------------------------------------------------
* $Id: LogFile.java,v 1.12 2005/11/14 19:49:10 girouxm Exp $
* ------------------------------------------------------------------------------
*/
package org.objectweb.howl.log;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.File;
import java.io.RandomAccessFile;
import java.nio.channels.FileChannel;
import java.nio.channels.FileLock;
/**
* An individual file within a set of log files managed by a Logger.
*
* The Logger will create an instance of LogFile for each physical file that is
* configured for the Logger.
*
* @author Michael Giroux
*
*/
class LogFile
{
File file = null;
/**
* @see java.io.RandomAccessFile#RandomAccessFile(java.lang.String, java.lang.String)
*/
String fileMode = "rw";
/**
* FileChannel associated with this LogFile.
*
*
The FileChannel is private to guarantee that all calls to the
* channel methods come through this LogFile object to allow
* for statistics collection.
*/
FileChannel channel = null;
/**
* number of times this file position was reset to zero.
*/
int rewindCounter = 0;
/**
* total number of data written.
*/
long bytesWritten = 0;
/**
* log key for the first record in the next file.
*
when a file switch occurs, the LogFileManager stores the mark
* for the file header record of the next log file into this LogFile object.
* Effectively, any mark with a value less than the header record
* for the next log file resides in this or some previous log file.
*
*
Later, when this LogFile object is about to be reused, the value for the
* active mark is compared with the highMark value.
* If the active mark is less than highMark, then a LogFileOverflowException
* is thrown to prevent re-use of a log file that contains active data.
*
*
While this LogFile is the current LogFile of the set,
* (ie, the LogFile that is currently being written to)
* the highMark will be set to first record of the subsequent
* block. For example, while block 1 is being written,
* highMark will be set to the first record of block 2.
*
*
During replay operations, the end of the active
* journal can be detected by comparing the BSN of a desired
* block with the current highMark. If the requested BSN
* is less than highMark, then the requested block resides
* within active journal space. If the requested BSN is
* >= the highMark, then the BSN is invalid.
*
*
Any attempt to add records to the log will cause an exception
* until Logger.mark() is called to clear prior records from the log.
*/
long highMark = Long.MIN_VALUE;
/**
* BSN of first block in the file.
*
*
Initialized by LogFileManager and updated as
* log files are reused.
*
Used by LogFileManager.read() to calculate offset into a file to
* read a specific block.
*/
int firstBSN = 0;
/**
* currentTimeMillis when LogFileManager switched to this LogFile.
*/
long tod = 0;
/**
* FileChannel.position() of last read or write.
*
*
May be used to report the file position when IOException occurs.
*/
long position = 0;
/**
* indicates the file was created during the call to open()
* @see #open(String filemode)
*/
boolean newFile = true;
/**
* FileLock acquired when file is opened.
*/
FileLock lock = null;
/**
* construct an instance of LogFile for a given file name
* @param file filename
*/
LogFile(File file)
{
this.file = file;
}
/**
* open the file and get the associated nio FileChannel for the file.
*
*
If the file does not exist, then the newFile member is set true.
*
* @param fileMode value passed to RandomAccessFile constructor.
* @throws FileNotFoundException
* if the parent directory structure does not exist.
* @see java.io.RandomAccessFile#RandomAccessFile(java.lang.String, java.lang.String)
*/
LogFile open(String fileMode) throws LogConfigurationException, FileNotFoundException
{
this.fileMode = fileMode;
// remember whether the file existed or not
newFile = !file.exists();
// if it already existed, but length is zero, then it is still a new file
if (!newFile) newFile = file.length() == 0;
channel = new RandomAccessFile(file, fileMode).getChannel();
assert channel != null : "RandomAccessFile() returns null";
// FEATURE 300922; lock file to prevent simultanious access
try {
lock = channel.tryLock();
// TODO log lock event
// if (lock != null) System.err.println(file.getName() + " locked");
} catch (IOException e) {
throw new LogConfigurationException(e);
}
if (lock == null) {
// TODO: log lock failed
// System.err.println(file.getName() + " unable to lock");
throw new LogConfigurationException("Unable to obtain lock on " + file.getAbsolutePath());
}
if (lock.isShared()) {
// TODO: log lock is shared
// System.err.println(file.getName() + " lock is shared");
throw new LogConfigurationException("Shared lock on " + file.getAbsolutePath());
}
// TODO: log lock acquired
// System.err.println(file.getName() + " open");
return this;
}
/**
* Close the channel associated with this LogFile.
*
Also releases the lock that is held on the file.
* @return this LogFile
* @throws IOException
*/
LogFile close() throws IOException
{
try {
if (channel.isOpen())
{
// prevent multiple close
position = channel.position(); // remember postion at close
// FEATURE 300922; unlock the file if we obtained a lock.
if (lock != null)
{
lock.release();
// TODO: log lock released
// System.err.println(file.getName() + " unlocked");
}
channel.close();
// TODO: log file closed
// System.err.println(file.getName() + " closed");
}
} catch(IOException e) {
// BUG 303907 - add message to IOException
IOException ioe = new IOException("LogFile.close(): attempting to close " +
file.getName() + " [" + e.getMessage() + "]");;
ioe.setStackTrace(e.getStackTrace());
throw ioe;
}
return this;
}
/**
* Helper provides access to the FileChannel.write() method for
* the FileChannel associated with this LogFile.
* @param lb Reference to a LogBuffer object that is to be written.
* @throws IOException
*/
void write(LogBuffer lb) throws IOException
{
try {
if (lb.rewind)
{
channel.position(0);
++rewindCounter;
lb.rewind = false;
}
bytesWritten += channel.write(lb.buffer);
position = channel.position();
} catch (IOException e) {
// BUG 303907 - add message to IOException
IOException ioe = new IOException("LogFile.write(): attempting to write " +
file.getName() + " [" + e.getMessage() + "]");
ioe.setStackTrace(e.getStackTrace());
throw ioe;
}
}
/**
* Helper provides access to the FileChannel.force() method for
* the FileChannel associated with this LogFile.
*
*
Hides actual FileChannel and allows capture of statistics.
*
*
In theory the force could be eliminated if the
* file is open with mode "rwd" or "rws" because the
* access method is supposed to guarantee that the
* writes do not return until the data is on the media.
* Unfortunately, testing with Windows XP platforms
* suggests that system write cache may confuse the
* Java runtime and the program will actually return
* before data is on media. Consequently, this
* method *always* does a force() regardless of
* the file open mode.
*
* @param forceMetadata as defined by FileChannel.force()
* @throws IOException
* @see FileChannel#force(boolean)
*/
void force(boolean forceMetadata) throws IOException
{
try {
channel.force(forceMetadata);
} catch (IOException e) {
// BUG 303907 - add message to IOException
IOException ioe = new IOException("LogFile.force(): attempting to force" +
file.getName() + " [" + e.getMessage() + "]");
ioe.setStackTrace(e.getStackTrace());
throw ioe;
}
}
/**
* return statistics for this LogFile as an XML string.
* @return XML string containing LogFile statistics.
*/
String getStats()
{
StringBuffer result = new StringBuffer("\n" +
"\n Number of times this file was rewind to position(0) " +
"\n Number of bytes written to the file " +
"\n FileChannel.position() " +
"\n " +
"\n"
);
return result.toString();
}
}