com.aurel.track.ApplicationStarter.java Source code

Java tutorial

Introduction

Here is the source code for com.aurel.track.ApplicationStarter.java

Source

/**
 * Genji Scrum Tool and Issue Tracker
 * Copyright (C) 2015 Steinbeis GmbH & Co. KG Task Management Solutions
    
 * <a href="http://www.trackplus.com">Genji Scrum Tool</a>
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
 */

/* $Id:$ */

package com.aurel.track;

import java.io.File;
import java.io.InputStream;
import java.lang.reflect.Method;
import java.net.InetAddress;
import java.net.URL;
import java.net.URLEncoder;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.ResourceBundle;
import java.util.Set;
import java.util.concurrent.ExecutorService;

import javax.servlet.ServletConfig;
import javax.servlet.ServletContext;

import org.apache.commons.configuration.PropertiesConfiguration;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.core.config.Configurator;
import org.apache.torque.Torque;
import org.apache.torque.TorqueException;

import com.aurel.track.admin.server.dbbackup.DatabaseBackupBL;
import com.aurel.track.beans.TClusterNodeBean;
import com.aurel.track.beans.TSiteBean;
import com.aurel.track.cluster.ClusterBL;
import com.aurel.track.cluster.ClusterUpdateBL;
import com.aurel.track.dao.DAOFactory;
import com.aurel.track.dao.SiteDAO;
import com.aurel.track.dbase.HandleHome;
import com.aurel.track.dbase.InitDatabase;
import com.aurel.track.dbase.InitReportTemplateBL;
import com.aurel.track.dbase.JobScheduler;
import com.aurel.track.dbase.UpdateDbSchema;
import com.aurel.track.dbase.jobs.FileMonitor;
import com.aurel.track.fieldType.types.FieldTypeDescriptorUtil;
import com.aurel.track.item.lock.ItemLockBL;
import com.aurel.track.lucene.LuceneUtil;
import com.aurel.track.lucene.index.LuceneIndexer;
import com.aurel.track.persist.BaseTSitePeer;
import com.aurel.track.persist.TLoggingLevelPeer;
import com.aurel.track.prop.ApplicationBean;
import com.aurel.track.resources.ResourceBundleManager;
import com.aurel.track.util.DateTimeUtils;
import com.aurel.track.util.LabelValueBean;
import com.aurel.track.util.PluginUtils;
import com.aurel.track.util.Support;
import com.aurel.track.util.event.EventPublisher;
import com.aurel.track.util.event.IEventSubscriber;
import com.cybozu.labs.langdetect.DetectorFactory;
import com.opensymphony.xwork2.util.LocalizedTextUtil;
import com.trackplus.license.LicenseManager;

/**
 * ApplicationStarter initializes the Genji application. This includes
 * <ul>
 * <li>Creating the database structure if it is not available
 * <li>Updating the database structure in case it has changed
 * <li>Filling the database with default values
 * <li>Copying files like logos and templates to their proper places
 * <li>Initializing the full text search engine
 * <li>Initializing the job scheduling subsystem
 * </ul>
 * After ApplicationStarter is done, the Struts2 Action servlet serves the user
 * at the browser or web service interface as defined in file
 * <code>struts.xml</code>.
 *
 */
public final class ApplicationStarter implements Runnable {

    private static final Logger LOGGER = LogManager.getLogger(ApplicationStarter.class);

    private static ApplicationStarter instance = null;

    // The second number in the arrays below are the total percentage
    // complete allocated to this initialization step. The first number
    // is usually not used.
    public static final int[] DB_TRACK_SCHEMA = { 0, 20 };

    public static final int[] INIT_DB_DATA = { 0, 10 };
    // Increment percentage complete with each call by this amount
    public static final int INIT_DB_DATA_STEP = INIT_DB_DATA[1] / 8;

    public static final int[] RESOURCE_UPGRADE = { 0, 60 };

    public static final int[] DB_DATA_UPGRADE = { 0, 10 };

    // Increment percentage complete with each call by this amount
    public static final int DB_DATA_UPGRADE_STEP = DB_DATA_UPGRADE[1] / 10;

    public static final String PERCENT_COMPLETE = "PERCENT_COMPLETE";
    public static final String PROGRESS_TEXT = "PROGRESS_TEXT";
    public static final String READY = "READY";

    public static String DB_SCHEMA_UPGRADE_SCRIPT_TEXT = "Executing script ";
    public static String DB_SCHEMA_UPGRADE_READY_TEXT = "Database schema is up to date";// "starter.dbSchema"
    public static String INIT_DB_DATA_TEXT = "Initialize database data";// "starter.initData"
    public static String RESOURCE_UPGRADE_LOCALE_TEXT = "Loading resource for locale ";// "starter.resourceForLocale"
    public static String RESOURCE_UPGRADE_DEFAULT_LOCALE_TEXT = "Loading resources for default locale";// "starter.resourceForDefaultLocale"
    public static String RESOURCE_UPGRADE_READY_TEXT = "Localized resources are loaded ";// "starter.resource"
    public static String DATA_UPGRADE_TO_TEXT = "Migrating data to ";// "starter.dbDataUpgradeTo"
    public static String DATA_UPGRADE_READY_TEXT = "Migrating data ready";// "starter.dbDataUpgrade"
    public static String REPORT_COPY_READY_TEXT = "Copying report templates...";// "starter.reportTemplates"
    public static String READY_TEXT = "starter.ready";
    public static String PLEASE_TEXT = "Please give us a moment...";
    public static String WAIT_TEXT = "to initialize the system. This can take a few minutes.";
    public static String TITLE = "Application initializing";

