cl.utfsm.cdbChecker.CDBChecker.java Source code

Java tutorial

Introduction

Here is the source code for cl.utfsm.cdbChecker.CDBChecker.java

Source

/*******************************************************************************
 * 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