/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code 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 General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/**
* This class is a simple implementation of a reliable Log. The
* client of a ReliableLog must provide a set of callbacks (via a
* LogHandler) that enables a ReliableLog to read and write
* checkpoints and log records. This implementation ensures that the
* current value of the data stored (via a ReliableLog) is recoverable
* after a system crash. <p>
*
* The secondary storage strategy is to record values in files using a
* representation of the caller's choosing. Two sorts of files are
* kept: snapshots and logs. At any instant, one snapshot is current.
* The log consists of a sequence of updates that have occurred since
* the current snapshot was taken. The current stable state is the
* value of the snapshot, as modified by the sequence of updates in
* the log. From time to time, the client of a ReliableLog instructs
* the package to make a new snapshot and clear the log. A ReliableLog
* arranges disk writes such that updates are stable (as long as the
* changes are force-written to disk) and atomic : no update is lost,
* and each update either is recorded completely in the log or not at
* all. Making a new snapshot is also atomic. <p>
*
* Normal use for maintaining the recoverable store is as follows: The
* client maintains the relevant data structure in virtual memory. As
* updates happen to the structure, the client informs the ReliableLog
* (all it "log") by calling log.update. Periodically, the client
* calls log.snapshot to provide the current value of the data
* structure. On restart, the client calls log.recover to obtain the
* latest snapshot and the following sequences of updates; the client
* applies the updates to the snapshot to obtain the state that
* existed before the crash. <p>
*
* The current logfile format is: <ol>
* <li> a format version number (two 4-octet integers, major and
* minor), followed by
* <li> a sequence of log records. Each log record contains, in
* order, <ol>
* <li> a 4-octet integer representing the length of the following log
* data,
* <li> the log data (variable length). </ol> </ol> <p>
*
* @see LogHandler
*
* @author Ann Wollrath
*
*/
public class ReliableLog {
// sun.rmi.log.debug=false
private boolean Debug = false;
//private long padBoundary = intBytes;
/**
* Constructor for the log file. If the system property
* sun.rmi.log.class is non-null and the class specified by this
* property a) can be loaded, b) is a subclass of LogFile, and c) has a
* public two-arg constructor (String, String), ReliableLog uses the
* constructor to construct the LogFile.
**/
private static final Constructor<? extends LogFile>
/**
* Creates a ReliableLog to handle checkpoints and logging in a
* stable storage directory.
*
* @param dirPath path to the stable storage directory
* @param logCl the closure object containing callbacks for logging and
* recovery
* @param pad ignored
* @exception IOException If a directory creation error has
* occurred or if initialSnapshot callback raises an exception or
* if an exception occurs during invocation of the handler's
* snapshot method or if other IOException occurs.
*/
boolean pad)
throws IOException
{
super();
// create directory
throw new IOException("could not create directory for log: " +
dirPath);
}
}
//padBoundary = (pad ? diskPageSize : intBytes);
lastSnapshot = 0;
lastLog = 0;
getVersion();
if (version == 0) {
try {
} catch (IOException e) {
throw e;
} catch (Exception e) {
throw new IOException("initial snapshot failed with " +
"exception: " + e);
}
}
}
/**
* Creates a ReliableLog to handle checkpoints and logging in a
* stable storage directory.
*
* @param dirPath path to the stable storage directory
* @param logCl the closure object containing callbacks for logging and
* recovery
* @exception IOException If a directory creation error has
* occurred or if initialSnapshot callback raises an exception
*/
throws IOException
{
}
/* public methods */
/**
* Returns an object which is the value recorded in the current
* snapshot. This snapshot is recovered by calling the client
* supplied callback "recover" and then subsequently invoking
* the "readUpdate" callback to apply any logged updates to the state.
*
* @exception IOException If recovery fails due to serious log
* corruption, read update failure, or if an exception occurs
* during the recover callback
*/
throws IOException
{
if (Debug)
if (version == 0)
return null;
if (Debug)
try {
try {
} catch (IOException e) {
throw e;
} catch (Exception e) {
if (Debug)
throw new IOException("log recover failed with " +
"exception: " + e);
}
} finally {
}
return recoverUpdates(snapshot);
}
/**
* Records this update in the log file (does not force update to disk).
* The update is recorded by calling the client's "writeUpdate" callback.
* This method must not be called until this log's recover method has
* been invoked (and completed).
*
* @param value the object representing the update
* @exception IOException If an exception occurred during a
* writeUpdate callback or if other I/O error has occurred.
*/
}
/**
* Records this update in the log file. The update is recorded by
* calling the client's writeUpdate callback. This method must not be
* called until this log's recover method has been invoked
* (and completed).
*
* @param value the object representing the update
* @param forceToDisk ignored; changes are always forced to disk
* @exception IOException If force-write to log failed or an
* exception occurred during the writeUpdate callback or if other
* I/O error occurs while updating the log.
*/
throws IOException
{
// avoid accessing a null log field.
throw new IOException("log is inaccessible, " +
"it may have been corrupted or closed");
}
/*
* If the entry length field spans a sector boundary, write
* the high order bit of the entry length, otherwise write zero for
* the entry length.
*/
/*
* Write update, and sync.
*/
try {
} catch (IOException e) {
throw e;
} catch (Exception e) {
throw (IOException)
}
if (spansBoundary) {
/*
* If length field spans a sector boundary, then
* the next two steps are required (see 4652922):
*
* 1) Write actual length with high order bit set; sync.
* 2) Then clear high order bit of length; sync.
*/
} else {
/*
* Write actual length; sync.
*/
}
logEntries++;
}
/**
* Returns the constructor for the log file if the system property
* sun.rmi.log.class is non-null and the class specified by the
* property a) can be loaded, b) is a subclass of LogFile, and c) has a
* public two-arg constructor (String, String); otherwise returns null.
**/
private static Constructor<? extends LogFile>
new GetPropertyAction("sun.rmi.log.class"));
if (logClassName != null) {
try {
new PrivilegedAction<ClassLoader>() {
public ClassLoader run() {
return ClassLoader.getSystemClassLoader();
}
});
} catch (Exception e) {
e.printStackTrace();
}
}
return null;
}
/**
* Records this value as the current snapshot by invoking the client
* supplied "snapshot" callback and then empties the log.
*
* @param value the object representing the new snapshot
* @exception IOException If an exception occurred during the
* snapshot callback or if other I/O error has occurred during the
* snapshot process
*/
throws IOException
{
int oldVersion = version;
incrVersion();
try {
try {
} catch (IOException e) {
throw e;
} catch (Exception e) {
throw new IOException("snapshot failed", e);
}
} finally {
}
openLogFile(true);
writeVersionFile(true);
}
/**
* Close the stable storage directory in an orderly manner.
*
* @exception IOException If an I/O error occurs when the log is
* closed
*/
try {
} finally {
}
}
/**
* Returns the size of the snapshot file in bytes;
*/
public long snapshotSize() {
return snapshotBytes;
}
/**
* Returns the size of the log file in bytes;
*/
public long logSize() {
return logBytes;
}
/* private methods */
/**
* Write an int value in single write operation. This method
* assumes that the caller is synchronized on the log file.
*
* @param out output stream
* @param val int value
* @throws IOException if any other I/O error occurs
*/
throws IOException
{
}
/**
* Generates a filename prepended with the stable storage directory path.
*
* @param name the leaf name of the file
*/
}
/**
* Generates a version 0 filename prepended with the stable storage
* directory path
*
* @param name version file name
*/
}
/**
* Generates a version filename prepended with the stable storage
* directory path with the version number as a suffix.
*
* @param name version file name
* @thisversion a version number
*/
}
/**
* Increments the directory version number.
*/
private void incrVersion() {
}
/**
* Delete a file.
*
* @param name the name of the file
* @exception IOException If new version file couldn't be removed
*/
if (!f.delete())
}
/**
* Removes the new version number file.
*
* @exception IOException If an I/O error has occurred.
*/
}
/**
* Removes the snapshot file.
*
* @param ver the version to remove
* @exception IOException If an I/O error has occurred.
*/
if (ver == 0) return;
}
/**
* Removes the log file.
*
* @param ver the version to remove
* @exception IOException If an I/O error has occurred.
*/
if (ver == 0) return;
}
/**
* created.
*
* @param truncate if true and file exists, file is truncated to zero
* length
* @exception IOException If an I/O error has occurred.
*/
try {
close();
} catch (IOException e) { /* assume this is okay */
}
try {
} catch (Exception e) {
throw (IOException) new IOException(
"unable to construct LogFile instance").initCause(e);
}
if (truncate) {
}
}
/**
* Creates a new log file, truncated and initialized with the format
* version number preferred by this implementation.
* <p>Environment: inited, synchronized
* <p>Precondition: valid: log, log contains nothing useful
* <p>Postcondition: if successful, log is initialised with the format
* version number (Preferred{Major,Minor}Version), and logBytes is
* set to the resulting size of the updatelog, and logEntries is set to
* zero. Otherwise, log is in an indeterminate state, and logBytes
* is unchanged, and logEntries is unchanged.
*
* @exception IOException If an I/O error has occurred.
*/
private void initializeLogFile()
throws IOException
{
logEntries = 0;
}
/**
* Writes out version number to file.
*
* @param newVersion if true, writes to a new version file
* @exception IOException If an I/O error has occurred.
*/
if (newVersion) {
} else {
name = versionFile;
}
}
}
/**
* Creates the initial version file
*
* @exception IOException If an I/O error has occurred.
*/
version = 0;
writeVersionFile(false);
}
/**
* Commits (atomically) the new version.
*
* @exception IOException If an I/O error has occurred.
*/
writeVersionFile(false);
}
/**
* Reads version number from a file.
*
* @param name the name of the version file
* @return the version
* @exception IOException If an I/O error has occurred.
*/
(new FileInputStream(name))) {
}
}
/**
* Sets the version. If version file does not exist, the initial
* version file is created.
*
* @exception IOException If an I/O error has occurred.
*/
try {
} catch (IOException e) {
try {
}
catch (IOException ex) {
}
try {
}
catch (IOException ex) {
}
}
}
/**
* Applies outstanding updates to the snapshot.
*
* @param state the most recent snapshot
* @exception IOException If serious log corruption is detected or
* if an exception occurred during a readUpdate callback or if
* other I/O error has occurred.
* @return the resulting state of the object after all updates
*/
throws IOException
{
logBytes = 0;
logEntries = 0;
if (Debug)
try {
} catch (EOFException e) {
* fsck or equivalent). This is not an error.
*/
openLogFile(true); // create and truncate
}
/* A new major version number is a catastrophe (it means
* that the file format is incompatible with older
* clients, and we'll only be breaking things by trying to
* use the log). A new minor version is no big deal for
* upward compatibility.
*/
if (majorFormatVersion != PreferredMajorVersion) {
if (Debug) {
}
"version " + majorFormatVersion +
"." + minorFormatVersion +
" format, and this implementation " +
" understands only version " +
PreferredMajorVersion + "." +
}
try {
int updateLen = 0;
try {
} catch (EOFException e) {
if (Debug)
break;
}
if (Debug) {
"log.debug: last update incomplete, " +
"updateLen = 0x" +
}
break;
}
// this is a fragile use of available() which relies on the
// twin facts that BufferedInputStream correctly consults
// the underlying stream, and that FileInputStream returns
// the number of bytes remaining in the file (via FIONREAD).
/* corrupted record at end of log (can happen since we
* do only one fsync)
*/
if (Debug)
break;
}
if (Debug)
try {
state);
} catch (IOException e) {
throw e;
} catch (Exception e) {
e.printStackTrace();
throw new IOException("read update failed with " +
"exception: " + e);
}
logEntries++;
} /* while */
} finally {
}
if (Debug)
/* reopen log file at end */
openLogFile(false);
// avoid accessing a null log field
throw new IOException("rmid's log is inaccessible, " +
"it may have been corrupted or closed");
}
return state;
}
/**
* ReliableLog's log file implementation. This implementation
* is subclassable for testing purposes.
*/
/**
* Constructs a LogFile and initializes the file descriptor.
**/
throws FileNotFoundException, IOException
{
}
/**
* Invokes sync on the file descriptor for this log file.
*/
}
/**
* Returns true if writing 4 bytes starting at the specified file
* position, would span a 512 byte sector boundary; otherwise returns
* false.
**/
}
}
}