    private int actualProgress = 0;

    private static final int REPORT_COPY = 2;// resource upgrade is ready

    private SiteDAO siteDAO = DAOFactory.getFactory().getSiteDAO();

    private transient ExecutorService executor = null;

    private ServletConfig servletConfig = null;

    private ApplicationBean appBean = null;

    private Boolean serverIsReady = false;

    // ---------------------------------------------------- HttpServlet Methods

    /*
     * Hidden constructor for singleton pattern
     */
    private ApplicationStarter() {

    }

    /**
     * Get a singleton instance of this class.
     *
     * @return the one and only ApplicationStarter instance of this JVM.
     */
    public static ApplicationStarter getInstance() {
        if (instance == null) {
            instance = new ApplicationStarter();
        }
        return instance;
    }

    /**
     * Initialize before use.
     *
     * @param scfg
     *            the servlet configuration
     * @param _executor
     *            the executor running everything in its own thread.
     */
    public void init(ServletConfig scfg, ExecutorService pexecutor) {
        setServletConfig(scfg);
        this.executor = pexecutor;
    }

    /*
     * starter.dbSchema=Database schema is up-to-date
     * starter.dbSchemaScript=Executing script {0}...
     * starter.dbDataUpgrade=Migrating data ready
     * starter.dbDataUpgradeTo=Migrating data to {0}...
     * starter.ready=Application is ready starter.resource=Resources are loaded
     * starter.reportTemplates=Copying report templates...
     * starter.resourceForLocale=Loading resource for locale {0}...
     */

    /** Initializes the application */
    @Override
    public void run() {
        setLoaderResourceBundleMessages();
        ServletContext servletContext = getServletConfig().getServletContext();
        appBean = initApplicationBeanStep1(servletContext);

        LOGGER.info("-------------------------------------------------------------");
        LOGGER.info(ApplicationBean.getInstance().getAppTypeString() + ": System initializaton for version "
                + appBean.getVersion() + " build " + appBean.getBuild() + " started...");
        LOGGER.info("TRACKPLUS_HOME set to " + HandleHome.getTrackplus_Home());

        updateOrCreateDbSchema(servletContext);
        TSiteBean site = initDatabaseAndAdjustHome(appBean.getVersion(), servletContext);

        initExtraLoggers(servletContext);

        printSystemInfo();
        Support.loadLastURIs();
        try {
            setColumnSizes();
            InitDatabase.upgradeDatabase(site, servletContext, appBean.getVersion(), appBean.getBuild());
        } catch (Exception e) {
            LOGGER.debug(ExceptionUtils.getStackTrace(e));
            ApplicationBean.getInstance().setInstallProblem(createInstallProblemMessage(e));
            LOGGER.error("Problem when printing system info or upgrading database.");
            emergencyExit(e);
        }
        // Initialize the length of short descriptions in the report page
        initDescriptionLength();
        initEmailSubsystem();
        initCookieTimeout();
        initLDAP();
        initWebService();
        // Update the versions of the application and database scheme.
        updateVersions(servletContext);
        LuceneUtil.initLuceneParameters();

        site = siteDAO.load1();
        appBean = initLicSys(site);

        ClusterBL.processCluster(false);
        String myIp = ClusterBL.getIPAddress();
        if (ApplicationBean.getInetAddress() != null) {
            TClusterNodeBean clusterNodeBean = ClusterBL.loadByIP(myIp);
            appBean.setClusterNodeBean(clusterNodeBean);
        }

        HandleHome.initGroovyPlugins(servletContext);
        Constants.setGroovyScriptEngine();
        servletContext.setAttribute("SITECONFIG", site);

        servletContext.setAttribute(Constants.APPLICATION_BEAN, appBean);

        initJobs(servletContext);

        getDesignPaths(servletContext);
        servletContext.setAttribute("FirstTime", "FT");

        initLucene(myIp);

        EventPublisher evp = EventPublisher.getInstance();
        EventPublisher.init(); // only first time, don't repeat

        if (evp != null) {
            List<Integer> events = new ArrayList<Integer>();
            events.add(Integer.valueOf(IEventSubscriber.EVENT_POST_SYSTEM_STARTED));
            evp.notify(events, site);
        }

        //
        // Verify the reportTemplates
        //
        actualizePercentComplete(ApplicationStarter.REPORT_COPY, ApplicationStarter.REPORT_COPY_READY_TEXT);
        InitReportTemplateBL.verifyReportTemplates();

        // remove stuck locks
        ItemLockBL.removeAllLocks();

        Set<String> fieldTypeResourceBundleNames = FieldTypeDescriptorUtil.getCustomFieldTypeResourceBundles();
        if (fieldTypeResourceBundleNames != null) {
            for (String resourceBundleName : fieldTypeResourceBundleNames) {
                // add the resource bundles specified in the fieldType
                // plugins
                // to the struts' default resource bundles.
                // needed for example to report the conversion errors
                // during configuring the custom fields
                // (by conversion error text there is not possible to
                // specify the bundle explicitly)
                LocalizedTextUtil.addDefaultResourceBundle(resourceBundleName);
            }
        }
        actualizePercentComplete(100 - getPercentComplete(), ApplicationStarter.READY_TEXT);
        String version = site.getTrackVersion();
        LOGGER.info(appBean.getAppTypeString() + " system version " + version + " Build " + appBean.getBuild()
                + " started...");

        // ------------------------------------------------------------
        // Initialize general logging with settings from the database
        initGeneralLogging(servletContext);

        servletContext.setAttribute(READY, new Boolean(true));
        serverIsReady = true;
        try {
            FileMonitor.monitor();
        } catch (Exception e) {
            LOGGER.info(e.getMessage());
        }

        // This is for unit test purposes
        File lock = new File(HandleHome.getTrackplus_Home() + "/lock");
        if (lock.exists()) {
            lock.delete();
        }
        try {
            DetectorFactory
                    .loadProfile(HandleHome.getTrackplus_Home() + File.separator + "LanguageDetectionProfiles");
        } catch (Exception e) {
            LOGGER.warn("Unable to load language profiles: " + e.getMessage());
        }
    }

