com.mirth.connect.server.launcher.MirthLauncher.java Source code

Java tutorial

Introduction

Here is the source code for com.mirth.connect.server.launcher.MirthLauncher.java

Source

/*
 * Copyright (c) Mirth Corporation. All rights reserved.
 * 
 * http://www.mirthcorp.com
 * 
 * The software in this package is published under the terms of the MPL license a copy of which has
 * been included with this distribution in the LICENSE.txt file.
 */

package com.mirth.connect.server.launcher;

import java.io.File;
import java.io.FileFilter;
import java.io.FileInputStream;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Properties;
import java.util.jar.JarFile;

import javax.xml.parsers.DocumentBuilderFactory;

import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOCase;
import org.apache.commons.io.filefilter.FileFilterUtils;
import org.apache.commons.io.filefilter.IOFileFilter;
import org.apache.commons.io.filefilter.NameFileFilter;
import org.apache.log4j.Logger;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;

public class MirthLauncher {
    private static final String EXTENSIONS_DIR = "./extensions";
    private static final String MIRTH_PROPERTIES_FILE = "./conf/mirth.properties";
    private static final String EXTENSION_PROPERTIES = "extension.properties";
    private static final String PROPERTY_APP_DATA_DIR = "dir.appdata";
    private static final String PROPERTY_INCLUDE_CUSTOM_LIB = "server.includecustomlib";

    private static String appDataDir = null;

    private static Logger logger = Logger.getLogger(MirthLauncher.class);

