/*
 * The contents of this file are subject to the Mozilla Public License Version 1.1
 * (the "License"); you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at <http://www.mozilla.org/MPL/>.
 * 
 * Software distributed under the License is distributed on an "AS IS" basis, WITHOUT
 * WARRANTY OF ANY KIND, either express or implied. See the License for the specific
 * language governing rights and limitations under the License.
 * 
 * The Original Code is the Venice Web Communities System.
 * 
 * The Initial Developer of the Original Code is Eric J. Bowersox <erbo@silcom.com>,
 * for Silverwrist Design Studios.  Portions created by Eric J. Bowersox are
 * Copyright (C) 2001 Eric J. Bowersox/Silverwrist Design Studios.  All Rights Reserved.
 * 
 * Contributor(s): 
 */
package com.silverwrist.venice.core;

import java.io.*;
import javax.xml.parsers.*;
import org.apache.log4j.*;
import org.w3c.dom.*;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;
import com.silverwrist.util.DOMElementHelper;
import com.silverwrist.venice.except.*;

public class Startup
{
  /*--------------------------------------------------------------------------------
   * Static data values
   *--------------------------------------------------------------------------------
   */

  private static Category logger = Category.getInstance(Startup.class);

  /*--------------------------------------------------------------------------------
   * Constructor
   *--------------------------------------------------------------------------------
   */

  private Startup()
  { // this class cannot be created
  } // end constructor

  /*--------------------------------------------------------------------------------
   * Internal functions
   *--------------------------------------------------------------------------------
   */

  private static String getEngineClassName(Document config) throws ConfigException
  {
    // Make sure the configuration is valid...
    Element root = config.getDocumentElement();
    if (!(root.getTagName().equals("venice-config")))
    { // not the correct root tag name
      logger.fatal("config document is not a <venice-config/> document (root tag: <"
		   + root.getTagName() + "/>)");
      throw new ConfigException("document is not a <venice-config/> document",root);

    } // end if

    // Get the <engine/> section.
    DOMElementHelper root_h = new DOMElementHelper(root);
    Element engine_sect = root_h.getSubElement("engine");
    if (engine_sect==null)
    { // no <engine/> section - bail out now!
      logger.fatal("config document has no <engine/> section");
      throw new ConfigException("no <engine/> section found in config file",root);

    } // end if

    // Get the classname out of that section.
    DOMElementHelper engine_sect_h = new DOMElementHelper(engine_sect);
    String rc = engine_sect_h.getSubElementText("classname");
    if (rc==null)
    { // no <classname/> specified - bail out now!
      logger.fatal("config document <engine/> section has no <classname/>");
      throw new ConfigException("no <classname/> found in <engine/> section",engine_sect);

    } // end if

    return rc;

  } // end getEngineClassName

  /*--------------------------------------------------------------------------------
   * External static methods (global functions)
   *--------------------------------------------------------------------------------
   */

  public static Document loadConfiguration(String configname) throws ConfigException
  {
    try
    { // create a simple DOM parser by using the Java XML parsing API
      DocumentBuilderFactory fac = DocumentBuilderFactory.newInstance();
      fac.setNamespaceAware(false);
      fac.setValidating(false);
      DocumentBuilder parser = fac.newDocumentBuilder();

      // access the config file and parse it into our config data tree
      File configfile = new File(configname);
      Document rc = parser.parse(configfile);
      if (logger.isDebugEnabled())
	logger.debug("configuration loaded successfully");
      return rc;

    } // end try
    catch (FactoryConfigurationError e1)
    { // if the document builder factory could not be created
      logger.fatal("Parser factory configuration error: " + e1.getMessage(),e1);
      throw new ConfigException("XML parser factory could not be created - " + e1.getMessage());

    } // end catch
    catch (ParserConfigurationException e2)
    { // if the XML parser itself could not be created
      logger.fatal("Parser configuration error: " + e2.getMessage(),e2);
      throw new ConfigException("XML parser could not be created - " + e2.getMessage(),e2);

    } // end catch
    catch (SAXException e3)
    { // if the XML parser choked on our document
      if (e3 instanceof SAXParseException)
      { // we have a detailed message - make a proper exception
	SAXParseException e3a = (SAXParseException)e3;
	logger.fatal("Config file error [" + configname + ":" + e3a.getLineNumber() + ","
		     + e3a.getColumnNumber() + "]: " + e3a.getMessage(),e3a);
	throw new ConfigException("Configuration file error: " + e3a.getMessage() + " at line "
				  + e3a.getLineNumber() + ", column " + e3a.getColumnNumber(),e3a);

      } // end if
      else
      { // generic exception - just send up a simple error message
	logger.fatal("Config file error [" + configname + "]: " + e3.getMessage(),e3);
	throw new ConfigException("Configuration file error - " + e3.getMessage(),e3);

      } // end else

    } // end catch
    catch (IOException e4)
    { // error reading the config file itself off the disk
      logger.fatal("IO error reading config: " + e4.getMessage(),e4);
      throw new ConfigException("unable to read config file \"" + configname + "\" - " + e4.getMessage(),e4);

    } // end catch

  } // end loadConfiguration

  public static VeniceEngine createEngine(String configname, String app_root)
      throws ConfigException, DataException
  {
    // load the configuration data
    Document config = loadConfiguration(configname);

    // find the classname of the engine
    String cname = getEngineClassName(config);

    if (logger.isDebugEnabled())
      logger.debug("Venice engine classname: " + cname);

    try
    { // attempt to load the engine class
      Class engine_class = Class.forName(cname);

      // now create a new Venice engine (cast to the external interface)
      VeniceEngine engine = (VeniceEngine)(engine_class.newInstance());

      if (logger.isDebugEnabled())
	logger.debug("VeniceEngine created successfully");

      // initialize the engine
      engine.initialize(config,app_root);

      // and return it
      return engine;

    } // end try
    catch (ClassNotFoundException e1)
    { // the class was not found
      logger.fatal("Venice engine class \"" + cname + "\" not found",e1);
      throw new ConfigException("Invalid engine classname: " + cname);

    } // end catch
    catch (IllegalAccessException e2)
    { // could not access constructor or something
      logger.fatal("Can't access \"" + cname + "\" constructor: " + e2.getMessage(),e2);
      throw new ConfigException("Unable to create classname: " + cname);

    } // end catch
    catch (InstantiationException e3)
    { // could not create the instance
      logger.fatal("Can't create instance of \"" + cname + "\": " + e3.getMessage(),e3);
      throw new ConfigException("Unable to create classname: " + cname);

    } // end catch
    catch (ExceptionInInitializerError e4)
    { // some exception while initializing class
      logger.fatal("Exception while initializing \"" + cname + "\": " + e4.getMessage(),e4);
      throw new ConfigException("Unable to create classname: " + cname);

    } // end catch
    catch (SecurityException e5)
    { // security violation somewhere
      logger.fatal("Security violation on \"" + cname + "\": " + e5.getMessage(),e5);
      throw new ConfigException("Unable to create classname: " + cname);

    } // end catch
    catch (ClassCastException e6)
    { // engine could not be cast to VeniceEngine interface type
      logger.fatal("\"" + cname + "\" is not of type VeniceEngine",e6);
      throw new ConfigException("Invalid engine classname: " + cname);

    } // end catch

  } // end createEngine

} // end class Startup