    // -------------------- End of startup, details
    // follow----------------------------- //

    /**
     * This method updates the percent complete bar shown during system startup
     *
     * @param percentComplete
     * @param progressText
     */
    public void actualizePercentComplete(Integer percentComplete, String progressText) {
        actualProgress = actualProgress + percentComplete;
        ApplicationBean.getInstance().getServletContext().setAttribute(PERCENT_COMPLETE, actualProgress);
        ApplicationBean.getInstance().getServletContext().setAttribute(PROGRESS_TEXT, progressText);
    }

    /**
     * Get the progress text shown during system initialization in the browser
     *
     * @return
     */
    public String getProgressText() {
        ServletContext scx = ApplicationBean.getInstance().getServletContext();
        if (scx != null) {
            return (String) scx.getAttribute(PROGRESS_TEXT);
        }
        return "";
    }

    public int getPercentComplete() {
        ServletContext scx = ApplicationBean.getInstance().getServletContext();
        if (scx != null) {
            Integer pc = (Integer) scx.getAttribute(PERCENT_COMPLETE);
            if (pc != null) {
                return pc;
            }
        }
        return 0;
    }

    private void setLoaderResourceBundleMessages() {
        // request not available yet
        ResourceBundle rb = ResourceBundle.getBundle(ResourceBundleManager.LOADER_RESOURCES, Locale.getDefault()); // getLoaderResourceBundle
        // ResourceBundle.getBundle("resources/UserInterface/LoaderResources",
        // locale)
        DB_SCHEMA_UPGRADE_SCRIPT_TEXT = rb.getString("DB_SCHEMA_UPGRADE_SCRIPT_TEXT");
        // Executing script starter.dbSchemaScript

        DB_SCHEMA_UPGRADE_READY_TEXT = rb.getString("DB_SCHEMA_UPGRADE_READY_TEXT");
        // "Database schema is up to date

        INIT_DB_DATA_TEXT = rb.getString("INIT_DB_DATA_TEXT");
        // Initialize database data starter.initData

        RESOURCE_UPGRADE_LOCALE_TEXT = rb.getString("RESOURCE_UPGRADE_LOCALE_TEXT");
        // Loading resource for locale tarter.resourceForLocale

        RESOURCE_UPGRADE_DEFAULT_LOCALE_TEXT = rb.getString("RESOURCE_UPGRADE_DEFAULT_LOCALE_TEXT");
        // Loading resources for default locale starter.resourceForDefaultLocale

        RESOURCE_UPGRADE_READY_TEXT = rb.getString("RESOURCE_UPGRADE_READY_TEXT");
        // Localized resources are loaded starter.resource

        DATA_UPGRADE_TO_TEXT = rb.getString("DATA_UPGRADE_TO_TEXT");
        // Migrating data to starter.dbDataUpgradeTo

        DATA_UPGRADE_READY_TEXT = rb.getString("DATA_UPGRADE_READY_TEXT");
        // Migrating data ready starter.dbDataUpgrade

        REPORT_COPY_READY_TEXT = rb.getString("REPORT_COPY_READY_TEXT");
        // Copying report templates... starter.reportTemplates

        READY_TEXT = rb.getString("READY_TEXT"); // "starter.ready"
        PLEASE_TEXT = rb.getString("please"); // "starter.ready"
        WAIT_TEXT = rb.getString("waitMinutes"); // "starter.ready"
        TITLE = rb.getString("title"); // "starter.ready"
    }

    public Boolean isServerReady() {
        return serverIsReady;
    }

