/*******************************************************************************
* ALMA - Atacama Large Millimeter Array
* Copyright (c) UTFSM - Universidad Tecnica Federico Santa Maria, 2011
* (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
*******************************************************************************/
/**
* @author Rodrigo Araya (raraya[at]inf.utfsm.cl) & Nicolas Barriga
* (nbarriga[at]inf.utfsm.cl) & Marco Salgado (msalgado[at]inf.utfsm.cl)
*
* */
package cl.utfsm.cdbChecker;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileWriter;
import java.io.FilenameFilter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.List;
import java.util.Properties;
import java.util.Set;
import java.util.Vector;
import java.util.logging.Logger;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import org.apache.commons.lang.StringUtils;
import org.apache.xerces.parsers.SAXParser;
import org.omg.CORBA.ORB;
import org.omg.CORBA.Repository;
import org.omg.CORBA.RepositoryHelper;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import org.xml.sax.ErrorHandler;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import gnu.getopt.Getopt;
import gnu.getopt.LongOpt;
import alma.acs.cdbChecker.BaciSchemaChecker;
import alma.acs.cdbChecker.BaciSchemaChecker.BaciPropertyLocator;
import alma.acs.logging.ClientLogManager;
public class CDBChecker {
/**
* This logger was introduced very late.
* It should be used for debug messages in favor of stdout printing,
* whenever we touch some cdbChecker code.
*/
public final Logger logger;
public String XMLPath = null;
public String XSDPath = null;
private File tmpDir;
private SAXParser SP;
private Properties props = new Properties();
private Hashtable<String, String> xsd_targetns;
public static final String IR_CORBALOC = "ACS.repository";
private String schemaFolder;
private String targetNamespace;
private String componentsFolder = null;
private String containersFolder = null;
private boolean foundErr = false;
/**
* This errorFlag is used to signal from the parser
* callbacks that something failed int the validation
* of one specific file.
* It shall be reset before starting the validation of each file.
*/
private boolean errorFlag = false;
public boolean isErrorFlag() {
return errorFlag;
}
public void setErrorFlag(boolean errorFlag) {
this.errorFlag = errorFlag;
}
/**
* This globalErrorFlag is used to keep memory of any failure.
* It is never reset and it is set to true whenever there is a failure.
* If at the end of all validations it is true, it means that something
* failed and therefore we have to return with a failure
* error code.
*/
private boolean globalErrorFlag = false;
public boolean isGlobalErrorFlag() {
return globalErrorFlag;
}
public void setGlobalErrorFlag(boolean globalErrorFlag) {
if (globalErrorFlag == true) {
this.globalErrorFlag = true;
}
}
private Repository rep= null;
public Repository getIrRep() {
return rep;
}
// Command line parameter flags
private boolean verbose = false;
private boolean network = false;
private boolean checkidl = false;
private boolean recursive = true;
public boolean isVerbose() {
return verbose;
}
public boolean isCheckIdl() {
return checkidl;
}
public CDBChecker(Logger logger) {
this.logger = logger;
}
/* Filename filters used when recursively collecting files from the paths */
private FilenameFilter xmlFileFilter = new FilenameFilter() {
@Override
public boolean accept(File dir, String name) {
return name.endsWith(".xml");
}
};
private FilenameFilter xsdFileFilter = new FilenameFilter() {
@Override
public boolean accept(File dir, String name) {
return name.endsWith(".xsd");
}
};
private FilenameFilter dirFileFilter = new FilenameFilter() {
@Override
public boolean accept(File dir, String name) {
return new File(dir.getAbsolutePath() + File.separator + name).isDirectory();
}
};
/**
* This get the filenames of type 'type' from the given path.
* There could be several paths separated by ":".
*
* @param path multiple paths separated by ":" to look for 'type' files.
* @param type type of files to get.
* @return a vector of strings with the filenames of type 'type' with absolute path.
* An empty vector is returned if paths is empty.
*/
protected Vector<String> getFilenames(String paths[],String type){
Vector<String> vector = new Vector<String>();
String files[];
/*
* Scans the list of paths.
*/
for(int i=0;i<paths.length;i++)
{
if(paths[i].length() != 0)
{
File file = new File(paths[i]);
if(file.exists())
{
if(file.isFile())
{ //Is a File
if (paths[i].endsWith("."+type))
vector.add(paths[i]);
}
else
{ //Is a directory
if(!(paths[i].endsWith(File.separator)))
paths[i]=paths[i]+File.separator;
//Search for files, filtering for 'type'
if(type.equals("xml")) files = (new File(paths[i])).list(xmlFileFilter);
else files = (new File(paths[i])).list(xsdFileFilter);
//Add the files to the vector.
if (files!=null)
for (int j=0; j < files.length; j++)
vector.addElement(paths[i]+files[j]);
if(this.recursive)
{
String[] dirs = (new File(paths[i])).list(dirFileFilter);
if(dirs.length != 0)
for (int j=0; j < dirs.length; j++){
dirs[j]=paths[i]+dirs[j]+File.separator;
}
vector.addAll(getFilenames(dirs,type));
}
}
}
}
}
return vector;
}
/**
* This method validates the file encoding of XSD and XML files.
*
* @param File of an XML or XSD file to validate.
*/
protected void validateFileEncoding(String filename) throws IOException {
File file = new File(filename);
FileInputStream fis;
fis = new FileInputStream(file);
//BufferedReader in = new BufferedReader(new InputStreamReader(fis,"ASCII"));
int ch;
int line = 1;
int i = 0;
boolean crEnding = false;
while((ch = fis.read())!=-1) {
i++;
//There shouldn't be any character over 126 since 127 is <del> and ASCII only goes to 127.
if(ch >= 127) {
System.out.print(filename+": [Error] Non-ASCII Character "+ch+" found in XML or XSD at character: "+line+":"+i+".\n");
errorFlag=true;
globalErrorFlag=true;
}
//There shouldn't be any control character but the line feed or tab.
if(ch < 32 && ch != 10 && ch != 9) {
if(ch == 13) {
if(!crEnding) {
System.out.print(filename+": [Error] Carriage Return Character ("+ch+") found in XML or XSD at: "+line+":"+i+".\n");
System.out.print(filename+": This is probably CRLF Windows termination. Further Carriage Return errors are hidden.\n");
crEnding = true;
}
}
else
System.out.print(filename+": [Error] Illegal Control Character ("+ch+") found in XML or XSD at: "+line+":"+i+".\n");
errorFlag=true;
globalErrorFlag=true;
}
if(ch == 10){
i = 0;
line++;
}
}
}
/**
* This method validates the XSD files.
*
* @param xsdFilenames names with absolute path of the XSD file to validate.
*/
protected void validateSchemas(Vector<String> xsdFilenames){
System.out.println("*** Will verify XSD files in: " + this.XSDPath);
// We share the resolver, to benefit more from its cache.
CDBSchemasResolver resolver = new CDBSchemasResolver(this, schemaFolder + File.pathSeparator + XSDPath);
ErrorHandler errorHandler = new CDBErrorHandler(this);
for (String xsdFilename : xsdFilenames) {
final File xsdFile = new File(xsdFilename);
if (xsdFile.length() != 0) {
if (verbose) {
System.out.print(" " + xsdFilename);
}
try {
validateFileEncoding(xsdFilename);
SP.reset();
resolver.setResolveOnlyHttp(true); // not sure why, but this is the legacy behavior
SP.setEntityResolver(resolver);
SP.setFeature("http://xml.org/sax/features/validation", true);
SP.setFeature("http://apache.org/xml/features/validation/schema", true);
SP.setFeature("http://xml.org/sax/features/namespace-prefixes", false);
SP.setFeature("http://xml.org/sax/features/namespaces", true);
SP.setErrorHandler(errorHandler);
SP.setProperty("http://apache.org/xml/properties/schema/external-schemaLocation",
"http://www.w3.org/2001/XMLSchema http://www.w3.org/2001/XMLSchema.xsd");
FileInputStream fis = new FileInputStream(xsdFile);
InputSource inputSource = new InputSource(fis);
inputSource.setSystemId("file:///" + xsdFile.getAbsolutePath());
SP.parse(inputSource);
fis.close();
try {
// Now we know that the schema is technically valid (well, it does not seem to check xsd imports...)
// Still we have to check special requirements for CharacteristicComponent XSDs.
// This second check probably includes the first check's functionality, so that the
// first check could be removed once we have verified XSOM's xsd error reporting
// and hooked up the shared error handler in BaciSchemaChecker.
resolver.setResolveOnlyHttp(false); // here we want to resolve all schemas
BaciSchemaChecker baciChecker = new BaciSchemaChecker(xsdFile, resolver, errorHandler, logger);
List<BaciPropertyLocator> badProps = baciChecker.findBaciPropsOutsideCharacteristicComp();
if (!badProps.isEmpty()) {
// Reduce the available error output to show only xsd element(s), not the individual baci properties
Set<String> badXsdElementNames = new HashSet<String>();
for (BaciPropertyLocator badProp : badProps) {
badXsdElementNames.add(badProp.elementName);
}
System.out.println(xsdFilename +
": illegal use of baci properties in xsd elements that are not derived from baci:CharacteristicComponent. " +
"Offending element(s): " + StringUtils.join(badXsdElementNames, ' ')
);
errorFlag=true;
globalErrorFlag = true;
}
}
catch (SAXException ex) {
// ignore SAXException coming from BaciSchemaChecker, because we don't want to report errors
// twice when the xsd file is really messed up.
// There are cases where xerces reports a normal error but XSOM reports a fatal error and throws this exception.
}
if (verbose && !errorFlag) {
System.out.println("[OK]");
}
} catch (SAXException e) {
System.out.println("Caught a SAXException in validateSchemas: ");
e.printStackTrace(System.out);
} catch (IOException e) {
System.out.println("[IOException] Probably " + xsdFilename + " doesn't exists.");
}
} else {
System.out.print(xsdFilename + ": [Warning] file is empty.\n");
}
}
resolver.setResolveOnlyHttp(true); // back to legacy mode again... yuck, our resolver still sticks to the "SP" field and will be used elsewhere!
}
/**
* This method check if the idl types on CDB are available
*
*/
protected void checkIdlTypes(){
//first check if IR is available
org.omg.CORBA.Object repRef = null;
ORB orb = org.omg.CORBA.ORB.init(new String[0], null);
String IRloc = props.getProperty(IR_CORBALOC);
try{
repRef = orb.string_to_object(IRloc);
}catch(Exception e){
System.out.println("[Error] - Interface repository is not running, no check will be done.");
}
rep = RepositoryHelper.narrow(repRef);
//iterate trough all idlTypes
//done in parseElement
}
/**
* This method validates the XML files.
*
* @param filenames name with absolute path of the XML file to validate.
*/
protected void XMLValidate(Vector<String> filenames){
System.out.println("*** Will verify XML files in directory: " + this.XMLPath);
for(int i=0;i<filenames.size();i++){
File file = new File(filenames.get(i));
if(file.length()!=0){
if(verbose){
System.out.print(" "+ filenames.get(i));
/*for(int j=0;j<(90-(int)((String)filename.get(i)).length())/8;j++)
System.out.print("\t");
*/}
String targetNamespace = ((xsd_targetns.toString()).replace(',',' ')).replace('=',' ').replace('{',' ').replace('}',' ');
errorFlag=false;
try{
validateFileEncoding(filenames.get(i));
SP.reset();
SP.setFeature("http://xml.org/sax/features/validation",true);
SP.setFeature("http://apache.org/xml/features/validation/schema", true);
SP.setFeature("http://xml.org/sax/features/namespace-prefixes",false);
SP.setFeature("http://xml.org/sax/features/namespaces",true);
SP.setErrorHandler(new CDBErrorHandler(this));
SP.setProperty("http://apache.org/xml/properties/schema/external-schemaLocation",targetNamespace);
FileInputStream fis = new FileInputStream(file);
InputSource inputSource = new InputSource(fis);
inputSource.setSystemId("file:///" + file.getAbsolutePath());
SP.parse(inputSource);
fis.close();
if(verbose && !errorFlag)
System.out.println("[OK]");
}catch(SAXException e){System.out.println("[SAXException] " + e.getMessage());}
catch(IOException e){System.out.println("[IOException] Probably "+ filenames.get(i) + " doesn't exist.");}
}else{
System.out.print( filenames.get(i) + ": [Warning] file is empty.\n");
}
}
}
/**
* This method checks for the targetNamespace defined by the schema files and fills the CDBChecker.xsd_targetns with pairs {targetNamespace, XSD filename}
*
* @param xsdFilenames Vector with all the XSD filenames with absolute path.
*/
protected void getTargetNamespace(Vector<String> xsdFilenames){
String filename;
for(int i=0;i<xsdFilenames.size();i++){
filename= xsdFilenames.get(i);
File file = new File(xsdFilenames.get(i));
if(file.length()!=0){
SP.setContentHandler(new CDBContentHandler(this));
SP.reset();
try{
SP.setFeature("http://xml.org/sax/features/validation",false);
SP.setFeature("http://apache.org/xml/features/validation/schema",false);
SP.setFeature("http://xml.org/sax/features/namespace-prefixes",false);
SP.setFeature("http://xml.org/sax/features/namespaces",true);
SP.setErrorHandler(new CDBErrorHandler(this));
FileInputStream fis = new FileInputStream(file);
InputSource inputSource = new InputSource(fis);
inputSource.setSystemId("file:///" + file.getAbsolutePath());
SP.parse(inputSource);
fis.close();
} catch(SAXException e){e.getMessage();}
catch (IOException e){System.out.println("[IOException] Probably "+filename+" doesn't exists.");}
if(targetNamespace!=null)
{
/* GCH
* If the targetNamespace has been already registered,
* I skip registering it again.
* In this way I give priority to definitions that come first.
* Since the search order depends on the order int the ACS.cdbPath
* property, standard definitions can be overwritten byte newer ones in
* the standard search path algorithm.
*/
if(xsd_targetns.containsKey(targetNamespace))
{
/*
* If the same targetNamespace appears int files with
* the same name, then we are simply overriding the
* default version with a new one and we need to warning.
* Otherwise, a warning can be useful to discover
* inconsistencies.
*/
String[] newArr = filename.split("/");
String[] oldArr = ((String)xsd_targetns.get(targetNamespace)).split("/");
if(newArr[newArr.length-1].compareTo(oldArr[oldArr.length-1])!=0)
{
System.out.println("[Warning] The XSD files \""+ xsdFilenames.get(i) + "\" and \""+xsd_targetns.get(targetNamespace)+"\" have same targetNamespace: \""+targetNamespace+"\". Skipping this one.");
}
}
else
{
xsd_targetns.put(targetNamespace, "file:///" + xsdFilenames.get(i));
}
}
}else{
System.out.print( xsdFilenames.get(i) + ": [Warning] file is empty.\n");
}
}
}
/**
* Sets the static variable CDBChecker.targetNamespace.
* Called by CDBContentHandler whenever it encounters a schema#targetNamespace attribute.
* @param targetNamespace
*/
public void setTargetNamespaceString(String targetNamespace){
// System.out.println("targetNamespace = " + targetNamespace);
this.targetNamespace = targetNamespace;
}
/**
* Downloads the file from the given URL. Creates the temporary directory directory if it doesn't already exists.
* Only downloads the file if it doesn't already exists.
* @param url where to download the file from.
*/
private void getFile(String url){
String myFile = new String();
try{
String[] arr = url.split("/");
myFile=schemaFolder+arr[arr.length-1];
File file = new File(myFile);
URL remFile = new URL(url);
String cad;
BufferedReader filePage = new BufferedReader(new InputStreamReader( remFile.openStream() ));
FileWriter output = new FileWriter(file);
while ((cad = filePage.readLine()) != null) {
output.write(cad+"\n");
}
output.flush();
output.close();
} catch( MalformedURLException e ){e.printStackTrace();}
catch( IOException e) {
System.out.println("[IOexception] Probably "+myFile+" couldn't be written.");
e.printStackTrace();
}
}
public void cleanUp()
{
if(tmpDir!=null) {
if(verbose) { System.out.println("*** Deleting Temporary files"); }
deleteTmp();
}
}
/**
* Calls CDBChecker.getFile() to download files usually needed by XSD schema files.
* @param reqSchemas Vector that contains the required schemas, to be downloaded.
*/
public void downloadSchemas(List<String> reqSchemas){
System.out.print("*** Downloading remote schemas");
if(verbose) {
System.out.print("\n*** Storing schemas in: " + schemaFolder);
}
for(int i=0;i<reqSchemas.size();i++){
String fileToGet = reqSchemas.get(i);
getFile(fileToGet);
if(verbose) {
System.out.print("\n\tDownloaded file: " + fileToGet);
}
}
System.out.println("\n*** Remote schemas succesfully downloaded");
}
/**
* Prints usage information.
*
*/
protected static void printUsage(){
System.out.println("\n[usage:]\n\n cdbChecker [-flags] [XMLPath] [XSDPath]");
System.out.println("\n\n Flags:\n");
System.out.println(" -v | --verbose Verbose output");
System.out.println(" -r | --recursive Disable recursive traversal of XMLPath and XSDPath");
System.out.println(" when searching for .xml/.xsd files");
System.out.println(" -n | --network Get required schemas from the network");
System.out.println(" -c | --checkIdlTypes Check if the idl types in CDB are available");
System.out.println(" -h | --help Show this help");
System.out.println("\n The XMLPath and XSDPath can have multiple paths separated by \":\".");
System.out.println(" The paths must be absolute (i.e. they should start with '/')");
System.out.println(" The checker will search for files recursively inside the given paths,");
System.out.println(" unless the -r flag is specified.\n");
System.out.println(" NOTE: 1) the value passed in as the XSDPath will be pre-pended to the");
System.out.println(" value from the ACS.cdbPath property; 2) if not specified, the XMLPath");
System.out.println(" will default to $ACS_CDB/CDB (if ACS_CDB environment variable is set).\n");
System.out.println(" ACS_CDB is used if XMLPath is not given");
}
/**
* Checks the command line arguments given to the program and capture the given flags.
* @param args command line arguments
* @return True if arguments are OK, false otherwise
*/
protected boolean checkArgs(String[] args)
{
boolean retVal = true;
int c;
LongOpt[] longopts = new LongOpt[5];
longopts[0] = new LongOpt("help", LongOpt.NO_ARGUMENT, null, 'h');
longopts[1] = new LongOpt("network", LongOpt.NO_ARGUMENT, null, 'n');
longopts[2] = new LongOpt("verbose", LongOpt.NO_ARGUMENT, null, 'v');
longopts[3] = new LongOpt("recursive", LongOpt.NO_ARGUMENT, null, 'r');
longopts[4] = new LongOpt("checkIdlTypes", LongOpt.NO_ARGUMENT, null, 'c');
Getopt myGetOpt = new Getopt("cdbChecker", args, "rhncvaW;", longopts);
myGetOpt.setOpterr(false); // We'll do our own error handling
while ((c = myGetOpt.getopt()) != -1) {
switch (c) {
case 'n':
this.network = true;
break;
case 'r':
this.recursive = false;
break;
case 'v':
this.verbose = true;
break;
case 'h':
retVal = false;
break;
case 'c':
this.checkidl = true;
break;
case 'W':
System.out.println("[Error] : you tried a -W with an incorrect long option name");
globalErrorFlag = true;
retVal = false;
break;
case '?':
if(0 == myGetOpt.getOptopt()) {
System.out.println("[Error] : the long option '" + args[myGetOpt.getOptind() - 1] + "' is not valid");
}
else {
System.out.println("[Error] : the option '" + (char)myGetOpt.getOptopt() + "' is not valid");
}
globalErrorFlag = true;
retVal = false;
break;
default:
globalErrorFlag = true;
retVal = false;
break;
}
}
// do the following only if we aren't already needing to pretVal is not false)
if(retVal) {
// check for the additional (optional) command line arguments, XMLPath and XSDPath
for (int i = myGetOpt.getOptind(); i < args.length ; i++) {
if(myGetOpt.getOptind() == i) {
if(args[i].startsWith("/") || args[i].matches("[a-zA-Z]:.*")) {
this.XMLPath = args[i];
}
else {
System.out.println("[Error] : XMLPath must start with '/'");
globalErrorFlag = true;
retVal = false;
}
}
else {
if(args[i].startsWith("/") || args[i].matches("[a-zA-Z]:.*")) {
this.XSDPath = args[i];
}
else {
System.out.println("[Error] : XSDPath must start with '/'");
globalErrorFlag = true;
retVal = false;
}
break;
}
}
// finally, if XMLPath wasn't specified, use a sensible default
if(retVal && null == this.XMLPath) {
String acsCdbPath = System.getenv("ACS_CDB");
if(null != acsCdbPath)
{
acsCdbPath += File.separator+"CDB";
System.out.println("*** XML path not specified; defaulting to $ACS_CDB"+File.separator+"CDB: " + acsCdbPath);
XMLPath = acsCdbPath;
}
else
{
System.out.println("\n[Error] XML path not specified and $ACS_CDB environment variable not set; no default possible.");
globalErrorFlag = true;
retVal = false;
}
}
}
return retVal;
}
/**
* @return <code>true</code> if all configuration data was set up OK.
*/
private boolean configLoader() {
String config_path;
String tmp_path;
List<String> reqSchemas = new ArrayList<String>();
if ((config_path = props.getProperty("ACS.config_path")) == null) {
System.out.println("config_path not defined");
return false;
}
// Use the default ACS_TMP directory to download the schemas from the network
tmp_path = System.getProperty("ACS.tmp");
// else use the systems default
if (tmp_path == null) {
tmp_path = File.separator + "tmp";
}
SP.setContentHandler(new ConfigurationCH(reqSchemas));
SP.reset();
try {
SP.setFeature("http://xml.org/sax/features/validation", false);
SP.setFeature("http://apache.org/xml/features/validation/schema", false);
SP.setFeature("http://xml.org/sax/features/namespace-prefixes", false);
SP.setFeature("http://xml.org/sax/features/namespaces", true);
SP.parse(config_path + File.separator + "config" + File.separator + "reqSchemas.xml");
} catch (SAXException e) {
e.getMessage(); // HSO: is this intended no-op behavior?
} catch (IOException e) {
System.out.println("[IOException] Probably the configuration file doesn't exist.");
return false;
}
if (this.network) {
tmpDir = new File(tmp_path, "cdbchecker." + System.currentTimeMillis() + ".tmp");
tmpDir.mkdirs();
if (!tmpDir.exists()) {
System.out.println("[Error] No permission to create temporary directory " + tmpDir.getAbsolutePath());
return false;
}
schemaFolder = tmpDir.getAbsolutePath() + File.separator;
downloadSchemas(reqSchemas);
} else {
schemaFolder = config_path + File.separator + "config" + File.separator + "CDB" + File.separator
+ "schemas" + File.separator;
if (!(new File(schemaFolder)).exists()) {
System.out.println("[Error] The required schema files are missing, please run the tool with the '-n' option.");
return false;
}
}
return true;
}
protected void deleteTmp(){
String list[] = tmpDir.list();
for(int i=0;i<list.length;i++)
(new File(tmpDir.getAbsolutePath()+File.separator+list[i])).delete();
tmpDir.delete();
}
/**
* Main function to run the cdbChecker tool.
* System.exit(0/1) is used to return success if everything if fine or
* failure in case errors were encountered.
*/
public static void main(String[] args) {
Logger logger = ClientLogManager.getAcsLogManager().getLoggerForApplication(CDBChecker.class.getSimpleName(), false);
CDBChecker cdbchecker = new CDBChecker(logger);
cdbchecker.props = System.getProperties();
/* Blank lines to see the output clearly */
System.out.println("\n\n");
/*
* Retrieves the CDB path from the property, if given
*/
String ACS_cdbpath = cdbchecker.props.getProperty("ACS.cdbpath");
/*
* Check for Paths and flags received from command line
* and sets accordiningly member variables.
* These non explicit side effects should be avoided.
*/
if(cdbchecker.checkArgs(args)) {
//add panta@naoj 2009/10/05
String pathsMulti[]=cdbchecker.XMLPath.split(File.pathSeparator);
for(int i = 0; i < pathsMulti.length; i++){
File file_ = new File(pathsMulti[i]);
if(!file_.exists()){
System.out.println("*** ImplLang Check: Specified path " + file_+ " does not exist");
cdbchecker.setGlobalErrorFlag(true);
cdbchecker.showEndResult();
break;
}
}
//add panta@naoj 2009/10/05 end
//Creating the parser
// System.setProperty("org.apache.xerces.xni.parser.XMLParserConfiguration", "org.apache.xerces.parsers.XIncludeAwareParserConfiguration");
cdbchecker.SP = new SAXParser();
cdbchecker.xsd_targetns = new Hashtable<String,String>();
//Download the required Schemas
if(cdbchecker.verbose) {
System.out.println("*** Reading required schema files");
}
if (cdbchecker.configLoader()) {
if (cdbchecker.verbose) {
System.out.println("*** Reading given schema files");
}
// Appends command line schema files, if any
if (ACS_cdbpath != null) {
// We assume that cdbchecker.XSDPath is at least initialised to the empty string and never null
// Modify panta@naoj 2009/10/15
if (cdbchecker.XSDPath == null) {
cdbchecker.XSDPath = ACS_cdbpath;
} else {
cdbchecker.XSDPath = cdbchecker.XSDPath + ":" + ACS_cdbpath;
}
}
if (cdbchecker.checkidl) {
if (cdbchecker.verbose) {
System.out.println("*** Checking Idl Types");
}
cdbchecker.checkIdlTypes();
}
String paths[] = cdbchecker.XSDPath.split(File.pathSeparator);
Vector<String> xsdFilenames = cdbchecker.getFilenames(paths, "xsd");
if (cdbchecker.verbose)
System.out.println("*** Reading given XML files");
// We assume that cdbchecker.XMLPath is at least
// initialised to the empty string and never null
paths = cdbchecker.XMLPath.split(File.pathSeparator);
Vector<String> XMLFilenames = cdbchecker.getFilenames(paths, "xml");
// Fill the map with the targetNamespace and the filenames
if (cdbchecker.verbose)
System.out.println("*** Getting TargetNamespaces from schema files");
cdbchecker.getTargetNamespace(xsdFilenames);
// Validating Schemas
if (cdbchecker.verbose)
System.out.println("*** Validating Schemas");
cdbchecker.validateSchemas(xsdFilenames);
// Validating XML files
if (cdbchecker.verbose)
System.out.println("*** Validating XML files");
cdbchecker.XMLValidate(XMLFilenames);
// add panta@naoj 2009/10/05
// checks if implLang matches, those written in XXComponents.xml and XXContainers.xml
for (int i = 0; i < pathsMulti.length; i++) {
cdbchecker.componentsFolder = pathsMulti[i] + File.separator + "MACI" + File.separator + "Components";
cdbchecker.containersFolder = pathsMulti[i] + File.separator + "MACI" + File.separator + "Containers";
File compFolder = new File(cdbchecker.componentsFolder);
File contFolder = new File(cdbchecker.containersFolder);
// System.out.println("compFolder: " + compFolder);
// System.out.println("contFolder: " + contFolder);
if (compFolder.exists() && contFolder.exists()) {
cdbchecker.setGlobalErrorFlag(cdbchecker.checkImplLangMatch(compFolder, contFolder));
// exit if error
if (cdbchecker.isGlobalErrorFlag()) {
break;
}
}
}
// add panta@naoj 2009/10/05 end
}
} else {
printUsage();
}
cdbchecker.cleanUp();
cdbchecker.showEndResult();
}
private void showEndResult(){
if(globalErrorFlag==true) {
System.out.println("\n[Error] CDBChecker exiting. Errors were found\n");
System.exit(1);
}
else {
System.out.println("\nCDBChecker exiting. No errors found\n");
System.exit(0);
}
}
/******************************************************************
* This method finds files in "Components" and "Containers"
* directories and sub-directories. It then extracts "implLang"
* properties, and compares. Error messages are displayed if
* Components.xml's implLang and Containers.xml's implLang
* don't match.
* Returns 'true' if error is found, false otherwise
* added by panta@naoj 2009/10/05
*****************************************************************/
protected boolean checkImplLangMatch(File compFolder, File contFolder){
File[] files = compFolder.listFiles();
search:
for (int x = 0; x < files.length; x++){
if(foundErr){
break search;
}
if (files[x].isDirectory()){
if(!files[x].getName().equals("CVS")){
checkImplLangMatch(files[x], contFolder); //recursive call
}
}
else{
//only process .xml files
String ext = "";
int iExt = files[x].getName().lastIndexOf(".");
ext=files[x].getName().substring(iExt+1,files[x].getName().length());
if(!ext.equals("xml")){
continue;
}
//System.out.println("\nChecking.. " + files[x]);
DocumentBuilderFactory dbfComp = DocumentBuilderFactory.newInstance();
DocumentBuilder dbComp = null;
try {
dbComp = dbfComp.newDocumentBuilder();
} catch (ParserConfigurationException e) {
e.printStackTrace();
}
Document docComp = null;
try {
docComp = dbComp.parse(files[x]);
} catch (SAXException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
docComp.getDocumentElement().normalize();
NodeList compNodeList = docComp.getElementsByTagName( "Component" );
//modify bhola.panta@naoj 2010-03-15
if(compNodeList.getLength() == 1) { //only the variant where a single component is configured in its own file
String compName = ((Element) compNodeList.item(0)).getAttribute("Name" );
//add bhola.panta@naoj 2010-03-03
//fileName and "Name" property must match
if(!compName.equals("*") && !files[x].getName().equals("Components.xml")){
//must support hierarchical component names (separated by "/")
if(!checkHierarchical(files[x], compName)){
if(!(compName + ".xml").equals(files[x].getName())){
System.out.print("\nMismatch between component name and XML file name.");
System.out.print("\nComponent File: " + files[x]);
System.out.print("\nComponent name: '" + compName +"'");
System.out.print("\nFile name: '" + files[x].getName()+"'");
foundErr = true;
break search;
}
}
}
}
if(compNodeList.getLength() == 0) {
compNodeList = docComp.getElementsByTagName( "_" );
if(compNodeList.getLength() == 0) {
continue;
}
}
//this part extracts "implLang" for each component
for( int j = 0; j < compNodeList.getLength(); j++ ) {
Element elm = (Element) compNodeList.item( j );
String compName = null;
String implLang = null;
String tempContainersFolder = null;
compName = elm.getAttribute("Name" );
implLang = elm.getAttribute("ImplLang" );
//System.out.println("\ncompName being checked: " + compName );
if(compName.equals("*")){ //--> dynamic component
if(implLang.equals("") || implLang.equals("*")){
continue;
}
//some dynamic components may not have predefined Containers
if(elm.getAttribute("Container" ).equals("") || elm.getAttribute("Container" ).equals("*")){
continue;
}
}
//add bhola.panta@naoj 2011/07/21
//component does have a name, but container is dynamic (?), that is, "*"
else if(elm.getAttribute("Container" ).equals("*")){
continue;
}
else{//actually, ImpLang field in the CDB is mandatory since ACS 8
if(implLang.equals("")){
System.out.println("\nFile being checked: " + files[x] );
System.out.print("\n'ImplLang' missing for component: " + compName);
foundErr = true;
break search;
}
}
//go get containers at the "Container" location
tempContainersFolder = containersFolder + File.separator + elm.getAttribute("Container" );
//open the container file and have a look
DocumentBuilderFactory dbfCont = DocumentBuilderFactory.newInstance();
DocumentBuilder dbCont = null;
try {
dbCont = dbfCont.newDocumentBuilder();
} catch (ParserConfigurationException e) {
e.printStackTrace();
}
Document docCont = null;
try {
//System.out.println("\ntempContainersFolder " + tempContainersFolder);
File contFile = new File(tempContainersFolder + File.separator + new File(tempContainersFolder).getName()+ ".xml");
//System.out.println("\ncontainerFile " + contFile);
if(contFile.exists()){
docCont = dbCont.parse(contFile);
docCont.getDocumentElement().normalize();
NodeList contNodeList = docCont.getElementsByTagName( "Container" );
//Go through Container files and check ImplLang
for( int k = 0; k < contNodeList.getLength(); k++ ) {
Element elmCont = (Element) contNodeList.item( k );
//check if Component ImplLang and Container ImplLang match
if(implLang.equals(elmCont.getAttribute("ImplLang" ))){
}
else{
System.out.println("\nComponent File being checked: " + files[x] );
System.out.println("Container File being checked: " + contFile);
System.out.println("'ImplLang' does not match for component: " + compName +".");
foundErr = true;
break search;
}
}//Container for loop
}
else{
System.out.print("\nComponent File being checked: " + files[x] );
System.out.print("\nMissing Container " + new File(tempContainersFolder));
System.out.println("");
foundErr = true;
break search;
}
} catch (SAXException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}//Component for loop
}//is a file (not dir)
}//all files for loop
return foundErr;
}
/**************************************************************************************************************************
* Add bhola.panta@naoj 2010/05/12, in order to support hierarchical component names (separated by "/")
* I am quoting a comment from Heiko Sommer, ref. #COMP-4247
* We must support hierarchical component names (separated by "/"), where the path of the xml
* file should match the component name.
* eg. File path: /alma/ACS-9.0/acsdata/config/defaultCDB/CDB/MACI/Components/ARCHIVE/TMCDB/MONITOR_BLOBBER2/MONITOR_BLOBBER2.xml
* Component name: 'ARCHIVE/TMCDB/MONITOR_BLOBBER2'
* File name: 'MONITOR_BLOBBER2.xml'
* Under "CDB/MACI/Components", there is the path "ARCHIVE/TMCDB/MONITOR_BLOBBER2",
* which does match the component name.
* (It's a convention of the CDB that for hierarchical components, but also for other nodes,
* the xml file name and the directory name where the xml file is in, are the same
* except for the .xml ending. For matching the component name we count it only once,
* instead of taking "ARCHIVE/TMCDB/MONITOR_BLOBBER2/MONITOR_BLOBBER2".)
* Credit: http://java.sun.com/docs/books/tutorial/java/data/comparestrings.html
*****************************************************************************************************************************/
private boolean checkHierarchical(File fSearchMe, String findMe){
String searchMe = fSearchMe.getPath();
int searchMeLength = searchMe.length();
int findMeLength = findMe.length();
boolean foundIt = false;
for (int i = 0; i <= (searchMeLength - findMeLength); i++) {
if (searchMe.regionMatches(i, findMe, 0, findMeLength)) {
foundIt = true;
break;
}
}
return foundIt;
}
}//class