View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership. The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License. You may obtain a copy of the License at
9    * 
10   *      http://www.apache.org/licenses/LICENSE-2.0
11   * 
12   * Unless required by applicable law or agreed to in writing, software
13   * distributed under the License is distributed on an "AS IS" BASIS,
14   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15   * See the License for the specific language governing permissions and
16   * limitations under the License.
17   */
18  package org.apache.river.outrigger.snaplogstore;
19  
20  import org.apache.river.outrigger.OutriggerServerImpl;
21  
22  import java.io.File;
23  import java.io.IOException;
24  import java.util.ArrayList;
25  import java.util.Collection;
26  import java.util.Iterator;
27  import java.util.TreeMap;
28  import java.util.logging.Level;
29  import java.util.logging.Logger;
30  
31  /**
32   * The base class for the logging file classes.  This class provides
33   * the common functionality, but you should not instantiate it.
34   * <p>
35   * (Note -- I use <code>protected</code> here as an advisory notice.
36   * Clearly, since this is package code, all classes in the package have
37   * access, but fields marked <code>protected</code> are expected to be
38   * used only by subclasses.  Use good taste.)
39   *
40   * @author Sun Microsystems, Inc.
41   *
42   * @see LogOutputFile
43   * @see LogInputFile
44   */
45  class LogFile {
46      /**
47       * The directory in which the log files live.
48       */
49      protected volatile File baseDir;
50  
51      /**
52       * The base part of the file name (e.g., <code>"log."</code> for
53       * <code>"log.0"</code>, <code>"log.1"</code>, ...)
54       */
55      protected volatile String baseFile;
56  
57      /**
58       * The type of log stream
59       */
60      static final String LOG_TYPE = "LogStore";
61  
62      /**
63       * The version of the log stream (the highest one known).
64       */
65      protected static final int LOG_VERSION = 3;
66  
67      /** A log entry that records a boot. */
68      protected static final byte BOOT_OP		= 1;
69      /** A log entry that records the join state. */
70      protected static final byte JOINSTATE_OP	= 11;
71      /** A log entry that records a <code>write</code>. */
72      protected static final byte WRITE_OP	= 2;
73      /** A log entry that records a <code>take</code>. */
74      protected static final byte TAKE_OP		= 3;
75      /** A log entry that records a <code>notify</code>. */
76      protected static final byte REGISTER_OP	= 4;
77      /** A log entry that records a <code>notify</code>. */
78      protected static final byte RENEW_OP	= 5;
79      /** A log entry that records a notification and new sequence number. */
80      protected static final byte NOTIFIED_OP	= 6;
81      /** A log entry that records a <code>cancel</code>. */
82      protected static final byte CANCEL_OP	= 7;
83      /** A log entry that records a transaction <code>prepare</code>. */
84      protected static final byte PREPARE_OP	= 8;
85      /** A log entry that records a transaction <code>commit</code>. */
86      protected static final byte COMMIT_OP	= 9;
87      /** A log entry that records a transaction <code>abort</code>. */
88      protected static final byte ABORT_OP	= 10;
89      /** A log entry that records the service's <code>Uuid</code>. */
90      protected static final byte UUID_OP 	= 12;
91      /** A log entry that records a batch <code>write</code>. */
92      protected static final byte BATCH_WRITE_OP 	= 13;
93      /** A log entry that records a batch <code>take</code>. */
94      protected static final byte BATCH_TAKE_OP 	= 14;
95  
96      /** Logger for logging persistent store related information */
97      private static final Logger logger = 
98  	Logger.getLogger(OutriggerServerImpl.storeLoggerName);
99  
100     /**
101      * Create a log file with the given base directory, base file name
102      * within the directory.  This is intended only for
103      * when you are sure of the exact values -- no modifications are
104      * made to the values.
105      */
106     protected LogFile(File baseDir, String baseFile) {
107 	this.baseDir = baseDir;
108 	this.baseFile = baseFile;
109     }
110 
111     /**
112      * Create a log file from the given template.  If
113      * <code>basePath</code> has a directory component, it is used as
114      * the base directory.  Otherwise the base directory is
115      * <code>"."</code>.  If <code>basePath</code> names a directory,
116      * the base name will be <code>""</code>.  Otherwise the file
117      * component is used as the base, with a "." added at the end if it
118      * is not already present.
119      */
120     protected LogFile(String basePath) throws IOException {
121 	baseDir = new File(basePath);
122 	if (baseDir.isDirectory()) {
123 	    baseFile = "";
124 	} else {
125 	    baseFile = baseDir.getName();
126 	    String pname = baseDir.getParent();
127 	    if (pname == null)
128 		pname = ".";
129 	    baseDir = new File(pname);
130 	    if (baseFile.charAt(baseFile.length() - 1) != '.')
131 		baseFile += ".";
132 	}
133     }
134 
135     /**
136      * Fill in a list of existing matching log files, oldest to newest,
137      * returning the highest number used as a suffix, or -1 if
138      * no files were found.  If two files have the same time, they are
139      * sorted by the numeric value of the suffix.
140      */
141     int existingLogs(Collection files) {
142 	if (logger.isLoggable(Level.FINE)) {
143 	    logger.log(Level.FINE, "scanning {0} for {1} baseFile",
144 		       new Object[]{baseDir, baseFile});
145 	}
146 
147 	String[] inDir = baseDir.list();	// directory contents
148 	TreeMap found = new TreeMap();		// the files we've found
149 	int highest = -1;			// largest # suffix seen
150 
151 	// no directory or files (can happen on destroy)
152 	if (inDir == null)
153 	    return highest;
154 
155     fileLoop:
156 	for (int f = 0; f < inDir.length; f++) {
157 
158 	    String name = inDir[f];
159 
160 	    logger.log(Level.FINE, "checking {0}", name);
161 
162 	    if (!name.startsWith(baseFile))		// is it one of ours?
163 		continue;
164 
165 	    // ensure that there is a numerical suffix
166 	    int num;
167 	    try {
168 		num = Integer.parseInt(name.substring(baseFile.length()));
169 		if (num > highest)		       // keep track of highest
170 		    highest = num;
171 	    } catch (NumberFormatException e) {
172 		continue fileLoop;		       // can't be one of ours
173 	    }
174 
175 	    found.put(Integer.valueOf(num), new File(baseDir, name));
176 	}
177 
178 	files.addAll(found.values());
179 	if (logger.isLoggable(Level.FINE)) {
180 	    logger.log(Level.FINE, "returning {0} files",
181 		       Integer.valueOf(files.size()));
182 	    Iterator it = files.iterator();
183 	    while (it.hasNext())
184 		logger.log(Level.FINE, it.next().toString());
185 	}
186 
187 	return highest;
188     }
189 
190     /**
191      * Destroy all log files associated with this stream.
192      */
193     void destroy() {
194 	if (logger.isLoggable(Level.FINE))
195 	    logger.log(Level.FINE, "destroy");
196 
197 	ArrayList files = new ArrayList();
198 	existingLogs(files);
199 	for (int i = 0; i < files.size(); i++) {
200 	    File log = (File) files.get(i);
201 	    try {
202 		if (!log.delete()) {
203 		    logger.log(Level.INFO, "Could not delete {0}", log);
204 		}
205 	    } catch (SecurityException e) {
206 		if (!log.delete()) {
207 		    logger.log(Level.INFO,
208 			       "SecurityException : Could not delete " + log,
209 			       e);
210 		}
211 	    }
212 	}
213     }
214 }