    // ======================================================================
    // ======================================================================
    // ======================================================================
    /**
     * Gracefully shut down this thread, releasing any resources that were
     * allocated at initialization
     */
    public void destroy() {
        System.out.println("Going down...");
        LOGGER.info("Going down...");

        JobScheduler.stopScheduler();
        FileMonitor.removeFileWatcher();

        appBean.removeClusterNode();

        ApplicationBean.getInstance().getExecutor().shutdown();

        getServletConfig().getServletContext().removeAttribute(Constants.DATABASE_KEY);
        getServletConfig().getServletContext().removeAttribute(Constants.APPLICATION_BEAN);
        getServletConfig().getServletContext().removeAttribute("SITECONFIG");
        // lucene: close all the IndexModifiers to release the locks
        LuceneIndexer.closeWriters();
        // shutdown torque
        try {
            LOGGER.debug("Shutdown torque");
            Torque.shutdown();
        } catch (TorqueException e) {
            LOGGER.error("Stopping torque failed! " + e);
        }

        EventPublisher evp = EventPublisher.getInstance();
        if (evp != null) {
            List<Integer> events = new LinkedList<Integer>();
            events.add(Integer.valueOf(IEventSubscriber.EVENT_PRE_SYSTEM_STOPPED));
            evp.notify(events, null);
        }
        System.out.println("System down!");
    }

    // -----------------------------------------------------------------------
    /*
     * Initializes the logging system
     */
    private void initExtraLoggers(ServletContext servletContext) {
        try {
            String log4j2FileAbsPath = HandleHome.getTrackplus_Home() + File.separator + HandleHome.LOG4J2_FILE;
            log4j2FileAbsPath = URLEncoder.encode(log4j2FileAbsPath, "UTF-8");
            Configurator.initialize(null, log4j2FileAbsPath);
        } catch (Exception e) {
            LOGGER.error(ExceptionUtils.getStackTrace(e));
        }
    }

    /*
     * Initialize general logging with settings from the database
     */
    private void initGeneralLogging(ServletContext servletContext) {
        try {

            TLoggingLevelPeer.load(); // make sure the database settings prevail
            LOGGER.info("Loaded logging settings from database");

        } catch (Exception e) {
            LOGGER.error("Logging initialization failed: " + e.getMessage());
        }
    }

    private ApplicationBean initApplicationBeanStep1(ServletContext servletContext) {
        ApplicationBean applicationBean = ApplicationBean.getInstance();
        setVersionFromVersionProperties(servletContext);
        applicationBean.setServletContext(servletContext);
        applicationBean.setExecutor(executor);
        actualizePercentComplete(0, "");

        if (servletContext.getAttribute("INTEST") != null && (Boolean) servletContext.getAttribute("INTEST")) {
            applicationBean.setInTestMode(true);
        }

        return applicationBean;
    }

    // -------------------------------------------------------------------------
    /*
     * Set the short description length in reports, has historic reasons.
     */
    private void initDescriptionLength() {
        int dlength = 0;
        if (InitDatabase.getFirstTime()) {
            dlength = 160;
            Map<Integer, Object> siteBeanValues = new HashMap<Integer, Object>();
            siteBeanValues.put(TSiteBean.COLUMNIDENTIFIERS.DESCRIPTIONLENGTH, new Integer(dlength));
            siteDAO.loadAndSaveSynchronized(siteBeanValues);
        }
    }

    // --------------------------------------------------------------------
    /*
     *
     */
    private void initEmailSubsystem() {
        Map<Integer, Object> siteBeanValues = new HashMap<Integer, Object>();
        TSiteBean siteBean = siteDAO.load1();
        // --------------------------------------------------------------------
        // Initialize the Genji email account and domain. Under this
        // e-mail account e-mails are being sent
        //
        String sendFrom = null;
        if (InitDatabase.getFirstTime()
                && (siteBean.getTrackEmail() == null || "".equals(siteBean.getTrackEmail()))) {
            sendFrom = "";
            siteBeanValues.put(TSiteBean.COLUMNIDENTIFIERS.TRACKEMAIL, sendFrom);
        }
        sendFrom = siteBean.getTrackEmail();

        // ---------------------------------------------------------------------
        // Initialize the email subsystem
        //
        if (InitDatabase.getFirstTime()) {
            siteBeanValues.put(TSiteBean.COLUMNIDENTIFIERS.SMTPSERVERNAME, "");
            siteBeanValues.put(TSiteBean.COLUMNIDENTIFIERS.SMTPPORT, new Integer(25));
            siteBeanValues.put(TSiteBean.COLUMNIDENTIFIERS.SMTPUSER, "");
            siteBeanValues.put(TSiteBean.COLUMNIDENTIFIERS.SMTPPASSWORD, "");
            siteBeanValues.put(TSiteBean.COLUMNIDENTIFIERS.POPSERVERNAME, "");
            siteBeanValues.put(TSiteBean.COLUMNIDENTIFIERS.POPPORT, new Integer(110));
            siteBeanValues.put(TSiteBean.COLUMNIDENTIFIERS.POPUSER, "");
            siteBeanValues.put(TSiteBean.COLUMNIDENTIFIERS.POPPASSWORD, "");
            siteBeanValues.put(TSiteBean.COLUMNIDENTIFIERS.MAILENCODING, "UTF8");
        }

        // ---------------------------------------------------------------------
        // Initialize the allowed email domain pattern
        //
        String emailPattern = "\\w[\\-.\\w]+\\@\\w[\\-.\\w]+\\.\\w{2,3}";
        if (InitDatabase.getFirstTime()) {
            siteBeanValues.put(TSiteBean.COLUMNIDENTIFIERS.ALLOWEDEMAILPATTERN, emailPattern);
        }

        if (siteBean.getAllowedEmailPattern() == null || "".equals(siteBean.getAllowedEmailPattern())) {
            siteBeanValues.put(TSiteBean.COLUMNIDENTIFIERS.ALLOWEDEMAILPATTERN, emailPattern);

        }
        siteDAO.loadAndSaveSynchronized(siteBeanValues);
        if (InitDatabase.getFirstTime()) {
            siteBean = siteDAO.load1();
            siteBean.setUseLucene("true");
            siteBean.setReindexOnStartup("true");
            siteBean.setIsVersionReminderOn(true);
            siteBean.setSummaryItemsBehavior(true);
        }
    }

