/**
* Copyright (c) 2007, Markus Jevring <markus@jevring.net>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. 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.
* 3. The names of the contributors may not be used to endorse or promote
* products derived from this software without specific prior written
* permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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.
*
*/
package cu.ftpd.logging;
import cu.ftpd.filesystem.metadata.MetadataHandler;
import cu.ftpd.modules.dirlog.Dirlog;
import cu.ftpd.modules.dupecheck.Dupelog;
import java.io.IOException;
import java.io.File;
import java.util.Deque;
import java.util.LinkedList;
/**
* This class will traverse a directory structure and populate a dirlog and dupelog with it's content.
* @author Markus Jevring <markus@jevring.net>
* @since 2008-jan-31 - 15:11:18
* @version $Id: LogCreator.java 307 2011-02-27 19:10:52Z jevring $
*/
public class LogCreator {
// NOTE: if we start adding from the leaf, then we can propagate the size up recursively to when we add in the dirlog
private Dirlog dirlog;
private Dupelog dupelog;
private String rcp;
public long totalSize;
public long totalDirs;
public long totalFiles;
private MetadataHandler mh = new MetadataHandler();
public LogCreator(String dirlogPath, String dupelogPath, boolean append) throws IOException {
dirlog = new Dirlog(dirlogPath);
dupelog = new Dupelog(dupelogPath, Integer.MAX_VALUE, false, "*");
if (append) {
dirlog.load();
dupelog.load();
}
}
private String resolve(File file) {
// we created our own resolve so we don't have to initialize the filesystem
String s;
try {
s = file.getCanonicalPath();
s = s.substring(rcp.length());
String t;
if (s.length() == 0) {
t = "/";
} else {
t = s;
}
if (File.separatorChar != '/') { // this will happen on windows and older versions of mac
t = t.replace(File.separatorChar, '/');
}
return t;
} catch (IOException e) {
// There is a pretty good reason that something is terribly wrong if we end up here
System.err.println("We were unable to get the canonical path for " + file.getAbsolutePath());
// NOTE: returning null here is fine, it is in the specified behavior.
return null;
}
}
public void start(File root) {
try {
rcp = root.getCanonicalPath();
addToLog(root);
dirlog.shutdown();
dupelog.shutdown();
} catch (IOException e) {
e.printStackTrace();
}
}
private void addToLog(File entity) {
final Deque<File> queue = new LinkedList<File>();
queue.push(entity);
// since we don't care about consecutive dirlog accesses, all we want to do is minimize the size of the queue at any time
// removeFirst keeps the size to its smallest
int max = 0;
while (!queue.isEmpty()) {
// max = Math.max(queue.size(), max);
// System.out.println("size: " + queue.size());
File file = queue.removeFirst();
String username = "cuftpd";
/*
File parent = file.getParentFile();
File metadata = new File(parent, ".metadata");
if (metadata.exists()) {
Directory d = mh.getDirectory(file.getParentFile());
if (d != null) {
Metadata m = d.getMetadata(file.getName());
if (m != null) {
username = m.getUsername();
}
}
}
*/
if (file.isFile()) {
// we want to have as few objects in memory at a time as possible, so prioritize leaves rather than branches
// _todo: we don't want to write it to disk except at the end, preferably.
// to do this we could write a memory-only dirlog subclass that overrides save(), and puts the real save in something else.
// Ahh, but it doesn't save, that's done by the timer, awesome!
if (!file.getName().equals(".metadata") && !file.getName().equals(".raceinfo")) {
dirlog.update(resolve(file.getParentFile()), file.length(), username);
dupelog.addDupe(username, file.getName());
totalFiles++;
totalSize += file.length();
}
} else {
totalDirs++;
// _todo: ideally we don't want this to include the COMPLETE* dirs, but there's little we can do, since we do't really want to provide a skiplist
dirlog.create(resolve(file), username);
File[] files = file.listFiles();
if (files != null) {
for (File f : files) {
queue.push(f);
}
} else {
System.out.println("Could not index: " + file.getAbsolutePath() + ", skipping...");
}
}
}
// System.out.println("max: " + max);
}
public static void main(String[] args) {
/*
for (int i = 0; i < args.length; i++) {
String arg = args[i];
System.out.println(i + " = " + arg);
}
*/
if (args.length == 4) {
LogCreator lc;
try {
long start = System.currentTimeMillis();
lc = new LogCreator(args[0], args[1], Boolean.parseBoolean(args[2]));
File root = new File(args[3]);
if (root.exists() && root.isDirectory()) {
lc.start(new File(args[3]));
double timeTaken = ((double)(System.currentTimeMillis() - start))/ 1000.0d;
System.out.println("Complete!");
System.out.println("Directories: " + lc.totalDirs);
System.out.println("Files: " + lc.totalFiles);
System.out.println("Size: " + Formatter.size(lc.totalSize));
System.out.println("Time: " + timeTaken + " seconds");
} else {
System.err.println(root.getAbsolutePath() + " either does not exist or is not a directory.");
}
} catch (IOException e) {
e.printStackTrace();
}
} else {
usage();
}
}
private static void usage() {
System.out.println("Usage: java -jar logcreator.jar dirlogpath dupelogpath append root");
System.out.println();
System.out.println("where append is a boolean, and thus the only accepted values are: true or false");
System.out.println("and root indicates the directory that we want to index, which will also serve as");
System.out.println("the root of the ftpd, as far as the dirlog is concerned.");
System.out.println("This would typically be /cuftpd/site");
System.out.println("set append to true if you want the dupelog and dirlog to be appended, rather than overwritten");
System.out.println("any argument with spaces in them need to be enclosed in quotation marks");
}
}