com.aurel.track.dbase.HandleHome.java Source code

Java tutorial

Introduction

Here is the source code for com.aurel.track.dbase.HandleHome.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.dbase;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.URL;
import java.security.DigestInputStream;
import java.security.MessageDigest;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;

import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.xml.bind.DatatypeConverter;

import org.apache.commons.configuration.PropertiesConfiguration;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.tools.zip.ZipEntry;
import org.apache.tools.zip.ZipFile;

import com.aurel.track.Constants;
import com.aurel.track.GeneralSettings;
import com.aurel.track.prop.ApplicationBean;
import com.aurel.track.util.PluginUtils;

public class HandleHome {

    public final static String TRACKPLUS_HOME = "TRACKPLUS_HOME";
    public final static String LATEX_HOME = "LATEX_HOME";
    public final static String IMAGEMAGICK_HOME = "IMAGEMAGICK_HOME";
    public final static String PHANTOMJS_HOME = "PHANTOMJS_HOME";
    public final static String USER_LEVELS_FILE = "UserLevels.properties";
    public final static String GENERAL_SETTINGS_FILE = "GeneralSettings.properties";
    public final static String FILTER_SUBSCRIPTIONS_FILE = "FilterSubscriptions.properties";
    public final static String PDF_EXCEL_EXPORT_FILE = "PdfExcelExport.properties";
    public final static String TORQUE_FILE = "Torque.properties";
    public final static String QUARTZ_JOBS_FILE = "quartz-jobs.xml";
    public final static String LOG4J2_FILE = "log4j2.xml";

    //first label directories in TRACKPLUS_HOME
    public static final String DB_BACKUP_DIR = "dbBackup";
    public final static String LOG_DIR = "log";
    public final static String LOGOS_DIR = "logos";
    public final static String PLUGINS_DIR = "plugins";
    public static final String REPORT_TEMPLATES_DIR = "reportTemplates";
    public static final String WORD_TEMPLATES_DIR = "ExportTemplates" + File.separator + "wordTemplates";
    public static final String LATEX_TEMPLATES_DIR = "ExportTemplates" + File.separator + "latexTemplates";
    public static final String LANGUAGE_PROFILES_DIR = "LanguageDetectionProfiles";
    public static final String TEMP_DIR = "tmp";
    public static final String DATA_DIR = "trackdata";
    public final static String XRESOURCES_DIR = "xresources";
    public final static String SSO_DIR = "SSO";
    public final static String GANTT_EXPORT_RENDERER = "GanttExportRenderer";

    public final static String INIT_DATA = "/initData";
    private static String Trackplus_Home = null;

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

    private HandleHome() {
    }

    /**
     * Initialize the pointer to the TRACKPLUS_HOME directory and copy
     * the database connection configuration file there if required.
     * @param context
     * @throws ServletException
     */
    public static void initTrackplus_Home(ServletContext context) throws ServletException {
        String home = getTrackplus_Home();
        LOGGER.info("Server current working directory is " + System.getProperty("user.dir"));
        copyPropertiesFile(context, TORQUE_FILE);
        copyPropertiesFile(context, FILTER_SUBSCRIPTIONS_FILE);
        copyPropertiesFile(context, GENERAL_SETTINGS_FILE);
        copyPropertiesFile(context, PDF_EXCEL_EXPORT_FILE);
        GeneralSettings.loadGeneralConfigs();
        copyAndMergeQuartzJobsFile(context, QUARTZ_JOBS_FILE);
        copyAndMergeLog4j2File(context, LOG4J2_FILE);

        copyObject(context, "resources/MailTemplates", "tracklogo.gif", LOGOS_DIR); // old mail templates

        String logoSourceDir = INIT_DATA;

        switch (ApplicationBean.getInstance().getAppType()) {
        case ApplicationBean.APPTYPE_FULL:
            logoSourceDir = logoSourceDir + "/full";
            break;
        case ApplicationBean.APPTYPE_DESK:
            logoSourceDir = logoSourceDir + "/desk";
            break;
        case ApplicationBean.APPTYPE_BUGS:
            logoSourceDir = logoSourceDir + "/bugs";
            break;
        default:
            logoSourceDir = logoSourceDir + "/full";
        }

        LOGGER.info("Init data directory is " + logoSourceDir);

        logoSourceDir = logoSourceDir + "/Logos";

        copyObject(context, logoSourceDir, "trackLogo.png", LOGOS_DIR);
        copyObject(context, logoSourceDir, "logo-254x105.png", LOGOS_DIR); // report templates
        copyObject(context, logoSourceDir, "logo-98x40.png", LOGOS_DIR); // mail templates
        copyObject(context, logoSourceDir, "logo-68x28.png", LOGOS_DIR); // tool bar

        moveWordTemplates();
        copyExportTemplates(context, WORD_TEMPLATES_DIR);
        copyExportTemplates(context, LATEX_TEMPLATES_DIR);

        copyFAQTemplates(context);
        copySSOFolder(context);

        copyLanguageProfiles(context, LANGUAGE_PROFILES_DIR);

        File pluginDir = new File(home + File.separator + HandleHome.PLUGINS_DIR);
        PluginUtils.extractArchiveFromClasspath("/WEB-INF/classes/plugins", ".*\\.tpx", pluginDir);
        PluginUtils.addPluginLocationsToClassPath(home);
        //init torque again to get the database access for external databases configured in plugins (included before in classpath. see addPluginLocationsToClassPath)
        InitDatabase.initTorque();
    }

    /**
     * Copy the Torque.properties file to the TRACKPLUS_HOME directory
     * @param context
     * @throws ServletException
     */
    public static void copyTorquePropertiesToHome(ServletContext context) throws ServletException {
        copyPropertiesFile(context, TORQUE_FILE);
    }