    public static void main(String[] args) {
        try {
            try {
                uninstallPendingExtensions();
                installPendingExtensions();
            } catch (Exception e) {
                logger.error("Error uninstalling or installing pending extensions.", e);
            }

            Properties mirthProperties = new Properties();
            String includeCustomLib = null;

            try {
                mirthProperties.load(new FileInputStream(new File(MIRTH_PROPERTIES_FILE)));
                includeCustomLib = mirthProperties.getProperty(PROPERTY_INCLUDE_CUSTOM_LIB);
                createAppdataDir(mirthProperties);
            } catch (Exception e) {
                logger.error("Error creating the appdata directory.", e);
            }

            ManifestFile mirthServerJar = new ManifestFile("server-lib/mirth-server.jar");
            ManifestFile mirthClientCoreJar = new ManifestFile("server-lib/mirth-client-core.jar");
            ManifestDirectory serverLibDir = new ManifestDirectory("server-lib");
            serverLibDir.setExcludes(new String[] { "mirth-client-core.jar" });

            List<ManifestEntry> manifestList = new ArrayList<ManifestEntry>();
            manifestList.add(mirthServerJar);
            manifestList.add(mirthClientCoreJar);
            manifestList.add(serverLibDir);

            // We want to include custom-lib if the property isn't found, or if it equals "true"
            if (includeCustomLib == null || Boolean.valueOf(includeCustomLib)) {
                manifestList.add(new ManifestDirectory("custom-lib"));
            }

            ManifestEntry[] manifest = manifestList.toArray(new ManifestEntry[manifestList.size()]);

            // Get the current server version
            JarFile mirthClientCoreJarFile = new JarFile(mirthClientCoreJar.getName());
            Properties versionProperties = new Properties();
            versionProperties.load(mirthClientCoreJarFile
                    .getInputStream(mirthClientCoreJarFile.getJarEntry("version.properties")));
            String currentVersion = versionProperties.getProperty("mirth.version");

            List<URL> classpathUrls = new ArrayList<URL>();
            addManifestToClasspath(manifest, classpathUrls);
            addExtensionsToClasspath(classpathUrls, currentVersion);
            URLClassLoader classLoader = new URLClassLoader(classpathUrls.toArray(new URL[classpathUrls.size()]));
            Class<?> mirthClass = classLoader.loadClass("com.mirth.connect.server.Mirth");
            Thread mirthThread = (Thread) mirthClass.newInstance();
            mirthThread.setContextClassLoader(classLoader);
            mirthThread.start();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    // if we have an uninstall file, uninstall the listed extensions
    private static void uninstallPendingExtensions() throws Exception {
        File extensionsDir = new File(EXTENSIONS_DIR);
        File uninstallFile = new File(extensionsDir, "uninstall");

        if (uninstallFile.exists()) {
            List<String> extensionPaths = FileUtils.readLines(uninstallFile);

            for (String extensionPath : extensionPaths) {
                File extensionFile = new File(extensionsDir, extensionPath);

                if (extensionFile.exists() && extensionFile.isDirectory()) {
                    logger.trace("uninstalling extension: " + extensionFile.getName());
                    FileUtils.deleteDirectory(extensionFile);
                }
            }

            // delete the uninstall file when we're done
            FileUtils.deleteQuietly(uninstallFile);
        }
    }

    /*
     * This picks up any folders in the installation temp dir and moves them over to the extensions
     * dir, in effect "installing" them.
     */
    private static void installPendingExtensions() throws Exception {
        File extensionsDir = new File(EXTENSIONS_DIR);
        File extensionsTempDir = new File(extensionsDir, "install_temp");

        if (extensionsTempDir.exists()) {
            File[] extensions = extensionsTempDir.listFiles();

            for (int i = 0; i < extensions.length; i++) {
                if (extensions[i].isDirectory()) {
                    logger.trace("installing extension: " + extensions[i].getName());
                    File target = new File(extensionsDir, extensions[i].getName());

                    // delete it if it's already there
                    if (target.exists()) {
                        FileUtils.deleteQuietly(target);
                    }

                    extensions[i].renameTo(target);
                }
            }

            FileUtils.deleteDirectory(extensionsTempDir);
        }
    }

    private static void addManifestToClasspath(ManifestEntry[] manifestEntries, List<URL> urls) throws Exception {
        for (ManifestEntry manifestEntry : manifestEntries) {
            File manifestEntryFile = new File(manifestEntry.getName());

            if (manifestEntryFile.exists()) {
                if (manifestEntryFile.isDirectory()) {
                    ManifestDirectory manifestDir = (ManifestDirectory) manifestEntry;
                    IOFileFilter fileFilter = null;

                    if (manifestDir.getExcludes().length > 0) {
                        fileFilter = FileFilterUtils.and(FileFilterUtils.fileFileFilter(),
                                FileFilterUtils.notFileFilter(new NameFileFilter(manifestDir.getExcludes())));
                    } else {
                        fileFilter = FileFilterUtils.fileFileFilter();
                    }

                    Collection<File> pathFiles = FileUtils.listFiles(manifestEntryFile, fileFilter,
                            FileFilterUtils.trueFileFilter());

                    for (File pathFile : pathFiles) {
                        logger.trace("adding library to classpath: " + pathFile.getAbsolutePath());
                        urls.add(pathFile.toURI().toURL());
                    }
                } else {
                    logger.trace("adding library to classpath: " + manifestEntryFile.getAbsolutePath());
                    urls.add(manifestEntryFile.toURI().toURL());
                }
            } else {
                logger.warn("manifest path not found: " + manifestEntryFile.getAbsolutePath());
            }
        }
    }

    private static void addExtensionsToClasspath(List<URL> urls, String currentVersion) throws Exception {
        FileFilter extensionFileFilter = new NameFileFilter(
                new String[] { "plugin.xml", "source.xml", "destination.xml" }, IOCase.INSENSITIVE);
        FileFilter directoryFilter = FileFilterUtils.directoryFileFilter();
        File extensionPath = new File(EXTENSIONS_DIR);

        Properties extensionProperties = new Properties();
        File extensionPropertiesFile = new File(appDataDir, EXTENSION_PROPERTIES);

        /*
         * If the file does not exist yet, an empty Properties object will be used, returning the
         * default of true for all extensions.
         */
        if (extensionPropertiesFile.exists()) {
            extensionProperties.load(new FileInputStream(extensionPropertiesFile));
        }

        if (extensionPath.exists() && extensionPath.isDirectory()) {
            File[] directories = extensionPath.listFiles(directoryFilter);

            for (File directory : directories) {
                File[] extensionFiles = directory.listFiles(extensionFileFilter);

                for (File extensionFile : extensionFiles) {
                    try {
                        Document document = DocumentBuilderFactory.newInstance().newDocumentBuilder()
                                .parse(extensionFile);
                        Element rootElement = document.getDocumentElement();

                        boolean enabled = extensionProperties
                                .getProperty(rootElement.getElementsByTagName("name").item(0).getTextContent(),
                                        "true")
                                .equalsIgnoreCase("true");
                        boolean compatible = isExtensionCompatible(
                                rootElement.getElementsByTagName("mirthVersion").item(0).getTextContent(),
                                currentVersion);

                        // Only add libraries from extensions that are not disabled and are compatible with the current version
                        if (enabled && compatible) {
                            NodeList libraries = rootElement.getElementsByTagName("library");

                            for (int i = 0; i < libraries.getLength(); i++) {
                                Element libraryElement = (Element) libraries.item(i);
                                String type = libraryElement.getAttribute("type");

                                if (type.equalsIgnoreCase("server") || type.equalsIgnoreCase("shared")) {
                                    File pathFile = new File(directory, libraryElement.getAttribute("path"));

                                    if (pathFile.exists()) {
                                        logger.trace("adding library to classpath: " + pathFile.getAbsolutePath());
                                        urls.add(pathFile.toURI().toURL());
                                    } else {
                                        logger.error("could not locate library: " + pathFile.getAbsolutePath());
                                    }
                                }
                            }
                        }
                    } catch (Exception e) {
                        logger.error("failed to parse extension metadata: " + extensionFile.getAbsolutePath(), e);
                    }
                }
            }
        } else {
            logger.warn("no extensions found");
        }
    }

    private static boolean isExtensionCompatible(String extensionVersion, String currentVersion) {
        if (extensionVersion != null) {
            String[] extensionMirthVersions = extensionVersion.split(",");

            // If there is no build version, just use the patch version
            if (currentVersion.split("\\.").length == 4) {
                currentVersion = currentVersion.substring(0, currentVersion.lastIndexOf('.'));
            }

            for (int i = 0; i < extensionMirthVersions.length; i++) {
                if (extensionMirthVersions[i].trim().equals(currentVersion)) {
                    return true;
                }
            }
        }

        return false;
    }

    private static void createAppdataDir(Properties mirthProperties) {
        File appDataDirFile = null;

        if (mirthProperties.getProperty(PROPERTY_APP_DATA_DIR) != null) {
            appDataDirFile = new File(mirthProperties.getProperty(PROPERTY_APP_DATA_DIR));

            if (!appDataDirFile.exists()) {
                if (appDataDirFile.mkdir()) {
                    logger.debug("created app data dir: " + appDataDirFile.getAbsolutePath());
                } else {
                    logger.error("error creating app data dir: " + appDataDirFile.getAbsolutePath());
                }
            }
        } else {
            appDataDirFile = new File(".");
        }

        appDataDir = appDataDirFile.getAbsolutePath();
        logger.debug("set app data dir: " + appDataDir);
    }
}