    // ------------------------------------------------------------------------
    /*
     * Initializes the cookie timeout for automated authentication.
     */
    private void initCookieTimeout() {
        Constants.setCookieTimeout(24 * 7 * 3600);
    }

    // ------------------------------------------------------------------
    /*
     * Initializes LDAP related stuff
     */
    private void initLDAP() {
        Boolean ldapOn = null;
        if (InitDatabase.getFirstTime()) {
            String sldapOn = "false";
            ldapOn = new Boolean(sldapOn);
            Map<Integer, Object> siteBeanValues = new HashMap<Integer, Object>();
            if (ldapOn.booleanValue() == true) {
                siteBeanValues.put(TSiteBean.COLUMNIDENTIFIERS.ISLDAPON, "Y");
            } else {
                siteBeanValues.put(TSiteBean.COLUMNIDENTIFIERS.ISLDAPON, "N");
            }
            siteBeanValues.put(TSiteBean.COLUMNIDENTIFIERS.LDAPSERVERURL, "");
            siteBeanValues.put(TSiteBean.COLUMNIDENTIFIERS.LDAPATTRIBUTELOGINNAME, "");
            siteDAO.loadAndSaveSynchronized(siteBeanValues);
        }
    }

    // ---------------------------------------------------------------------
    /*
     *
     */
    private void initWebService() {

    }

    /*
     * Print IP address information for this host
     */
    private void printInetAdressInfo() {

        InetAddress[] allAds = ApplicationBean.getInetAddress();
        if (allAds == null || allAds.length == 0) {
            LOGGER.error("No IP addresses found for this host!");
            LOGGER.error("Most likely your DNS configuration is faulty.");
        }

        LOGGER.info("All IP addresses found for this host: ");
        for (int i = 0; i < allAds.length; ++i) {
            LOGGER.info("IP[" + i + "] = " + allAds[i].getHostAddress());
        }
    }

    // ---------------------------------------------------------------
    /*
     * Initialize the licensing subsystem.
     */
    private ApplicationBean initLicSys(TSiteBean site) {
        ApplicationBean applicationBean = ApplicationBean.getInstance();
        try {
            Class<LicenseManager> clazz = (Class<LicenseManager>) Class
                    .forName("com.trackplus.license.LicenseManagerImpl");
            if (clazz != null) {
                Class<Object>[] args = null;
                Object[] params = null;
                Method m = clazz.getDeclaredMethod("getInstance", args);
                LicenseManager lm = (LicenseManager) m.invoke(null, params);
                applicationBean.setLicenseManager(lm);
            }
        } catch (Exception e) {
            // This is quite normal, the plug-in may not be there.
        }
        if (InitDatabase.getFirstTime()) {
            Map<Integer, Object> siteBeanValues = new HashMap<Integer, Object>();
            siteBeanValues.put(TSiteBean.COLUMNIDENTIFIERS.LICENSEKEY, "");
            siteBeanValues.put(TSiteBean.COLUMNIDENTIFIERS.NUMBEROFUSERS, new Integer(5));
            try {
                siteBeanValues.put(TSiteBean.COLUMNIDENTIFIERS.EXPDATE,
                        DateTimeUtils.getInstance().parseISODate("2012-12-01"));
            } catch (Exception e) {
                LOGGER.error("Problem converting license expiration date");
            }
            siteDAO.loadAndSaveSynchronized(siteBeanValues);
        }

        applicationBean.setActualUsers();
        printInetAdressInfo();

        if (site.getInstDate() == null || "".equals(site.getInstDate())) {
            site.setInstDate(new Long(new Date().getTime()).toString());
        }
        try {
            applicationBean.setInstDate(new Long(site.getInstDate()).longValue());
        } catch (Exception e) {
            LOGGER.info("Setting install date to " + new Date());
        }

        try {
            applicationBean.initLic(site.getLicenseExtension());
        } catch (Exception ex) {
            LOGGER.error(ExceptionUtils.getStackTrace(ex));
        }

        site.setNumberOfUsers(new Integer(
                applicationBean.getMaxNumberOfFullUsers() + applicationBean.getMaxNumberOfLimitedUsers()));
        site.setNumberOfFullUsers(applicationBean.getMaxNumberOfFullUsers());
        site.setNumberOfLimitedUsers(applicationBean.getMaxNumberOfLimitedUsers());
        if (applicationBean.getLicenseHolder() != null) {
            site.setLicenseHolder(applicationBean.getLicenseHolder());
        }
        site.setExpDate(applicationBean.getExpDate());

        applicationBean.setSiteParams(site);
        applicationBean.setSiteBean(site);

        getServletConfig().getServletContext().setAttribute(Constants.APPLICATION_BEAN, applicationBean);
        return applicationBean;
    }