    private static void copyExportTemplates(ServletContext context, String templateBaseDir) {
        String tpHome = getTrackplus_Home();
        File templatesDir = new File(tpHome + File.separator + templateBaseDir);

        URL rootTillFolder = null;
        String rootPathTillFolder = null;
        String templatePath = templateBaseDir.replace("\\", "/");
        try {
            if (context.getResource("/WEB-INF/classes/resources/" + templatePath) != null) {
                rootTillFolder = context.getResource("/WEB-INF/classes/resources/" + templatePath);
            } else if (context.getResource("/WEB-INF/classes/" + templatePath) != null) {
                rootTillFolder = context.getResource("/WEB-INF/classes/" + templatePath);
            } else {
                rootTillFolder = new URL(context.getRealPath("../."));
            }
        } catch (IOException ioEx) {
            LOGGER.error(ExceptionUtils.getStackTrace(ioEx));
        }
        if (rootTillFolder != null) {
            rootPathTillFolder = rootTillFolder.getPath();
            if (rootPathTillFolder.contains("/WEB-INF")) {
                rootPathTillFolder = rootPathTillFolder.substring(rootPathTillFolder.indexOf("/WEB-INF"),
                        rootPathTillFolder.length());
            }
            Set<String> folderContent = context.getResourcePaths(rootPathTillFolder);
            if (folderContent != null) {
                for (String fileNameWithPath : folderContent) {
                    String fileName = fileNameWithPath.replace("/WEB-INF/classes/resources/" + templatePath + "/",
                            "");
                    if (fileName.endsWith(".docx") || fileName.endsWith(".tex") || fileName.endsWith(".jpg")
                            || fileName.endsWith(".png") || fileName.endsWith(".tlx") || fileName.endsWith(".sh")
                            || fileName.endsWith(".cmd") || fileName.endsWith(".pdf")) {
                        try {
                            copyObject(context, "resources/" + templatePath, fileName, templateBaseDir);
                        } catch (ServletException servEx) {
                            LOGGER.error(ExceptionUtils.getStackTrace(servEx));
                        }
                    }

                    if (fileName.endsWith(".sh") || fileName.endsWith(".cmd")) {
                        File fileToCopyInHome = new File(
                                tpHome + File.separator + templateBaseDir + File.separator + fileName);
                        fileToCopyInHome.setExecutable(true);
                    }

                    if (fileName.endsWith(".zip") || fileName.endsWith(".tlx")) {
                        try {
                            File fileToCopyInHome = new File(
                                    tpHome + File.separator + templateBaseDir + File.separator + fileName);
                            copyObject(context, "resources/" + templatePath, fileName, templateBaseDir);
                            File fileToUnzip = new File(
                                    tpHome + File.separator + templateBaseDir + File.separator + fileName);
                            PluginUtils.unzipFileIntoDirectory(fileToUnzip, templatesDir);
                        } catch (ServletException servEx) {
                            LOGGER.error(ExceptionUtils.getStackTrace(servEx));
                        }
                    }
                }
            }
        }
    }

    private static void copyLanguageProfiles(ServletContext context, String templateBaseDir) {
        String tpHome = getTrackplus_Home();
        File templatesDir = new File(tpHome + File.separator + templateBaseDir);

        URL rootTillFolder = null;
        String rootPathTillFolder = null;
        String templatePath = templateBaseDir.replace("\\", "/");
        try {
            if (context.getResource("/WEB-INF/classes/resources/" + templatePath) != null) {
                rootTillFolder = context.getResource("/WEB-INF/classes/resources/" + templatePath);
            } else if (context.getResource("/WEB-INF/classes/" + templatePath) != null) {
                rootTillFolder = context.getResource("/WEB-INF/classes/" + templatePath);
            } else {
                rootTillFolder = new URL(context.getRealPath("../."));
            }
        } catch (IOException ioEx) {
            LOGGER.error(ExceptionUtils.getStackTrace(ioEx));
        }
        if (rootTillFolder != null) {
            rootPathTillFolder = rootTillFolder.getPath();
            if (rootPathTillFolder.contains("/WEB-INF")) {
                rootPathTillFolder = rootPathTillFolder.substring(rootPathTillFolder.indexOf("/WEB-INF"),
                        rootPathTillFolder.length());
            }
            Set<String> folderContent = context.getResourcePaths(rootPathTillFolder);

            for (String fileNameWithPath : folderContent) {
                String fileName = fileNameWithPath.replace("/WEB-INF/classes/resources/" + templatePath + "/", "");
                try {
                    copyObject(context, "resources/" + templatePath, fileName, templateBaseDir);
                } catch (ServletException servEx) {
                    LOGGER.error(ExceptionUtils.getStackTrace(servEx));
                }
            }
            try {
                FileUtils.deleteQuietly(new File(HandleHome.getTrackplus_Home() + File.separator
                        + LANGUAGE_PROFILES_DIR + File.separator + "hash.txt"));
            } catch (Exception e) {
                LOGGER.error(e.getMessage());
            }
        }
    }

