/*
* ALMA - Atacama Large Millimiter Array
* (c) European Southern Observatory, 2005
* Copyright by ESO (in the framework of the ALMA collaboration),
* All rights reserved
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*/
package alma.acs.config.validators;
import java.io.File;
import java.io.FileFilter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
import com.cosylab.util.FileHelper;
import alma.acs.testsupport.TestLogger;
import alma.acs.util.CmdLineArgs;
import alma.acs.util.CmdLineRegisteredOption;
/**
* Tool that scans modules (or all of ALMA software) for files that may be configuration files
* and therefore should be considered for moving to a different location.
* <p>
* All files in and below the directory given by the option <code>-baseDir</code> are scanned.
* Those files with certain endings are suspected of being configuration files, but then some of these files get cleared
* if they are recognized by one of the filters which check all suspicious files.
* For example, a filter specialized in xml files will recognize those files used for ACS error definitions.
* All files that are still suspect after filtering will be reported, either by printing their pathname to stdout,
* or by copying them to a target directory if the <code>-targetDir</code> option is given.
*
* @author hsommer
*/
public class ConfigFileFinder {
private Logger logger;
/** directory under which we look for config files */
private File baseDir;
/** optional target dir to which suspected config files are copied */
private File targetDir;
/** endings of files to consider, e.g. {"xml", "properties"} */
private Set<String> fileEndings = new HashSet<String>();
/** should files be copied to targetDir flat, i.e. w/o subdir structure? */
private boolean targetFilesFlat;
/** classes that can recognize specific suspect files and state that they are actually not config files */
private List<ConfigFileRedeemer> redeemers = new ArrayList<ConfigFileRedeemer>();
/**
* @throws Exception
*/
public ConfigFileFinder() throws Exception {
targetFilesFlat = false;
logger = TestLogger.getLogger(getClass().getName());
// hardwired file endings that don't need to be supplied by any filters
addFileEndings(new String[] {".properties", ".config"});
}
public void configureRedeemers() throws Exception {
addRedeemer(new ConfigFileRedeemerFilepath(logger, baseDir));
addRedeemer(new ConfigFileRedeemerTestDir(logger));
addRedeemer(new ConfigFileRedeemerXml(logger));
// todo: perhaps allow user-supplied redeemers to be added, based on a command line parameter (classname)
}
public void addRedeemer(ConfigFileRedeemer redeemer) {
redeemers.add(redeemer);
addFileEndings(redeemer.getFileEndings());
}
public void configureFromArgs(String[] args) {
CmdLineArgs cmdArgs = new CmdLineArgs();
CmdLineRegisteredOption optBaseDir = new CmdLineRegisteredOption("-baseDir", 1);
cmdArgs.registerOption(optBaseDir);
CmdLineRegisteredOption optTargetDir = new CmdLineRegisteredOption("-targetDir", 1);
cmdArgs.registerOption(optTargetDir);
CmdLineRegisteredOption optTargetFilesFlat = new CmdLineRegisteredOption("-targetFilesFlat", 0);
cmdArgs.registerOption(optTargetFilesFlat);
CmdLineRegisteredOption optFileEndings = new CmdLineRegisteredOption("-fileEndings", 1);
cmdArgs.registerOption(optFileEndings);
cmdArgs.parseArgs(args);
if (cmdArgs.isSpecified(optBaseDir)) {
String baseDirName = cmdArgs.getValues(optBaseDir)[0].trim();
setBaseDir(new File(baseDirName));
}
if (cmdArgs.isSpecified(optTargetDir)) {
String targetDirName = cmdArgs.getValues(optTargetDir)[0].trim();
setTargetDir(new File(targetDirName));
}
targetFilesFlat = cmdArgs.isSpecified(optTargetFilesFlat);
if (cmdArgs.isSpecified(optFileEndings)) {
addFileEndings(cmdArgs.getValues(optFileEndings));
}
}
public void checkConfiguration() throws IllegalStateException {
String err = "";
String warn = "";
if (baseDir == null || !baseDir.exists()) {
err += "baseDir '" + baseDir + "' does not exist. ";
}
if (targetDir == null) {
warn += "targetDir not specified. No files will be copied. ";
}
if (fileEndings.isEmpty()) {
// should never happen thanks to default endings
err += "no files can be selected. Specify option \"-fileEndings\". ";
}
if (err.length() > 0) {
throw new IllegalStateException("Bad configuration: " + err);
}
if (warn.length() > 0) {
System.err.println("Configuration warning: " + warn);
}
}
/**
* Gets all Files under {@link #baseDir} whose name ends with any of the Strings given in {@link #fileEndings},
* and runs them through the filter chain to suppress files of known and safe content.
* @return
*/
private void run() {
String msg = "Tool " + getClass().getSimpleName() + " will search for configuration files in '" + baseDir +
"' and below, suspecting all files ending with ";
for (Iterator<String> iter = fileEndings.iterator(); iter.hasNext();) {
msg += "'" + iter.next() + "'";
if (iter.hasNext()) {
msg += ", ";
}
}
msg += " and will then prune those files recognized by any of the filters ";
for (Iterator<ConfigFileRedeemer> iter = redeemers.iterator(); iter.hasNext();) {
msg += iter.next().getName();
if (iter.hasNext()) {
msg += ", ";
}
}
msg += ". ";
if (targetDir != null) {
msg += "The remaining potential config files are copied to '" + targetDir + "' with their original directory structure ";
if (targetFilesFlat) {
msg += "flattened.";
}
else {
msg += "maintained.";
}
}
logger.info(msg);
FileFilter fileFilter = new FileFilter() {
public boolean accept(File pathname) {
if (pathname.isDirectory()) {
return true;
}
String name = pathname.getName();
for (Iterator iter = fileEndings.iterator(); iter.hasNext();) {
String fnEnding = (String) iter.next();
if (name.endsWith(fnEnding)) {
return true;
}
}
return false;
}
};
runRecursive(baseDir, fileFilter);
}
private void runRecursive(File currentDir, FileFilter fileFilter) {
if (currentDir == null || !currentDir.exists() || !currentDir.isDirectory() ) {
return;
}
if (!currentDir.canRead()) {
logger.warning("failed to read from directory " + currentDir.getAbsolutePath());
return;
}
// get files and subdirs from the current directory
File[] allFiles = currentDir.listFiles(fileFilter);
for (int i = 0; i < allFiles.length; i++) {
if (allFiles[i].isFile()) {
// check if the current file is known to be not a config file
boolean redeemed = false;
if (allFiles[i].canRead()) {
for (Iterator<ConfigFileRedeemer> iter = redeemers.iterator(); iter.hasNext();) {
ConfigFileRedeemer filter = iter.next();
if (filter.isNotAConfigFile(allFiles[i])) {
redeemed = true;
break;
}
}
}
else {
logger.severe("Failed to read file " + allFiles[i]);
}
if (!redeemed) {
handleConfigFile(allFiles[i]);
}
}
else if (allFiles[i].isDirectory()) {
runRecursive(allFiles[i], fileFilter);
}
}
}
protected void handleConfigFile(File configFile) {
try {
if (targetDir != null) {
try {
if (targetFilesFlat) {
File targetFile = new File(targetDir, configFile.getName());
FileHelper.copy(configFile, targetFile, true);
}
else {
String relPathName = configFile.getAbsolutePath().substring(baseDir.getAbsolutePath().length());
if (!relPathName.startsWith(File.separator)) {
relPathName = File.separator + relPathName;
}
File targetFile = new File(targetDir, relPathName);
// System.err.println("will create " + targetFile.getAbsolutePath());
FileHelper.copy(configFile, targetFile, true);
}
} catch (IOException e) {
e.printStackTrace();
}
}
System.out.println("Potential config file: " + configFile.getAbsolutePath());
}
catch (Throwable thr) {
logger.log(Level.SEVERE, "Failed to handle file " + configFile.getAbsolutePath(), thr);
}
}
protected void setBaseDir(File baseDir) {
this.baseDir = baseDir;
}
protected void setTargetDir(File targetDir) {
this.targetDir = targetDir;
}
protected void addFileEndings(String[] moreFileEndings) {
if (moreFileEndings != null) {
for (int i = 0; i < moreFileEndings.length; i++) {
if (moreFileEndings[i] == null || moreFileEndings[i].trim().length() == 0) {
throw new IllegalArgumentException("illegal empty file ending.");
}
this.fileEndings.add(moreFileEndings[i].trim());
}
}
}
public static void main(String[] args) {
try {
ConfigFileFinder configFinder = new ConfigFileFinder();
configFinder.configureFromArgs(args);
configFinder.configureRedeemers();
configFinder.checkConfiguration();
configFinder.run();
System.out.println("done.");
} catch (Exception e) {
e.printStackTrace();
}
}
}