    // -----------------------------------------------------------------------
    /**
     * Initialize the Lucene indexing system.
     *
     * @param myIp
     * @param indexing
     *            whether to initialize for both indexing and searching or only
     *            searching
     */
    private void initLucene(String myIp, boolean indexing) {
        // init lucene
        // get found analysers list
        List<LabelValueBean> analyzerList = LuceneUtil.getAnalyzersList(getServletConfig().getServletContext());
        LuceneUtil.setFoundAnalysers(analyzerList);
        // get config from database
        TSiteBean siteBean = siteDAO.load1();
        LuceneUtil.configLuceneParameters(siteBean);
        // if lucene is active
        if (indexing && LuceneUtil.isUseLucene()) {
            LuceneIndexer luceneIndexer = new LuceneIndexer();
            ApplicationBean.getInstance().setLuceneIndexer(luceneIndexer);
            if (LuceneUtil.isReindexOnStartup()) {
                LOGGER.debug("Reindex on startup on " + myIp);
                // reindex in a new thread (the modifiers are initialized inside
                // run)
                ApplicationBean.getInstance().getExecutor().execute(luceneIndexer);
            } else {
                // initialize index for modifications
                try {
                    LuceneIndexer.initIndexWriters(false);
                    LOGGER.debug("Init lucene on append mode " + myIp);
                } catch (Exception e) {
                    LOGGER.debug(ExceptionUtils.getStackTrace(e));
                }
            }
        }

    }

    private void updateVersions(ServletContext servletContext) {

        try {
            LabelValueBean versionBean = new LabelValueBean(appBean.getVersion(), "");
            LabelValueBean versionDateBean = new LabelValueBean(appBean.getVersionDate(), "");
            // Update the version in the database
            Map<Integer, Object> siteBeanValues = new HashMap<Integer, Object>();
            siteBeanValues.put(TSiteBean.COLUMNIDENTIFIERS.TRACKVERSION, appBean.getVersion());
            siteDAO.loadAndSaveSynchronized(siteBeanValues);

            servletContext.setAttribute("TVERSION", versionBean);
            servletContext.setAttribute("TVERSIONDATE", versionDateBean);

        } catch (Exception e) {
            // no version
            servletContext.setAttribute("TVERSION", new LabelValueBean("4.X", ""));
            servletContext.setAttribute("TVERSIONDATE", new LabelValueBean("?", ""));
        }

    }

    private void setVersionFromVersionProperties(ServletContext servletContext) {
        ApplicationBean applicationBean = ApplicationBean.getInstance();
        String appTypeString = "";
        Integer appType = -1;
        String theVersion = "";
        String theBuild = "";
        String theVersionDate = "";
        Integer theVersionNo = 370;
        try {
            URL versionURL = servletContext.getResource("/WEB-INF/Version.properties");
            PropertiesConfiguration vcfg = new PropertiesConfiguration();
            InputStream in = versionURL.openStream();
            vcfg.load(in);
            theVersion = (String) vcfg.getProperty("version");
            theBuild = (String) vcfg.getProperty("build");
            if (theVersion == null) {
                theVersion = "4.X";
            }
            if (theBuild == null) {
                theBuild = "1";
            }
            theVersionDate = (String) vcfg.getProperty("date");
            if (theVersionDate == null) {
                theVersionDate = "2015-01-01";
            }
            theVersionNo = new Integer((String) vcfg.getProperty("app.version"));
            try {
                appType = new Integer((String) vcfg.getProperty("ntype"));
            } catch (Exception e) {
                appType = ApplicationBean.APPTYPE_FULL;
            }

            appTypeString = (String) vcfg.getProperty("type");
            if (appTypeString == null) {
                appTypeString = "Genji";
            }
        } catch (Exception e) {
            theVersion = "1.x";
            theBuild = "0";
            theVersionDate = "2015-01-01";
        }
        applicationBean.setServletContext(servletContext);
        applicationBean.setVersion(theVersion);
        applicationBean.setVersionNo(theVersionNo);
        applicationBean.setBuild(theBuild);
        applicationBean.setVersionDate(theVersionDate);
        applicationBean.setAppType(appType);
        applicationBean.setAppTypeString(appTypeString);

    }

    // ----------------------------------------------------------------------
    /**
     * Gets the list of graphical skins defined
     *
     * @return
     */
    private static void getDesignPaths(ServletContext servletContext) {
        List<LabelValueBean> designPaths = new ArrayList<LabelValueBean>();
        String designDirectoryName = "design";
        File designDirectory = PluginUtils.getResourceFileFromWebAppRoot(servletContext, designDirectoryName);
        if (designDirectory == null || !designDirectory.exists() || !designDirectory.isDirectory()) {
            designPaths.add(new LabelValueBean(Constants.DEFAULTDESIGNPATH, Constants.DEFAULTDESIGNPATH));
        } else {
            File[] files = designDirectory.listFiles();
            for (int i = 0; i < files.length; i++) {
                if (files[i].isDirectory()) {
                    designPaths.add(new LabelValueBean(files[i].getName(), files[i].getName()));
                }
            }
        }
        ApplicationBean.getInstance().setDesigns(designPaths);
    }