    /**
     * Returns the path to the TRACKPLUS_HOME directory, where
     * configuration data is being kept.
     * @return
     */
    public static String getTrackplus_Home() {
        if (Trackplus_Home == null) {
            Connection con = null;
            Trackplus_Home = ""; // block  further processing from here by null length path
            // initialize from environment
            if (System.getProperty(TRACKPLUS_HOME) != null) { // first look at -DTRACKPLUS_HOME
                Trackplus_Home = System.getProperty(TRACKPLUS_HOME);
                LOGGER.info(
                        "Taking initial TRACKPLUS_HOME from Java VM argument -DTRACKPLUS_HOME=" + Trackplus_Home);
            } else if (System.getenv(TRACKPLUS_HOME) != null) {
                Trackplus_Home = System.getenv(TRACKPLUS_HOME); // then look at environ. variable
                LOGGER.info("Taking TRACKPLUS_HOME from environment variable as " + Trackplus_Home);
            } else {
                try {
                    PropertiesConfiguration tcfg = loadServletContextPropFile(TORQUE_FILE,
                            ApplicationBean.getInstance().getServletContext());
                    Class.forName(tcfg.getString("torque.dsfactory.track.connection.driver"));
                    con = DriverManager.getConnection(tcfg.getString("torque.dsfactory.track.connection.url"),
                            tcfg.getString("torque.dsfactory.track.connection.user"),
                            tcfg.getString("torque.dsfactory.track.connection.password"));
                    Statement stmt = con.createStatement();
                    ResultSet rs = stmt.executeQuery("SELECT ATTACHMENTROOTDIR FROM TSITE");
                    while (rs.next()) {
                        Trackplus_Home = rs.getString(1);
                    }
                } catch (Exception e) {
                    LOGGER.error("Cannot access database table TSITE");
                } finally {
                    if (con != null) {
                        try {
                            con.close();
                        } catch (SQLException e) {
                            LOGGER.info("Closing the connection failed with " + e.getMessage());
                            LOGGER.debug(ExceptionUtils.getStackTrace(e));
                        }
                    }
                }
            }
            if (Trackplus_Home != null && !"".equals(Trackplus_Home)) {
                File tph = new File(Trackplus_Home);
                if (!tph.exists()) { // try to create it
                    try {
                        tph.mkdirs();
                    } catch (Exception e) {
                        LOGGER.debug("We could not create directory " + Trackplus_Home + ".");
                    }
                }
                if (!tph.isDirectory() || !tph.canWrite()) { // we need to be able to write to this directory
                    LOGGER.error("We cannot write to the given TRACKPLUS_HOME directory at " + Trackplus_Home
                            + ". Ignoring environment.");
                    Trackplus_Home = ""; // block again by null length path
                }
            }

            if (Trackplus_Home == null || "".equals(Trackplus_Home)) {
                Trackplus_Home = System.getProperty("user.home") + File.separator + "trackplus";
                LOGGER.info("Creating TRACKPLUS_HOME at " + Trackplus_Home);
            }
        }
        return Trackplus_Home;
    }

    /**
     * Set the TRACKPLUS_HOME directory, where configuration data is being kept.
     * @param home
     */
    public static void setTrackplus_Home(String home) {
        LOGGER.info("Setting TRACKPLUS_HOME to " + home);
        File f = new File(home);
        try {
            if (!f.exists()) {
                new File(home).mkdirs();
                LOGGER.info("Created new directory for TRACKPLUS_HOME: " + home);
                Trackplus_Home = home;
            }
        } catch (Exception e) {
            LOGGER.error(e.getMessage());
        }
    }

    /**
     * Get the path to the Groovy plugins
     * @return
     */
    public static String getGroovyPluginPath() {
        return getTrackplus_Home() + File.separator + PLUGINS_DIR + File.separator + "groovy";
    }

    /**
     * Get the path to the workflow graphs temporary directory
     * @return
     */

    /*
     * Cleans up the workflow directory from any old pictures that might
     * not have been caught by the cleanup when the session was closed.
     */

    /**
     * Obtain the Torque.properties from TRACKPLUS_HOME or if not available from the WAR.
     * @return
     * @throws ServletException
     */
    public static PropertiesConfiguration getTorqueProperties(ServletContext servletContext,
            boolean includePluginDatasources) throws ServletException {
        return getTorqueProperties(servletContext, TORQUE_FILE, includePluginDatasources);
    }

    /**
     * Load the properties from the fileName properties file in TRACKPLUS_HOME, or if not available from the WAR.
     * @return
     * @throws ServletException
     */
    public static PropertiesConfiguration getProperties(String fileName, ServletContext servletContext)
            throws ServletException {
        return loadProperties(fileName, servletContext);
    }

    /**
     * Obtain the Torque.properties from TRACKPLUS_HOME or if not available from the WAR.
     * @return
     * @throws ServletException
     */
    private static HashMap<String, PropertiesConfiguration> propConfigurations = new HashMap<String, PropertiesConfiguration>();

    /**
     * This is a cached access!
     * @param propFile
     * @param servletContext
     * @return
     * @throws ServletException
     */
    public static PropertiesConfiguration loadProperties(String propFile, ServletContext servletContext)
            throws ServletException {
        if (propConfigurations.get(propFile) != null) {
            return propConfigurations.get(propFile);
        }
        PropertiesConfiguration pc = getTrackplusHomePropFile(propFile);
        if (pc == null && servletContext != null) { // if we could not get it from the environment
            pc = loadServletContextPropFile(propFile, servletContext);
        }
        propConfigurations.put(propFile, pc);
        return pc;
    }

    public static PropertiesConfiguration reloadProperties(String propFile, ServletContext servletContext)
            throws ServletException {
        PropertiesConfiguration pc = getTrackplusHomePropFile(propFile);
        if (pc == null && servletContext != null) { // if we could not get it from the environment
            pc = loadServletContextPropFile(propFile, servletContext);
        }
        propConfigurations.put(propFile, pc);
        return pc;
    }

    /**
     * Gets the PropertiesConfiguration for a property file from TRACKPLUS_HOME
     * @param propFile
     * Load a properties file from the class path
     * @param propFile
     * @param servletContext
     * @return
     * @throws ServletException
     */
    public static Properties loadPropertiesFromClassPath(String propFile, ServletContext servletContext)
            throws ServletException {
        Properties props = null;
        InputStream in = null;
        try {

            URL torqueURL = servletContext.getResource("/WEB-INF/" + propFile);
            in = torqueURL.openStream();
            props = new Properties();
            props.load(in);
            in.close();

        } catch (Exception e) {
            LOGGER.error("Could not read " + propFile + ". Exiting. " + e.getMessage());
            System.err.println("Could not read " + propFile + ". Exiting. " + e.getMessage());
            throw new ServletException(e);
        }
        return props;
    }

