// **********************************************************************
//
// <copyright>
//
// BBN Technologies
// 10 Moulton Street
// Cambridge, MA 02138
// (617) 873-8000
//
// Copyright (C) BBNT Solutions LLC. All rights reserved.
//
// </copyright>
// **********************************************************************
//
// $Source: /cvs/distapps/openmap/src/openmap/com/bbn/openmap/util/wanderer/OneWaySync.java,v $
// $RCSfile: OneWaySync.java,v $
// $Revision: 1.3 $
// $Date: 2005/08/09 18:41:09 $
// $Author: dietrick $
//
// **********************************************************************
package com.bbn.openmap.util.wanderer;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.logging.Level;
import java.util.logging.Logger;
import com.bbn.openmap.util.ArgParser;
/**
* The OneWaySync is a class that copies files from one directory to another,
* skipping specified extensions or only copying files and directories with
* specified extensions. It's used by the OpenMap team to keep the internal CVS
* tree in sync with the external one. The main() function has the avoid/limit
* suffixes hard-coded, you can extend or change the settings in a different
* class.
*/
public class OneWaySync extends Wanderer implements WandererCallback {
/** The source directory. */
protected File src;
/** The target directory. */
protected File tgt;
/** The suffixes to skip over for directories. */
public String[] dirSuffixAvoids = null;
/** The suffixes to skip over for files. */
public String[] fileSuffixAvoids = null;
/** The suffixes to limit copying to for directories. */
public String[] dirSuffixLimits = null;
/** The suffixes to limit copying to for files. */
public String[] fileSuffixLimits = null;
/** The list of stuff skipped over. */
protected LinkedList<File> notCopiedList = new LinkedList<File>();
/** Flag for printing out activities. */
protected boolean verbose = false;
/** Flag for not doing the changes, just saying what would happen. */
protected boolean fakeit = false;
/** Flag to not have files that exist overwritten. */
protected boolean overwrite = true;
public OneWaySync(String srcDirName, String targetDirName) {
super();
setCallback(this);
src = new File(srcDirName);
tgt = new File(targetDirName);
}
/**
* Check to see if a source directory name should be skipped, based on the
* avoid and limit list.
*/
protected boolean checkToSkipDirectory(String name) {
if (dirSuffixAvoids != null) {
for (int i = 0; i < dirSuffixAvoids.length; i++) {
if (name.endsWith(dirSuffixAvoids[i])) {
// Was on avoid list, skip it.
return true;
}
}
}
if (dirSuffixLimits != null) {
for (int i = 0; i < dirSuffixLimits.length; i++) {
if (name.endsWith(dirSuffixLimits[i])) {
return false;
}
}
// Wasn't on limit list, skip it.
return true;
}
return false;
}
/**
* Check to see if a source file name should be skipped, based on the avoid
* and limit list.
*/
protected boolean checkToSkipFile(String name) {
if (fileSuffixAvoids != null) {
for (int i = 0; i < fileSuffixAvoids.length; i++) {
if (name.endsWith(fileSuffixAvoids[i])) {
// Was on avoid list, skip it.
return true;
}
}
}
if (fileSuffixLimits != null) {
for (int i = 0; i < fileSuffixLimits.length; i++) {
if (name.endsWith(fileSuffixLimits[i])) {
return false;
}
}
// Wasn't on limit list, skip it.
return true;
}
return false;
}
/**
* Wanderer method handing directories.
*/
public boolean handleDirectory(File directory, String[] contentNames) {
String newDirName = getRelativePathFromSource(directory);
if (newDirName == null) {
if (directory != src) {
notCopiedList.add(directory);
}
super.handleDirectory(directory, contentNames);
return true;
}
if (!checkToSkipDirectory(newDirName)) {
File newDir = getTargetFile(newDirName);
if (!newDir.exists()) {
if (verbose) {
getLogger().info("Creating " + newDir);
}
if (!fakeit && overwrite)
newDir.mkdir();
}
super.handleDirectory(directory, contentNames);
} else {
notCopiedList.add(directory);
}
return true;
}
/**
* WandererCallback method handing directories, not used.
*/
public boolean handleDirectory(File file) {
return true;
}
/**
* WandererCallback method handing files, check and copy those that fit the
* avoid and limit parameters.
*/
public boolean handleFile(File file) {
String newFileName = getRelativePathFromSource(file);
if (!checkToSkipFile(newFileName)) {
File newFile = getTargetFile(newFileName);
if (verbose) {
getLogger().info("Copying " + file + " to " + newFile);
}
if (!fakeit && overwrite)
copy(file, newFile);
} else {
notCopiedList.add(file);
}
return true;
}
/**
* Copy files.
*/
public void copy(File fromFile, File toFile) {
try {
FileInputStream fis = new FileInputStream(fromFile);
FileOutputStream fos = new FileOutputStream(toFile);
int num = 0;
byte[] stuff = new byte[4096];
while ((num = fis.read(stuff)) > 0) {
fos.write(stuff, 0, num);
}
fis.close();
fos.close();
} catch (IOException ioe) {
getLogger().warning("Exception reading from " + fromFile + " and writing to " + toFile);
}
}
/**
* Strip the source directory part of the path from the file, return what
* remains.
*/
public String getRelativePathFromSource(File file) {
return subtractPathFromDirectory(src, file);
}
/**
* Strip the target directory part of the path from the file, return what
* remains.
*/
public String getRelativePathFromTarget(File file) {
return subtractPathFromDirectory(tgt, file);
}
/**
* Tack the file path onto the source directory.
*/
public File getSourceFile(String relativePath) {
return new File(src, relativePath);
}
/**
* Tack the file path onto the target directory.
*/
public File getTargetFile(String relativePath) {
return new File(tgt, relativePath);
}
/**
* Print out the files/directories not copied.
*/
public void writeUnsynched() {
for (Iterator<File> it = notCopiedList.iterator(); it.hasNext();) {
getLogger().info(" " + it.next());
}
}
/**
* Create a BackCheck object that looks to see what files are in the target
* but not in the source.
*/
public void checkTargetSolos() {
new BackCheck(tgt.getPath(), src.getPath());
}
/**
* Take the source directory out of the path to the directory.
*/
protected String subtractPathFromDirectory(File dir, File file) {
String name = file.getPath();
String dirName = dir.getPath();
if (name.equals(dirName)) {
if (verbose) {
getLogger().info("OneWaySync avoiding subtraction operation on top-level directory");
}
return null;
}
int index = name.indexOf(dirName);
if (index != -1) {
try {
String relative = name.substring(index + dirName.length() + 1);
if (verbose) {
getLogger().info("From " + file + ", returning " + relative);
}
return relative;
} catch (StringIndexOutOfBoundsException sioobe) {
getLogger().warning("Problem clipping first " + (dirName.length() + 1) + " characters off " + file);
return null;
}
} else {
getLogger().warning("File " + file + " is not in directory " + dir);
return null;
}
}
/**
* Start copying files from the source directory to the target directory.
*/
public void start() {
String errorMessage = null;
if (src == null) {
errorMessage = "OneWaySync: Source directory unspecified";
} else if (!src.exists()) {
errorMessage = "OneWaySync: Source directory (" + src + ") doesn't exist!";
}
if (tgt != null) {
if (!tgt.exists()) {
if (verbose) {
getLogger().info("OneWaySync: target directory (" + tgt + ") doesn't exist, creating...");
}
try {
if (!fakeit && !tgt.mkdir()) {
errorMessage = "OneWaySync: target directory (" + tgt + ") can't be created.";
}
} catch (SecurityException se) {
errorMessage = "OneWaySync: creating target directory (" + tgt
+ ") isn't allowed, Security Exception: " + se.getMessage();
se.printStackTrace();
}
}
} else {
errorMessage = "OneWaySync: target directory unspecified";
}
if (errorMessage != null) {
getLogger().warning(errorMessage);
System.exit(0);
}
handleEntry(src);
}
public void setVerbose(boolean val) {
verbose = val;
}
public boolean getVerbose() {
return verbose;
}
public void setFakeit(boolean val) {
fakeit = val;
}
public boolean getFakeit() {
return fakeit;
}
public void setDirSuffixAvoids(String[] avoids) {
dirSuffixAvoids = avoids;
}
public void setFileSuffixAvoids(String[] avoids) {
fileSuffixAvoids = avoids;
}
public void setDirSuffixLimits(String[] limits) {
dirSuffixLimits = limits;
}
public void setFileSuffixLimits(String[] limits) {
fileSuffixLimits = limits;
}
// <editor-fold defaultstate="collapsed" desc="Logger Code">
/**
* Holder for this class's Logger. This allows for lazy initialization of
* the logger.
*/
private static final class LoggerHolder {
/**
* The logger for this class
*/
private static final Logger LOGGER = Logger.getLogger(OneWaySync.class.getName());
/**
* Prevent instantiation
*/
private LoggerHolder() {
throw new AssertionError("The LoggerHolder should never be instantiated");
}
}
/**
* Get the logger for this class.
*
* @return logger for this class
*/
private static Logger getLogger() {
return LoggerHolder.LOGGER;
}
// </editor-fold>
/**
*/
public static void main(String[] argv) {
ArgParser ap = new ArgParser("OneWaySync");
ap.add("source", "The source directory to copy files and directories from.", 1);
ap.add("target", "The target directory to receive the updated files and directories.", 1);
ap.add("verbose", "Announce all changes, failures will still be reported.");
ap.add("fakeit", "Just print what would happen, don't really do anything.");
ap.add("report", "Print out what didn't get copied, and what files exist only on the target side.");
if (argv.length < 4) {
ap.bail("", true);
}
ap.parse(argv);
boolean verbose = false;
String[] verb = ap.getArgValues("verbose");
if (verb != null) {
verbose = true;
}
boolean fakeit = false;
verb = ap.getArgValues("fakeit");
if (verb != null) {
verbose = true;
fakeit = true;
}
boolean report = false;
verb = ap.getArgValues("report");
if (verb != null) {
report = true;
}
String[] sourceDir;
sourceDir = ap.getArgValues("source");
if (sourceDir != null && sourceDir.length >= 1) {
if (verbose) {
getLogger().info("Source directory is " + sourceDir[0]);
}
} else {
ap.bail("OneWaySync needs path to source directory", false);
}
String[] targetDir;
targetDir = ap.getArgValues("target");
if (targetDir != null && targetDir.length >= 1) {
if (verbose) {
getLogger().info("Target directory is " + targetDir[0]);
}
} else {
ap.bail("OneWaySync needs path to source directory", false);
}
// Should be 'since' instead of 'if'
if (sourceDir != null && targetDir != null) {
OneWaySync cc = new OneWaySync(sourceDir[0], targetDir[0]);
cc.setVerbose(verbose);
cc.setFakeit(fakeit);
cc.setDirSuffixAvoids(new String[] { "CVS" });
cc.setFileSuffixLimits(new String[] { ".java", "Makefile", ".cvsignore", ".html", ".properties", ".txt",
".c", ".h", ".png" });
cc.start();
if (report) {
getLogger().info("-------- Not Copied --------");
cc.writeUnsynched();
getLogger().info("----------------------------");
cc.checkTargetSolos();
}
}
}
public static class BackCheck extends OneWaySync {
public BackCheck(String targetDirName, String srcDirName) {
super(targetDirName, srcDirName);
fakeit = true;
overwrite = false;
if (getLogger().isLoggable(Level.FINE)) {
verbose = true;
}
start();
getLogger().info("-------- Only In Target Directory--------");
writeUnsynched();
getLogger().info("-----------------------------------------");
}
public boolean handleDirectory(File directory, String[] contentNames) {
String newDirName = getRelativePathFromSource(directory);
if (newDirName == null) {
return super.handleDirectory(directory, contentNames);
}
File newDir = getTargetFile(newDirName);
if (!newDir.exists()) {
notCopiedList.add(directory);
}
return super.handleDirectory(directory, contentNames);
}
public boolean handleFile(File file) {
if (!getTargetFile(getRelativePathFromSource(file)).exists()) {
notCopiedList.add(file);
}
return true;
}
}
}