    private void printSystemInfo() {
        LOGGER.info("Java: " + System.getProperty("java.vendor") + " " + System.getProperty("java.version"));
        LOGGER.info("Operating System: " + System.getProperty("os.name") + " " + System.getProperty("os.arch"));
        Locale loc = Locale.getDefault();
        LOGGER.info("Default locale: " + loc.getDisplayName());

        ServletContext application = ApplicationBean.getInstance().getServletContext();
        try {
            LOGGER.info("Servlet real path: " + application.getRealPath(File.separator));
        } catch (Exception ex) {
            LOGGER.error("Error trying to obtain getRealPath()");
        }
        LOGGER.info("Servlet container: " + application.getServerInfo());

        Connection conn = null;
        try {
            PropertiesConfiguration pc = ApplicationBean.getInstance().getDbConfig();
            LOGGER.info("Configured database type: " + pc.getProperty("torque.database.track.adapter"));
            LOGGER.info(
                    "Configured database driver: " + pc.getProperty("torque.dsfactory.track.connection.driver"));
            LOGGER.info("Configured JDBC URL: " + pc.getProperty("torque.dsfactory.track.connection.url"));
            conn = Torque.getConnection(BaseTSitePeer.DATABASE_NAME);
            DatabaseMetaData dbm = conn.getMetaData();
            LOGGER.info("Database type: " + dbm.getDatabaseProductName() + " " + dbm.getDatabaseProductVersion());
            LOGGER.info("Driver info:   " + dbm.getDriverName() + " " + dbm.getDriverVersion());
            Statement stmt = conn.createStatement();
            Date d1 = new Date();
            stmt.executeQuery("SELECT * FROM TSTATE");
            Date d2 = new Date();
            stmt.close();
            LOGGER.info("Database test query done in " + (d2.getTime() - d1.getTime()) + " milliseconds ");
        } catch (Exception e) {
            System.err.println("Problem retrieving meta data");
            LOGGER.error("Problem retrieving meta data");
        } finally {
            if (conn != null) {
                Torque.closeConnection(conn);
            }
        }
    }

    private void setColumnSizes() {
        Connection conn = null;
        int descSize = 32000;// Firebird
        int cSize = 10000;// Firebird
        int sizeMySQL = 16777215;
        Statement stmt = null;
        ResultSet rs = null;
        try {
            conn = Torque.getConnection(BaseTSitePeer.DATABASE_NAME);
            stmt = conn.createStatement();
            stmt.setMaxRows(2);
            rs = stmt.executeQuery("SELECT PACKAGEDESCRIPTION FROM TWORKITEM");
            if (rs != null) {
                descSize = rs.getMetaData().getColumnDisplaySize(1);
                if (descSize <= 0) {
                    // MySQL bug (MySql 5.0.26 for linux returns -1)
                    System.err.println("Incorrect maximum description length retrieved: " + descSize);
                    LOGGER.debug("Incorrect maximum description length retrieved: " + descSize);
                    descSize = sizeMySQL;
                }
                rs.close();
            }
            rs = stmt.executeQuery("SELECT CHANGEDESCRIPTION FROM TSTATECHANGE");
            if (rs != null) {
                cSize = rs.getMetaData().getColumnDisplaySize(1);
                if (cSize <= 0) {
                    // MySQL bug (MySql 5.0.26 for linux returns -1)
                    System.err.println("Incorrect maximum comment length retrieved: " + cSize);
                    LOGGER.debug("Incorrect maximum comment length retrieved: " + cSize);
                    cSize = sizeMySQL;
                }
            }
        } catch (Exception e) {
            LOGGER.debug(e);
            System.err.println("Could not retrieve column sizes from database: " + e.getMessage());
        } finally {
            if (rs != null) {
                try {
                    rs.close();
                } catch (SQLException e) {
                    LOGGER.info("Closing the resultset failed with " + e.getMessage());
                    LOGGER.debug(ExceptionUtils.getStackTrace(e));
                }
            }
            if (stmt != null) {
                try {
                    stmt.close();
                } catch (SQLException e) {
                    LOGGER.info("Closing the statement failed with " + e.getMessage());
                    LOGGER.debug(ExceptionUtils.getStackTrace(e));
                }
            }
            if (conn != null) {
                Torque.closeConnection(conn);
            }
        }
        LOGGER.info("Maximum description length: " + descSize);
        LOGGER.info("Maximum comment length: " + cSize);
        ApplicationBean.getInstance().setDescriptionMaxLength(descSize);
        ApplicationBean.getInstance().setCommentMaxLength(cSize);
    }