    /**
     * Gets the PropertiesConfiguration for a property file from TRACKPLUS_HOME
     * @param propFile
     * Load a properties file from the class path
     * @param propFile
     * @param servletContext
     * @return
     * @throws ServletException
     */
    public static List<String> loadFileFromClassPath(String propFile, ServletContext servletContext)
            throws ServletException {
        ArrayList<String> lines = new ArrayList<String>(100);
        InputStream in = null;
        try {

            URL fileURL = servletContext.getResource("/WEB-INF/" + propFile);
            in = fileURL.openStream();

            BufferedReader reader = new BufferedReader(new InputStreamReader(in));

            String line;
            while ((line = reader.readLine()) != null) {
                lines.add(line);
            }
            reader.close();

            in.close();

        } catch (Exception e) {
            LOGGER.error("Could not read " + propFile + ". Exiting. " + e.getMessage());
            System.err.println("Could not read " + propFile + ". Exiting. " + e.getMessage());
            throw new ServletException(e);
        }
        return lines;
    }

    /**
     * Load a properties file from TRACKPLUS_HOME
     * @param propFile
     * @param servletContext
     * @return
     * @throws ServletException
     */
    public static Properties loadPropertiesFromHome(String propFile) {
        Properties props = null;
        File fprops = null;
        InputStream in = null;
        String trackplusHome = HandleHome.getTrackplus_Home();
        try {
            // First check if we have a configuration file pointed to by the environment
            if (trackplusHome != null && !"".equals(trackplusHome)) {
                String fileName = trackplusHome + File.separator + propFile;
                fprops = new File(fileName);
                LOGGER.debug("Read file " + fileName);
                if (fprops.exists() && fprops.canRead()) {
                    LOGGER.info("Retrieving configuration from " + fileName);
                    in = new FileInputStream(fprops);
                    props = new Properties();
                    props.load(in);
                    in.close();
                }
            }
        } catch (Exception e) {
            LOGGER.error("Could not read " + propFile + ". Exiting. " + e.getMessage());
            System.err.println("Could not read " + propFile + ". Exiting. " + e.getMessage());
        }
        return props;
    }

    /**
     * Obtain the Torque.properties from TRACKPLUS_HOME or if not available from the WAR.
     * @return
     */
    public static PropertiesConfiguration getTrackplusHomePropFile(String propFile) {
        PropertiesConfiguration pc = null;
        File props = null;
        InputStream in = null;
        String trackplusHome = HandleHome.getTrackplus_Home();
        try {
            // First check if we have a configuration file pointed to by the environment
            if (trackplusHome != null && !"".equals(trackplusHome)) {
                String fileName = trackplusHome + File.separator + propFile;
                props = new File(fileName);
                LOGGER.debug("Read file " + fileName);
                if (props.exists() && props.canRead()) {
                    LOGGER.info("Retrieving configuration from " + fileName);
                    in = new FileInputStream(props);
                    pc = new PropertiesConfiguration();
                    pc.load(in);
                    in.close();
                }
            }
        } catch (Exception e) {
            LOGGER.error("Could not read " + propFile + " from TRACKPLUS_HOME " + trackplusHome + ". Exiting. "
                    + e.getMessage());
        }
        return pc;
    }

    /**
     * Gets the PropertiesConfiguration for a property file from servlet context
     * @param propFile
     * @param servletContext
     * @return
     * @throws ServletException
     */
    public static PropertiesConfiguration loadServletContextPropFile(String propFile, ServletContext servletContext)
            throws ServletException {
        PropertiesConfiguration pc = null;
        InputStream in = null;
        URL propFileURL = null;
        try {
            if (pc == null && servletContext != null) {
                propFileURL = servletContext.getResource("/WEB-INF/" + propFile);
                in = propFileURL.openStream();
                pc = new PropertiesConfiguration();
                pc.load(in);
                in.close();
            }
        } catch (Exception e) {
            LOGGER.error("Could not read " + propFile + " from servlet context " + propFileURL == null ? ""
                    : propFileURL.toExternalForm() + ". Exiting. " + e.getMessage());
            throw new ServletException(e);
        }
        return pc;
    }

    /**
     * Obtain the Torque.properties from TRACKPLUS_HOME or if not available from the WAR.
     * @param servletContext
     * @param propFile
     * @param includePluginDatasources whether the plugin datasources should be included
     * @return
     * @throws ServletException
     */
    private static PropertiesConfiguration getTorqueProperties(ServletContext servletContext, String propFile,
            boolean includePluginDatasources) throws ServletException {
        PropertiesConfiguration dbcfg = loadProperties(propFile, servletContext);
        if (includePluginDatasources) {
            dbcfg = getMergedCrmConfiguration(dbcfg);
        }
        // Here we need to call plugins that need their own database configuration.
        return dbcfg;
    }

    /**
     * Merge an existing Torque properties configuration with the one for the CRM custom
     * field database.
     * @param dbcfg the existing, original Genji Torque configuration
     * @return
     */
    private static boolean firstTimeMerge = true;

    public static PropertiesConfiguration getMergedCrmConfiguration(PropertiesConfiguration dbcfg) {
        String propFile = "CrmTorque.properties";
        ClassLoader cl = HandleHome.class.getClassLoader();
        InputStream in = null;
        try {
            URL torqueURL = cl.getResource(propFile);
            in = torqueURL.openStream();
            if (dbcfg == null) {
                dbcfg = new PropertiesConfiguration();
            }
            dbcfg.load(in);
            in.close();
            if (firstTimeMerge) {
                LOGGER.info("Obtained another database configuration from " + propFile + ".");
                firstTimeMerge = false;
            }
        } catch (Exception e) {
            String emsg = e.getMessage();
            if (emsg == null) {
                emsg = "";
            }
        }
        return dbcfg;
    }

