/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.sun.jini.outrigger.snaplogstore; import com.sun.jini.outrigger.OutriggerServerImpl; import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.Collection; import java.util.Iterator; import java.util.TreeMap; import java.util.logging.Level; import java.util.logging.Logger; /** * The base class for the logging file classes. This class provides * the common functionality, but you should not instantiate it. * <p> * (Note -- I use <code>protected</code> here as an advisory notice. * Clearly, since this is package code, all classes in the package have * access, but fields marked <code>protected</code> are expected to be * used only by subclasses. Use good taste.) * * @author Sun Microsystems, Inc. * * @see LogOutputFile * @see LogInputFile */ class LogFile { /** * The directory in which the log files live. */ protected File baseDir; /** * The base part of the file name (e.g., <code>"log."</code> for * <code>"log.0"</code>, <code>"log.1"</code>, ...) */ protected String baseFile; /** * The type of log stream */ static final String LOG_TYPE = "LogStore"; /** * The version of the log stream (the highest one known). */ protected static final int LOG_VERSION = 3; /** A log entry that records a boot. */ protected static final byte BOOT_OP = 1; /** A log entry that records the join state. */ protected static final byte JOINSTATE_OP = 11; /** A log entry that records a <code>write</code>. */ protected static final byte WRITE_OP = 2; /** A log entry that records a <code>take</code>. */ protected static final byte TAKE_OP = 3; /** A log entry that records a <code>notify</code>. */ protected static final byte REGISTER_OP = 4; /** A log entry that records a <code>notify</code>. */ protected static final byte RENEW_OP = 5; /** A log entry that records a notification and new sequence number. */ protected static final byte NOTIFIED_OP = 6; /** A log entry that records a <code>cancel</code>. */ protected static final byte CANCEL_OP = 7; /** A log entry that records a transaction <code>prepare</code>. */ protected static final byte PREPARE_OP = 8; /** A log entry that records a transaction <code>commit</code>. */ protected static final byte COMMIT_OP = 9; /** A log entry that records a transaction <code>abort</code>. */ protected static final byte ABORT_OP = 10; /** A log entry that records the service's <code>Uuid</code>. */ protected static final byte UUID_OP = 12; /** A log entry that records a batch <code>write</code>. */ protected static final byte BATCH_WRITE_OP = 13; /** A log entry that records a batch <code>take</code>. */ protected static final byte BATCH_TAKE_OP = 14; /** Logger for logging persistent store related information */ private static final Logger logger = Logger.getLogger(OutriggerServerImpl.storeLoggerName); /** * Create a log file with the given base directory, base file name * within the directory. This is intended only for * when you are sure of the exact values -- no modifications are * made to the values. */ protected LogFile(File baseDir, String baseFile) { this.baseDir = baseDir; this.baseFile = baseFile; } /** * Create a log file from the given template. If * <code>basePath</code> has a directory component, it is used as * the base directory. Otherwise the base directory is * <code>"."</code>. If <code>basePath</code> names a directory, * the base name will be <code>""</code>. Otherwise the file * component is used as the base, with a "." added at the end if it * is not already present. */ protected LogFile(String basePath) throws IOException { baseDir = new File(basePath); if (baseDir.isDirectory()) { baseFile = ""; } else { baseFile = baseDir.getName(); String pname = baseDir.getParent(); if (pname == null) pname = "."; baseDir = new File(pname); if (baseFile.charAt(baseFile.length() - 1) != '.') baseFile += "."; } } /** * Fill in a list of existing matching log files, oldest to newest, * returning the highest number used as a suffix, or -1 if * no files were found. If two files have the same time, they are * sorted by the numeric value of the suffix. */ int existingLogs(Collection files) { if (logger.isLoggable(Level.FINE)) { logger.log(Level.FINE, "scanning {0} for {1} baseFile", new Object[]{baseDir, baseFile}); } String[] inDir = baseDir.list(); // directory contents TreeMap found = new TreeMap(); // the files we've found int highest = -1; // largest # suffix seen // no directory or files (can happen on destroy) if (inDir == null) return highest; fileLoop: for (int f = 0; f < inDir.length; f++) { String name = inDir[f]; logger.log(Level.FINE, "checking {0}", name); if (!name.startsWith(baseFile)) // is it one of ours? continue; // ensure that there is a numerical suffix int num; try { num = Integer.parseInt(name.substring(baseFile.length())); if (num > highest) // keep track of highest highest = num; } catch (NumberFormatException e) { continue fileLoop; // can't be one of ours } found.put(new Integer(num), new File(baseDir, name)); } files.addAll(found.values()); if (logger.isLoggable(Level.FINE)) { logger.log(Level.FINE, "returning {0} files", new Integer(files.size())); Iterator it = files.iterator(); while (it.hasNext()) logger.log(Level.FINE, it.next().toString()); } return highest; } /** * Destroy all log files associated with this stream. */ void destroy() { if (logger.isLoggable(Level.FINE)) logger.log(Level.FINE, "destroy"); ArrayList files = new ArrayList(); existingLogs(files); for (int i = 0; i < files.size(); i++) { File log = (File) files.get(i); try { if (!log.delete()) { logger.log(Level.INFO, "Could not delete {0}", log); } } catch (SecurityException e) { if (!log.delete()) { logger.log(Level.INFO, "SecurityException : Could not delete " + log, e); } } } } }