    private void emergencyExit(Exception e) {
        // We need this to show a proper install problem page
        ApplicationBean applicationBean = ApplicationBean.getInstance();
        getServletConfig().getServletContext().setAttribute(Constants.APPLICATION_BEAN, applicationBean);
        PropertiesConfiguration pc = ApplicationBean.getInstance().getDbConfig();

        LOGGER.error("Something went wrong here.");
        LOGGER.error("Please have a look at the previous error messagesand stack traces.");
        LOGGER.error("Most likely the database connection does not work.");
        LOGGER.error("Please check if the user name, password and JDBC URL in the ");
        LOGGER.error("WEB-INF/Torque.properties file are set correctly.");
        LOGGER.error("Please check that you have only one Database type enabled in the");
        LOGGER.error("Torque.properties file. Please also check that the database server");
        LOGGER.error("is running and is accessible from this machine. If the database server is");
        LOGGER.error("running on a different machine, check that there are no firewall issues.");
        LOGGER.error("Please also check that you have run all SQL scripts to set up the database.");
        LOGGER.error("");
        LOGGER.error("For your information, your settings in Torque.properties are: ");
        LOGGER.error("Database user name: " + pc.getProperty("torque.dsfactory.track.connection.user"));
        String password = (String) pc.getProperty("torque.dsfactory.track.connection.password");
        if (password != null) {
            password = password.replaceAll(".", "*");
            LOGGER.error("Database password:  " + password + "(see Torque.properties file)");
        }
        LOGGER.error("Database type: " + pc.getProperty("torque.database.track.adapter"));
        LOGGER.error("Database JDBC driver: " + pc.getProperty("torque.dsfactory.track.connection.driver"));
        LOGGER.error("Database connection URL: " + pc.getProperty("torque.dsfactory.track.connection.url"));
        LOGGER.error("Exiting...");
        LOGGER.error("");
        LOGGER.error("");
        LOGGER.error(ExceptionUtils.getStackTrace(e));
    }

    private List<String> createInstallProblemMessage(Exception e) {
        List<String> msg = new ArrayList<String>();
        try {
            PropertiesConfiguration pc = ApplicationBean.getInstance().getDbConfig();
            msg.add("Database user name: (see Torque.properties file)");
            msg.add("Database password:  (see Torque.properties file)");
            msg.add("Database type: " + pc.getProperty("torque.database.track.adapter"));
            msg.add("Database JDBC driver: " + pc.getProperty("torque.dsfactory.track.connection.driver"));
            msg.add("Database connection URL: " + pc.getProperty("torque.dsfactory.track.connection.url"));
            msg.add("The system gives this error message: " + e.getMessage());
        } catch (Exception ex) {
            LOGGER.debug(ex);
            // Nothing to be done about this here
        }
        return msg;
    }

    public void setServletConfig(ServletConfig ctx) {
        servletConfig = ctx;
    }

    public ServletConfig getServletConfig() {
        return servletConfig;
    }

    private void updateOrCreateDbSchema(ServletContext servletContext) {
        try {
            //
            boolean dbUpdateOrCreateSuccessfull = UpdateDbSchema.doUpdateOrCreateFromScratch(servletContext);
            if (!dbUpdateOrCreateSuccessfull) {
                // Unfortunately we do not have a database connection
                // so we try to copy the configuration file to TRACKPLUS_HOME
                // before we terminate the attempt to start
                HandleHome.copyTorquePropertiesToHome(servletContext);
            }
        } catch (Exception e) {
            LOGGER.error("Problem creating or updating database schema: " + e.getMessage());
        }
    }

    private TSiteBean initDatabaseAndAdjustHome(String version, ServletContext servletContext) {
        TSiteBean site = null;
        try {
            site = InitDatabase.initDatabase(version, servletContext);
            HandleHome.initTrackplus_Home(servletContext);
        } catch (Exception e) {
            LOGGER.debug(ExceptionUtils.getStackTrace(e));
            ApplicationBean.getInstance().setInstallProblem(createInstallProblemMessage(e));
            emergencyExit(e);
        }
        return site;
    }

    private void initJobs(ServletContext servletContext) {
        // finally starting the Quartz scheduler ...
        LOGGER.info("Initializing Quartz scheduler...");

        JobScheduler.init(servletContext, ApplicationBean.getInstance().getDbConfig());
        JobScheduler.startScheduler();
        // update Database backup job
        TSiteBean site = ApplicationBean.getInstance().getSiteBean();
        String backupTime = site.getBackupTime();
        List<Integer> backupOnDays = site.getBackupOnDays();
        if (backupTime != null && backupOnDays != null) {
            DatabaseBackupBL.setDBJobCronExpression(backupTime, backupOnDays);
        } else {
            backupTime = "23:15:00";
            backupOnDays = new ArrayList<Integer>();
            for (int i = 2; i <= 6; i++) {
                backupOnDays.add(i);
            }
            site.setBackupTime(backupTime);
            site.setBackupOnDays(backupOnDays);
            siteDAO.save(site);
        }
    }

    public void initLucene(String myIp) {
        // --------------------------------------------------------------------
        // Initialize Lucene indexing system.
        //
        if (ClusterBL.isCluster()) {
            if (ClusterBL.isSharedLuceneIndex()) {
                if (ClusterBL.getIAmTheMaster()) {
                    // only the master might write on index
                    initLucene(myIp, true);
                } else {
                    // the other nodes only read
                    initLucene(myIp, false);
                }
            } else {
                // each node has own index files
                initLucene(myIp, true);
            }
            ClusterUpdateBL.updateClusterNode();
        } else {
            // no cluster, single instance node
            initLucene(myIp, true);
        }
    }
}