    /**
     * Obtain the Torque.properties from from TRACKPLUS_HOME or if not available from the WAR.
     * @return
     * @throws ServletException
     */
    public static InputStream getStream(ServletContext servletContext, String fileName) throws ServletException {
        File file = null;
        InputStream in = null;
        try {
            // First check if we have a configuration file pointed to by the environment
            if (HandleHome.getTrackplus_Home() != null && !"".equals(HandleHome.getTrackplus_Home())) {
                String fname = HandleHome.getTrackplus_Home();
                file = new File(fname + File.separator + fileName);
                LOGGER.debug("Probing for file " + fname + File.separator + fileName);
                if (file.exists() && file.canRead()) {
                    LOGGER.info("Retrieving configuration from " + fname + File.separator + fileName);
                    in = new FileInputStream(file); // must be closed by caller
                }
            }

            if (in == null) { // if we could not get it from the environment
                URL fileURL = servletContext.getResource("/WEB-INF/" + fileName);
                in = fileURL.openStream();
            }
        } catch (Exception e) {
            LOGGER.error("Could not read " + fileName + ". Exiting. " + e.getMessage());
            System.err.println("Could not read " + fileName + ". Exiting. " + e.getMessage());
            throw new ServletException(e);
        }
        return in;
    }

    /**
     * Copies properties file from the WAR to TRACKPLUS_HOME if it does not exist there yet.
     * @return
     * @throws ServletException
     */
    public static void copyPropertiesFile(ServletContext servletContext, String propFile) throws ServletException {
        copyPropertiesFile(servletContext, "/WEB-INF", propFile);

    }

    /**
     * Copies properties file from the WAR to TRACKPLUS_HOME if it does not exist there yet.
     * @return
     * @throws ServletException
     */
    public static void copyPropertiesFile(ServletContext servletContext, String rootDir, String propFile)
            throws ServletException {
        Boolean markForCopy = false;
        File props = null;
        File propsDir = null;
        try {
            // First check if we have a configuration file pointed to by the environment
            URL propFileURL = null;
            if (HandleHome.getTrackplus_Home() != null && !"".equals(HandleHome.getTrackplus_Home())) {
                props = new File(HandleHome.getTrackplus_Home() + File.separator + propFile);
                propsDir = new File(HandleHome.getTrackplus_Home());
                if (props.exists() && props.canRead()) {
                    propFileURL = new URL("file://" + HandleHome.getTrackplus_Home() + File.separator + propFile);
                } else {
                    markForCopy = true; // We have a TRACKPLUS_HOME but no property file. We will copy it there.
                    propFileURL = null;
                }
            }
            if (propFileURL == null) {
                propFileURL = servletContext.getResource(rootDir + "/" + propFile);
            }

            if (markForCopy) { // we copy the property file here when it does not exist yet
                if (propsDir != null && !propsDir.canWrite()) {
                    LOGGER.error(propFile + " not writable to " + propsDir.getAbsolutePath() + " by user "
                            + System.getProperty("user.name"));
                    return;
                }
                if (props != null) {
                    LOGGER.info("Copying configuration file " + propFile + " to " + props.getAbsolutePath());
                    InputStream from = null;
                    FileOutputStream to = null; // Stream to write to destination
                    try {
                        from = propFileURL.openStream(); // Create input stream
                        to = new FileOutputStream(props); // Create output stream
                        byte[] buffer = new byte[4096]; // To hold file contents
                        int bytes_read; // How many bytes in buffer
                        while ((bytes_read = from.read(buffer)) != -1)
                            // Read until EOF
                            to.write(buffer, 0, bytes_read); // write
                    }
                    // Always close the streams, even if exceptions were thrown
                    finally {
                        if (from != null)
                            try {
                                from.close();
                            } catch (IOException e) {
                            }
                        if (to != null)
                            try {
                                to.close();
                            } catch (IOException e) {
                            }
                    }
                }
            }
        } catch (Exception e) {
            LOGGER.error("Could not read " + propFile + ". Exiting." + e.getMessage());
            throw new ServletException(e);
        }
    }

    /**
     * This method copies resources/SSO folder and content from war into Trackplus_Home.
     * @param context
     * @throws IOException
     * @throws ServletException
     */
    public static void copySSOFolder(ServletContext context) {
        File ssoDirInHome = new File(HandleHome.getTrackplus_Home() + File.separator + SSO_DIR);
        if (!ssoDirInHome.exists()) {
            ssoDirInHome.mkdir();
            LOGGER.info("Creating folder for SSO in Track home: " + ssoDirInHome.getPath());
            URL rootTillFolder = null;
            String rootPathTillFolder = null;
            try {
                rootTillFolder = context.getResource("/WEB-INF/classes/resources/SSO");
            } catch (IOException ioEx) {
                LOGGER.error("Some errors happened while copying SSO folder from war into Track_Home!");
                LOGGER.error(ExceptionUtils.getStackTrace(ioEx));
            }
            rootPathTillFolder = rootTillFolder.getPath();
            rootPathTillFolder = rootPathTillFolder.substring(rootPathTillFolder.indexOf("/WEB-INF"),
                    rootPathTillFolder.length());
            Set<String> folderContent = context.getResourcePaths(rootPathTillFolder);
            String folderNameFound = null;
            for (String fileNameWithPath : folderContent) {
                if (fileNameWithPath.endsWith(".jar")) {
                    LOGGER.info("Copying following jar file into Track_home/" + SSO_DIR + ": " + fileNameWithPath);
                    String fileName = fileNameWithPath.replace("/WEB-INF/classes/resources/SSO/", "");
                    try {
                        copyObject(context, "resources/SSO", fileName, SSO_DIR);
                    } catch (ServletException servEx) {
                        LOGGER.error("Some errors happened while copying SSO folder from war into Track_Home!");
                        LOGGER.error(ExceptionUtils.getStackTrace(servEx));
                    }
                } else {
                    folderNameFound = fileNameWithPath.replace("/WEB-INF/classes/resources/SSO/", "");
                }
            }

            try {
                rootTillFolder = context.getResource("/WEB-INF/classes/resources/SSO/" + folderNameFound);
            } catch (IOException ioEx) {
                LOGGER.error("Some errors happened while copying SSO folder from war into Track_Home!");
                LOGGER.error(ExceptionUtils.getStackTrace(ioEx));
            }
            rootPathTillFolder = rootTillFolder.getPath();
            rootPathTillFolder = rootPathTillFolder.substring(rootPathTillFolder.indexOf("/WEB-INF"),
                    rootPathTillFolder.length());
            folderContent = context.getResourcePaths(rootPathTillFolder);
            File ssoSubDirInHome = new File(
                    HandleHome.getTrackplus_Home() + File.separator + SSO_DIR + File.separator + folderNameFound);
            if (!ssoSubDirInHome.exists()) {
                LOGGER.info("Creating sub folder in Track_home/SSO: " + ssoSubDirInHome.getPath());
                ssoSubDirInHome.mkdir();
            }
            for (String fileNameWithPath : folderContent) {
                String fileName = fileNameWithPath.replace("/WEB-INF/classes/resources/SSO/" + folderNameFound, "");
                LOGGER.info(
                        "Copying following file into  Track_home/SSO/" + folderNameFound + ": " + fileNameWithPath);
                try {
                    copyObject(context, "resources/SSO" + "/" + folderNameFound, fileName,
                            SSO_DIR + "/" + folderNameFound);
                } catch (ServletException servEx) {
                    LOGGER.error("Some errors happened while copying SSO folder from war into Track_Home!");
                    LOGGER.error(ExceptionUtils.getStackTrace(servEx));
                }
            }
        }
    }

    /**
     * Copies FAQ template structure from the WAR to TRACKPLUS_HOME if it does not exist there yet.
     * @return
     * @throws ServletException
     */
    public static void copyFAQTemplates(ServletContext servletContext) throws ServletException {
        File faqTemplateDir = null;
        File faqDir = null;
        ZipFile templateZipFile = null;
        try {
            // First check if we have a configuration file pointed to by the environment
            URL templateURL = null;
            if (HandleHome.getTrackplus_Home() != null && !"".equals(HandleHome.getTrackplus_Home())) {
                faqTemplateDir = new File(
                        HandleHome.getTrackplus_Home() + File.separator + "Faqs" + File.separator + "whc_template");
                faqDir = new File(HandleHome.getTrackplus_Home() + File.separator + "Faqs");

                if (!faqDir.exists() || !faqDir.isDirectory()) {
                    if (faqDir != null && !faqDir.exists()) {
                        faqDir.mkdirs();
                    }
                    if (faqDir != null && !faqDir.canWrite()) {
                        LOGGER.error(faqDir + " not writable to " + faqDir.getAbsolutePath() + " by user "
                                + System.getProperty("user.name"));
                        return;
                    }
                }

                if (!faqTemplateDir.exists() || !faqTemplateDir.isDirectory()) {
                    templateURL = PluginUtils.class.getResource("/resources/reportTemplates/whc_template.zip");

                    File faqTemplate = new File(HandleHome.getTrackplus_Home() + File.separator + "Faqs"
                            + File.separator + "whc_template.zip");
                    FileUtils.copyURLToFile(templateURL, faqTemplate);

                    templateZipFile = new ZipFile(faqTemplate);
                    Enumeration<? extends ZipEntry> entries = templateZipFile.getEntries();
                    while (entries.hasMoreElements()) {
                        ZipEntry entry = entries.nextElement();
                        File entryDestination = new File(faqDir, entry.getName());
                        entryDestination.getParentFile().mkdirs();
                        if (!entryDestination.isDirectory()) {
                            InputStream in = templateZipFile.getInputStream(entry);
                            OutputStream out = new FileOutputStream(entryDestination);
                            IOUtils.copy(in, out);
                            IOUtils.closeQuietly(in);
                            IOUtils.closeQuietly(out);
                        }
                    }
                }
            }
        } catch (Exception e) {
            LOGGER.error("Could not read " + templateZipFile + ". Ignoring. " + e.getMessage());
        }
    }

    /**
     * Copies images, templates, etc. from the WAR to TRACKPLUS_HOME. Original files are
     * overwritten unless they have been modified. Modification is checked via an MD5 digest.
     * @throws ServletException
     */
    public static void copyObject(ServletContext servletContext, String sourcePath, String file, String targetDir)
            throws ServletException {
        File targetFile = null;
        File targetDirAbsolutePath = null;
        try {
            // First check if we already have a file there
            if (HandleHome.getTrackplus_Home() != null && !"".equals(HandleHome.getTrackplus_Home())) {
                targetFile = new File(
                        HandleHome.getTrackplus_Home() + File.separator + targetDir + File.separator + file);
                targetDirAbsolutePath = new File(HandleHome.getTrackplus_Home() + File.separator + targetDir);
            }

            if (targetDirAbsolutePath != null && !targetDirAbsolutePath.exists()) {
                targetDirAbsolutePath.mkdirs();
            }

            if (targetDirAbsolutePath != null && !targetDirAbsolutePath.canWrite()) {
                LOGGER.error(file + " not writable to " + targetDirAbsolutePath.getAbsolutePath() + " by user "
                        + System.getProperty("user.name"));
                return;
            }

            URL sourceURL = null;
            try {
                sourceURL = servletContext.getResource(sourcePath + "/" + file);
            } catch (Exception e) {
                LOGGER.debug(
                        "Could not get file " + sourcePath + "/" + file + " from context. Now trying classpath.");
            }

            if (sourceURL == null) {
                ClassLoader cl = HandleHome.class.getClassLoader();
                sourceURL = cl.getResource(sourcePath + "/" + file);
            }

            String hashTarget = "";
            String hashSource = computeHash(sourceURL);
            boolean copy = true;

            if (targetFile.exists() && targetFile.canRead()) {
                hashTarget = computeHash(targetFile);
                if (!fileIsTheOriginal(targetFile, hashTarget) || hashSource.equals(hashTarget)) {
                    copy = false;
                }
            }

            writeHash(targetFile, hashSource);

            if (copy) {
                LOGGER.info("Copying file " + sourcePath + "/" + file + " to " + targetFile.getAbsolutePath());
                InputStream from = null;
                FileOutputStream to = null; // Stream to write to destination

                try {
                    from = sourceURL.openStream(); // Create input stream
                    to = new FileOutputStream(targetFile); // Create temporary output stream
                    byte[] buffer = new byte[4096]; // To hold file contents
                    int bytes_read; // How many bytes in buffer
                    while ((bytes_read = from.read(buffer)) != -1)
                        // Read until EOF
                        to.write(buffer, 0, bytes_read); // write
                }
                // Always close the streams, even if exceptions were thrown
                finally {
                    if (from != null)
                        try {
                            from.close();
                        } catch (IOException e) {
                        }
                    if (to != null)
                        try {
                            to.close();
                        } catch (IOException e) {
                        }
                }
            }
        } catch (Exception e) {
            LOGGER.error("Could not read " + file + ". Exiting: " + e.getMessage());
            LOGGER.error(ExceptionUtils.getStackTrace(e));
            throw new ServletException(e);
        }
    }

    public static void initGroovyPlugins(ServletContext servletContext) {
        URL groovyURL = null;
        try {
            groovyURL = servletContext.getResource("/WEB-INF/" + TORQUE_FILE);
            String path = groovyURL.toExternalForm();
            path = path.substring(0, path.lastIndexOf(TORQUE_FILE) - 1) + File.separator + "classes"
                    + File.separator + PLUGINS_DIR + File.separator + "groovy" + File.separator;
            groovyURL = new URL(path);
        } catch (Exception ge) {
            System.err.println("Can't get the Groovy URL"); // What can we do here?
        }

        Constants.setGroovyURL(groovyURL);
    }

    /**
     * We reorganized the directory structure from 5.0.1 to 5.0.2
     */
    private static void moveWordTemplates() {

        File sourceDir = new File(HandleHome.getTrackplus_Home() + File.separator + "wordTemplates");
        File targetDir = new File(HandleHome.getTrackplus_Home() + File.separator + WORD_TEMPLATES_DIR);

        try {
            if (sourceDir.exists() && !targetDir.exists()) {
                FileUtils.moveDirectory(sourceDir, targetDir);
            }
        } catch (IOException e) {
            LOGGER.error(e.getMessage());
            ;
        }
    }

    /**
     * Copies quartz-jobs.xml file from the WAR to TRACKPLUS_HOME if it does not exist there yet.
     * If it exists there check if it contains the password token clean job and if not add it
     * to the file.
     * @return
     * @throws ServletException
     */
    public static void copyAndMergeQuartzJobsFile(ServletContext servletContext, String propFile)
            throws ServletException {
        Boolean markForCopy = false;
        Boolean markForMerge = false;
        File props = null;
        File propsDir = null;
        try {
            // First check if we have a configuration file pointed to by the environment
            if (HandleHome.getTrackplus_Home() != null && !"".equals(HandleHome.getTrackplus_Home())) {
                props = new File(HandleHome.getTrackplus_Home() + File.separator + propFile);
                propsDir = new File(HandleHome.getTrackplus_Home());
                if (props.exists() && props.canRead()) {
                    markForMerge = true;
                } else {
                    markForCopy = true; // We have a TRACKPLUS_HOME but no property file. We will copy it there.
                }
            }

            if (markForCopy) {
                copyPropertiesFile(servletContext, propFile);
                return;
            }

            if (markForMerge) {
                if (propsDir != null && !propsDir.canWrite()) {
                    LOGGER.error(propFile + " not writable to " + propsDir.getAbsolutePath() + " by user "
                            + System.getProperty("user.name"));
                    return;
                }
                String propFilePath = HandleHome.getTrackplus_Home() + File.separator + propFile;

                LOGGER.info("Merging configuration file " + propFile + " to " + props.getAbsolutePath());
                try {
                    String fileData = FileUtils.readFileToString(new File(propFilePath), "UTF-8");

                    if (fileData.indexOf("<name>RegistrationPasswordCleanerJob</name>") <= 0) {

                        String replacement = "      <job>\n"
                                + "            <name>RegistrationPasswordCleanerJob</name>\n"
                                + "            <group>DefaultJobGroup</group>\n"
                                + "            <description>Remove unconfirmed users and password reset requests</description>\n"
                                + "            <job-class>com.aurel.track.dbase.jobs.RegistrationPasswordCleanerJob</job-class>\n"
                                + "            <job-data-map>\n" + "                <entry>\n"
                                + "                    <key>dummyParam1</key>\n"
                                + "                    <value>dummyValue</value>\n" + "                </entry>\n"
                                + "            </job-data-map>\n" + "        </job>\n" + "        <trigger>\n"
                                + "            <cron>\n"
                                + "                <name>RegistrationPasswordCleanerTrigger</name>\n"
                                + "                <group>DefaultTriggerGroup</group>\n"
                                + "                <job-name>RegistrationPasswordCleanerJob</job-name>\n"
                                + "                <job-group>DefaultJobGroup</job-group>\n"
                                + "                <cron-expression>0 30 23 * * ?</cron-expression>\n"
                                + "            </cron>\n" + "        </trigger>\n" + "    </schedule>";

                        fileData = fileData.replace("</schedule>", replacement);

                        FileUtils.write(new File(propFilePath), fileData, "UTF-8", false);

                    }

                } catch (Exception e) {
                    LOGGER.error(e.getMessage());
                }
            }

        } catch (Exception e) {
            LOGGER.error("Could not read " + propFile + ". Exiting." + e.getMessage());
            throw new ServletException(e);
        }
    }

    /**
     * Copies log4j2.xml file from the WAR to TRACKPLUS_HOME if it does not exist there yet.
     * In this process it changes the location of the logger output.
     * @return
     * @throws ServletException
     */
    public static void copyAndMergeLog4j2File(ServletContext servletContext, String propFile)
            throws ServletException {
        Boolean markForCopy = false;
        Boolean markForMerge = false;
        File props = null;
        File propsDir = null;
        try {
            // First check if we have a configuration file pointed to by the environment
            if (HandleHome.getTrackplus_Home() != null && !"".equals(HandleHome.getTrackplus_Home())) {
                props = new File(HandleHome.getTrackplus_Home() + File.separator + propFile);
                propsDir = new File(HandleHome.getTrackplus_Home());
                if (props.exists() && props.canRead()) {
                    return;
                } else {
                    markForCopy = true; // We have a TRACKPLUS_HOME but no property file. We will copy it there.
                }
            }

            if (markForCopy) {
                copyObject(servletContext, "", propFile, "");

                if (propsDir != null && !propsDir.canWrite()) {
                    LOGGER.error(propFile + " not writable to " + propsDir.getAbsolutePath() + " by user "
                            + System.getProperty("user.name"));
                    return;
                }
                String propFilePath = HandleHome.getTrackplus_Home() + File.separator + propFile;

                LOGGER.info("Merging configuration file " + propFile + " to " + props.getAbsolutePath());
                try {
                    String fileData = FileUtils.readFileToString(new File(propFilePath), "UTF-8");
                    fileData = fileData.replace("${sys:java.io.tmpdir}", getTrackplus_Home());
                    FileUtils.write(new File(propFilePath), fileData, "UTF-8", false);
                } catch (Exception e) {
                    LOGGER.error(e.getMessage());
                }

            }
        } catch (Exception e) {
            LOGGER.error("Could not read " + propFile + ". Exiting." + e.getMessage());
            throw new ServletException(e);
        }

    }

    /*
     * Compute an  MD5 digest for a file.
     *
     */
    public static String computeHash(File file) {
        try {
            MessageDigest md = MessageDigest.getInstance("MD5");

            InputStream is = new FileInputStream(file);
            byte[] buffer = new byte[4096]; // To hold file contents

            DigestInputStream dis = new DigestInputStream(is, md);
            while (dis.read(buffer) != -1) {

            }

            byte[] digest = md.digest();

            dis.close();

            String hash = DatatypeConverter.printHexBinary(digest);

            return hash;

        } catch (Exception e) {

        }
        return null;
    }

    /*
     * Compute an  MD5 digest for a file.
     *
     */
    public static String computeHash(URL url) {

        InputStream from = null;
        String hash = "";

        try {
            MessageDigest md = MessageDigest.getInstance("MD5");
            from = url.openStream(); // Create input stream
            byte[] buffer = new byte[4096]; // To hold file contents
            DigestInputStream dis = new DigestInputStream(from, md);
            while (dis.read(buffer) != -1) {

            }

            byte[] digest = md.digest();

            dis.close();
            from.close();

            hash = DatatypeConverter.printHexBinary(digest);

        } catch (Exception e) {
            LOGGER.error(e.getMessage());
        }
        // Always close the stream, even if exceptions were thrown
        finally {
            if (from != null)
                try {
                    from.close();
                } catch (IOException e) {
                }
        }
        return hash;
    }

    /**
     * Checks if the file with this name has been modified in the meantime. For this we compute the files
     * hash value and look if it exists in the hash.txt file where we keep them.
     * @param existingFile
     * @param targetDirAbsolutePath
     * @return
     */
    public static boolean fileIsTheOriginal(File existingFile, String hash) {
        BufferedReader br = null;
        try {
            File hashFile = new File(existingFile.getParent() + File.separator + "hash.txt");
            if (hashFile.exists()) {
                br = new BufferedReader(new FileReader(hashFile));
                String line;
                String fname = "";
                while ((line = br.readLine()) != null) {
                    String[] keyv = line.split("=");
                    if (keyv != null && keyv.length > 1 && keyv[1] != null) {
                        fname = keyv[0];
                        if (fname.equals(existingFile.getName()) && hash.equals(keyv[1])) {
                            br.close();
                            return true;
                        }
                    }
                }
                br.close();
            }
        } catch (Exception e) {
            LOGGER.warn(e.getMessage());
        }
        return false;
    }

    /*
     * Update the hash.txt file
     */
    public static void writeHash(File existingFile, String hash) {
        try {
            File hashFile = new File(existingFile.getParent() + File.separator + "hash.txt");
            Map<String, Object> hashMap = new HashMap<String, Object>();
            if (hashFile.exists()) {
                String fname = "";
                List<String> hashes = FileUtils.readLines(hashFile);
                for (String fhash : hashes) {
                    String[] keyv = fhash.split("=");
                    if (keyv != null && keyv.length > 1 && keyv[1] != null) {
                        fname = keyv[0];
                        hashMap.put(fname, keyv[1]);
                    }
                }
            }
            hashMap.put(existingFile.getName(), hash);
            PrintWriter output;
            output = new PrintWriter(new FileWriter(hashFile, false));

            for (String key : hashMap.keySet()) {
                output.println(key + "=" + hashMap.get(key));
            }
            output.close();
        } catch (Exception e) {
            LOGGER.warn(e.getMessage());
        }
    }

    public static File getMissingLaTeXPdf() {
        return new File(
                getTrackplus_Home() + File.separator + LATEX_TEMPLATES_DIR + File.separator + "missing.pdf");
    }
}